209

I am relatively new to C and I need some help with methods dealing with arrays. Coming from Java programming, I am used to being able to say int [] method() in order to return an array. However, I have found out that with C you have to use pointers for arrays when you return them. Being a new programmer, I really do not understand this at all, even with the many forums I have looked through.

Basically, I am trying to write a method that returns a char array in C. I will provide the method (let’s call it returnArray) with an array. It will create a new array from the previous array and return a pointer to it. I just need some help on how to get this started and how to read the pointer once it is sent out of the array.

Proposed Code Format for Array Returning Function

char *returnArray(char array []){
  char returned [10];
  // Methods to pull values from the array, interpret
  // them, and then create a new array
  return &(returned[0]); // Is this correct?
}

Caller of the Function

int main(){
  int i = 0;
  char array [] = {1, 0, 0, 0, 0, 1, 1};
  char arrayCount = 0;
  char* returnedArray = returnArray(&arrayCount); // Is this correct?
  for (i=0; i<10; i++)
    printf(%d, ",", returnedArray[i]); // Is this correctly formatted?
}

I have not tested this yet as my C compiler is not working at the moment, but I would like to figure this out.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
user1506919
  • 2,377
  • 5
  • 20
  • 15
  • Is the return array a known size as indicated in your code sample? The only other gotcha I see besides the stack issues mentioned in answers is that if your return array is an indeterminate size, given the way pointers/arrays work in C, you won't know how big it is. – strangefreeworld Jul 25 '12 at 18:56
  • Yes, I know the size of the incomming array at all times. The size of the input and output array wont change. – user1506919 Jul 25 '12 at 19:01
  • 1
    The Development of the C Language* - https://www.bell-labs.com/usr/dmr/www/chist.html – x4444 Dec 31 '17 at 07:45
  • For the beginner error of returning an (invalid) reference to a function's local stack, the canonical may be *[Returning a reference to a local variable in C++](https://stackoverflow.com/questions/4643713/returning-a-reference-to-a-local-variable-in-c)*. Though it may be hiding in the first 3 years' questions, but search engines are *very* reluctant (for whatever reason) to point to those (it is ***not*** implied it is due to their age). – Peter Mortensen Nov 03 '22 at 20:25

8 Answers8

294

You can't return arrays from functions in C. You also can't (shouldn't) do this:

char *returnArray(char array []){
 char returned [10];
 //methods to pull values from array, interpret them, and then create new array
 return &(returned[0]); //is this correct?
} 

returned is created with automatic storage duration and references to it will become invalid once it leaves its declaring scope, i.e., when the function returns.

You will need to dynamically allocate the memory inside of the function or fill a preallocated buffer provided by the caller.

Option 1:

dynamically allocate the memory inside of the function (caller responsible for deallocating ret)

char *foo(int count) {
    char *ret = malloc(count);
    if(!ret)
        return NULL;

    for(int i = 0; i < count; ++i) 
        ret[i] = i;

    return ret;
}

Call it like so:

int main() {
    char *p = foo(10);
    if(p) {
        // do stuff with p
        free(p);
    }

    return 0;
}

Option 2:

fill a preallocated buffer provided by the caller (caller allocates buf and passes to the function)

void foo(char *buf, int count) {
    for(int i = 0; i < count; ++i)
        buf[i] = i;
}

And call it like so:

int main() {
    char arr[10] = {0};
    foo(arr, 10);
    // No need to deallocate because we allocated 
    // arr with automatic storage duration.
    // If we had dynamically allocated it
    // (i.e. malloc or some variant) then we 
    // would need to call free(arr)
}
sazzad
  • 5,740
  • 6
  • 25
  • 42
Ed S.
  • 122,712
  • 22
  • 185
  • 265
  • 46
    Option 3: (a static array) – moooeeeep Jul 25 '12 at 18:55
  • 5
    @moooeeeep: Yeah, I left that out purposely to keep things simple, but yes, you can return a pointer to static data declared from within the function. – Ed S. Jul 25 '12 at 18:56
  • I like your first method. So how would I then reference `ret` in my main? – user1506919 Jul 25 '12 at 18:59
  • 4
    @user1506919: I would actually prefer option 2 as it is clear who allocates and deallocates memory, but I'll add an example for you. – Ed S. Jul 25 '12 at 19:02
  • @Ed S. In your second example, where does `ret` come from? Should that be `buf`? – user1506919 Jul 25 '12 at 19:22
  • @user1506919: Yes, sorry, typo – Ed S. Jul 25 '12 at 19:47
  • 14
    Option 4: Return a struct that contains a fixed-size array. – Todd Lehman May 21 '15 at 00:10
  • 1
    You said that once the function returns the array becomes invalid. But, this is also what happens to any local variable. So, why still a function can return a primitive(int, for example) and not an array? – CrazySynthax Oct 01 '16 at 15:02
  • 2
    Option 5: Return a union that contains a fixed-size array. – sqr163 May 18 '17 at 21:47
  • @CrazySynthax: Put simply, because arrays are weird. – Frungi Aug 08 '17 at 03:33
  • 2
    @CrazySynthax: Because you're returning a copy of that `int`. You can't return an array, the language doesn't allow it. So you return a pointer to bad memory. A better analogy would be returning an `int*` which points to an `int` local to the function. – Ed S. Aug 22 '17 at 18:13
  • Option 5: Static pointer to a dynamically allocated buffer that's reallocated if need be. Same outside usage as Option 3, but does not place a limit on the length of the string that can be returned. – cmaster - reinstate monica Sep 21 '17 at 13:09
  • What is I do this? char returned[10]; char* ptr = returned; return ptr ? – Jarkid Jan 29 '18 at 05:49
  • @Jarkid: same problem. you're returning a pointer to an array which is no longer valid once the function exits. – Ed S. Feb 15 '18 at 16:43
  • Leaving out static arrays is not justified. Some varieties of C do not allow pointers. Think GLSL. – Gherman Feb 19 '18 at 11:13
  • @Gherman: Yes, of course you can return _a pointer_ to static data. The answer to that is easily derived if you have a conceptual understanding of object lifetime. The issue here stems from the OP trying to return a pointer to a local. *That* is the problem, and that has to do with object lifetime, all answered above. – Ed S. Feb 19 '18 at 20:02
  • I'd really add the `* sizeof *p` here... as this is used generically – Antti Haapala -- Слава Україні Jun 08 '19 at 14:39
  • There's no chance of memory leaking with *Option 2* because the array being passed into `foo()` is from memory of another function's stack frame (which is located in stack memory and has automatic storage duration) -- right? If `main()` calls `bar()` and *bar* calls `foo()`, and *bar* creates a local array `buff` to pass to *foo*, then when *bar* returns, the memory for `buff` is freed right? – Minh Tran Jun 03 '20 at 01:54
  • Suggest `ret = malloc(count);` --> . `ret = malloc(sizeof *ret * count);` to handle cases where the destination type is not a `char *`. – chux - Reinstate Monica Jun 12 '21 at 13:20
  • @Gherman "*Some varieties of C do not allow pointers*" What are you doing in C without *pointers* ? – user426 Nov 14 '21 at 15:39
  • @user426 One example is writing a shader code GLSL. GLSL is almost same thing as C but no pointers. Or writing code for an unusual platform that may have its own limitations in its C implementation. – Gherman Nov 14 '21 at 18:08
  • On the static array concept - you need to be _very careful_ with these, because declaring e.g. `static int arr[10]; ...; return r` makes the function non reentrant. Meaning, if you call the same function a second time and it produces a different array, _any references to the first array now point to the data of the new array and are broken_. This is because `static` creates one location in memory for that variable, which is reused/rewritten any time the function is called. (fwiw, these sort of footguns are the problems that rust fixes) – Trevor Gross Sep 13 '22 at 17:37
  • @cmaster-reinstatemonica How and When we free memory in Option 5. If we are free at the caller level, then why do we need static? – Arun Kumar Oct 20 '22 at 07:35
  • @ArunKumar Never. It's basically statically allocated memory even though it's (re-)allocated via `malloc()`/`realloc()`. Just like any other statically allocated memory, it will be reclaimed by the kernel when the process exists. (Or rather the containing memory page(s), to be more precise.) – cmaster - reinstate monica Oct 20 '22 at 08:48
  • @cmaster-reinstatemonica Not getting it, could you show an example? – Arun Kumar Oct 20 '22 at 13:33
  • @ArunKumar `const char* spaces(int count) { static char* buf = NULL; buf = realloc(buf, count + 1); sprintf(buf, "%*s", count, ""); return buf; }` This will maintain a buffer that is large enough to hold a string of `count` spaces, format such a string into it, and return it. Subsequent calls will reuse the existing buffer, possibly expanding it. The buffer is never freed, and it does not need to be, as there is only ever a single such buffer in existence, which means that it does not classify as a memory leak. This is a bad example because `count` can be large, but I hope you get the idea. – cmaster - reinstate monica Oct 20 '22 at 14:41
  • Thank you for answering!!! But this way we reallocate for each call, keeping memory for the whole application cycle. good solution but expensive. – Arun Kumar Oct 20 '22 at 15:02
  • @ArunKumar You forgot the name, so I only saw your answer by chance. Indeed, I was aiming for brevity, not for a particularly good or efficient example. Of course, there's nothing to stop you from putting a size check in front of the `realloc()` call, or return a pointer into the middle of the string when you already have more than enough spaces buffered. But as I said, it's not the best example possible, I'd more likely use that method for some message formatting service, or some other use case where I know the buffer will typically be small, but runtime dependent. – cmaster - reinstate monica Oct 21 '22 at 22:17
44

C's treatment of arrays is very different from Java's, and you'll have to adjust your thinking accordingly. Arrays in C are not first class objects (that is, an array expression does not retain its "array-ness" in most contexts). In C, an expression of type "N-element array of T" will be implicitly converted ("decay") to an expression of type "pointer to T", except when the array expression is an operand of the sizeof or unary & operators, or if the array expression is a string literal being used to initialize another array in a declaration.

Among other things, this means that you cannot pass an array expression to a function and have it received as an array type; the function actually receives a pointer type:

void foo(char *a, size_t asize)
{
  // do something with a
}

int bar(void)
{
  char str[6] = "Hello";
  foo(str, sizeof str);
}

In the call to foo, the expression str is converted from type char [6] to char *, which is why the first parameter of foo is declared char *a instead of char a[6]. In sizeof str, since the array expression is an operand of the sizeof operator, it's not converted to a pointer type, so you get the number of bytes in the array (6).

If you're really interested, you can read Dennis Ritchie's The Development of the C Language to understand where this treatment comes from.

The upshot is that functions cannot return array types, which is fine since array expressions cannot be the target of an assignment, either.

The safest method is for the caller to define the array, and pass its address and size to the function that's supposed to write to it:

void returnArray(const char *srcArray, size_t srcSize, char *dstArray, char dstSize)
{
  ...
  dstArray[i] = some_value_derived_from(srcArray[i]);
  ...
}

int main(void)
{
  char src[] = "This is a test";
  char dst[sizeof src];
  ...
  returnArray(src, sizeof src, dst, sizeof dst);
  ...
}

Another method is for the function to allocate the array dynamically and return the pointer and size:

char *returnArray(const char *srcArray, size_t srcSize, size_t *dstSize)
{
  char *dstArray = malloc(srcSize);
  if (dstArray)
  {
    *dstSize = srcSize;
    ...
  }
  return dstArray;
}

int main(void)
{
  char src[] = "This is a test";
  char *dst;
  size_t dstSize;

  dst = returnArray(src, sizeof src, &dstSize);
  ...
  free(dst);
  ...
}

In this case, the caller is responsible for deallocating the array with the free library function.

Note that dst in the above code is a simple pointer to char, not a pointer to an array of char. C's pointer and array semantics are such that you can apply the subscript operator [] to either an expression of array type or pointer type; both src[i] and dst[i] will access the i'th element of the array (even though only src has array type).

You can declare a pointer to an N-element array of T and do something similar:

char (*returnArray(const char *srcArr, size_t srcSize))[SOME_SIZE]
{
  char (*dstArr)[SOME_SIZE] = malloc(sizeof *dstArr);
  if (dstArr)
  {
    ...
    (*dstArr)[i] = ...;
    ...
  }
  return dstArr;
}

int main(void)
{
  char src[] = "This is a test";
  char (*dst)[SOME_SIZE];
  ...
  dst = returnArray(src, sizeof src);
  ...
  printf("%c", (*dst)[j]);
  ...
}

There are several drawbacks with the above. First of all, older versions of C expect SOME_SIZE to be a compile-time constant, meaning that function will only ever work with one array size. Secondly, you have to dereference the pointer before applying the subscript, which clutters the code. Pointers to arrays work better when you're dealing with multi-dimensional arrays.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
John Bode
  • 119,563
  • 19
  • 122
  • 198
  • 2
    Your link to "the development of C" has broken... looks like it should direct us here: https://www.bell-labs.com/usr/dmr/www/chist.html – Dr.Queso Mar 25 '16 at 02:09
  • @Kundor: What `bar` recieves is a pointer, not an array. In the context of a function parameter declaration, `T a[N]` and `T a[]` are both treated as `T *a`. – John Bode Apr 14 '16 at 12:16
  • @JohnBode: You're right! For some reason I thought fixed-size arrays were passed on the stack. I recall an occasion, many years ago, when I found that an array's size had to be specified in the parameter signature, but I must have been confused. – Nick Matteo Apr 14 '16 at 13:22
  • @JohnBode , in second code part first line:`void returnArray(const char *srcArray, size_t srcSize, char *dstArray, char dstSize)` last parameter should be in `size_t` type not `char`. – Seyfi Nov 17 '18 at 14:36
21

I am not saying that this is the best solution or a preferred solution to the given problem. However, it may be useful to remember that functions can return structs. Although functions cannot return arrays, arrays can be wrapped in structs and the function can return the struct thereby carrying the array with it. This works for fixed length arrays.

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>

    typedef
    struct 
    {
        char v[10];
    } CHAR_ARRAY;



    CHAR_ARRAY returnArray(CHAR_ARRAY array_in, int size)
    {
        CHAR_ARRAY returned;

        /*
        . . . methods to pull values from array, interpret them, and then create new array
        */

        for (int i = 0;  i < size; i++ )
            returned.v[i] = array_in.v[i] + 1;

        return returned; // Works!
    } 




    int main(int argc, char * argv[])
    {
        CHAR_ARRAY array = {1,0,0,0,0,1,1};

        char arrayCount = 7;

        CHAR_ARRAY returnedArray = returnArray(array, arrayCount); 

        for (int i = 0; i < arrayCount; i++)
            printf("%d, ", returnedArray.v[i]);  //is this correctly formatted?

        getchar();
        return 0;
    }
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Indinfer
  • 532
  • 5
  • 4
  • 1
    unclear why this is not the accepted answer. The question was not if it is possible to return a pointer to an array. –  May 18 '20 at 16:02
  • 1
    Is the memory allocated for `CHAR_ARRAY returned` on the heap? It certainly can't the on the stack (in the stack frame of `returnArray()` right? – Minh Tran Jun 03 '20 at 02:05
  • Yes, this is answer to my question: Can C function return an array? Yes it can, and @Indinfer has answer it with the use of C own struc data type. And of course it should be of fixed length array. This is C, you have to be deterministic upfront unless you have time to play the pain of pointer, address, malloc, free, etc, for just a simple function return. Cheers. – KokoEfraim May 07 '21 at 07:05
  • @MinhTran Refer https://godbolt.org/z/1rYocv3PT - Essentially `ring_slice` is transformed into a function that accepts an address to store into. You can see `main` reserves 32 bytes on stack for a Mem2 (`sub rsp, 32`) and passes its address via `rdi` to `ring_slice`. I don't really know my calling conventions but I think `rdi` is typically the first argument to a function. `ring_slice` then stores its results there and returns that same address (`mov rax, rdi`). – ekipan Aug 01 '21 at 21:03
12

Use this deliciously evil implementation:

array.h

#define IMPORT_ARRAY(TYPE)    \
    \
struct TYPE##Array {    \
    TYPE* contents;    \
    size_t size;    \
};    \
    \
struct TYPE##Array new_##TYPE##Array() {    \
    struct TYPE##Array a;    \
    a.contents = NULL;    \
    a.size = 0;    \
    return a;    \
}    \
    \
void array_add(struct TYPE##Array* o, TYPE value) {    \
    TYPE* a = malloc((o->size + 1) * sizeof(TYPE));    \
    TYPE i;    \
    for(i = 0; i < o->size; ++i) {    \
        a[i] = o->contents[i];    \
    }    \
    ++(o->size);    \
    a[o->size - 1] = value;    \
    free(o->contents);    \
    o->contents = a;    \
}    \
void array_destroy(struct TYPE##Array* o) {    \
    free(o->contents);    \
}    \
TYPE* array_begin(struct TYPE##Array* o) {    \
    return o->contents;    \
}    \
TYPE* array_end(struct TYPE##Array* o) {    \
    return o->contents + o->size;    \
}

main.c

#include <stdlib.h>
#include "array.h"

IMPORT_ARRAY(int);

struct intArray return_an_array() {
    struct intArray a;
    a = new_intArray();
    array_add(&a, 1);
    array_add(&a, 2);
    array_add(&a, 3);
    return a;
}

int main() {
    struct intArray a;
    int* it;
    int* begin;
    int* end;
    a = return_an_array();
    begin = array_begin(&a);
    end = array_end(&a);
    for(it = begin; it != end; ++it) {
        printf("%d ", *it);
    }
    array_destroy(&a);
    getchar();
    return 0;
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
pyrospade
  • 7,870
  • 4
  • 36
  • 52
  • 4
    This is devilishly delicious enough to arose my curiosity. Can you explain a bit more what you did up there or perhaps suggest a reading to this deliciousness you call? Thanks in advance. – Unheilig Jan 08 '14 at 19:49
  • 1
    @Unheilig - Note that there are sime potential bugs in this, it was just a proof of Concept. That said, the trick is returning a `struct` as an array container/object. Think of it like a C++ std::vector. The preprocessor would expand the `int` version of this to `struct intArray { int* contents; int size; };`. – pyrospade Jan 09 '14 at 04:05
  • 1
    I like the approach. pro: this is generic solution; contra: memory intensive solution. Not optimal for vectors of kown sizes. Anyway this can be upgraded with inital size allocation. I would definitley add some allocation check. Very good proposal to start with :) – urkon Jan 04 '18 at 13:47
  • 1
    Object oriented-esk prepossessing mix-mash. I like it. – Jack G Jan 22 '18 at 03:24
  • This reminds me of [stb_ds](https://github.com/nothings/stb/blob/master/stb_ds.h) – bhathiya-perera Sep 17 '22 at 11:23
12

You can do it using heap memory (through malloc() invocation) like other answers reported here, but you must always manage the memory (use free() function every time you call your function).

You can also do it with a static array:

char* returnArrayPointer()
{
    static char array[SIZE];

    // Do something in your array here

    return array;
}

You can then use it without worrying about memory management.

int main()
{
    char* myArray = returnArrayPointer();
    /* Use your array here */
    /* Don't worry to free memory here */
}

In this example you must use static keyword in array definition to set to application-long the array lifetime, so it will not destroyed after return statement.

Of course, in this way you occupy SIZE bytes in your memory for the entire application life, so size it properly!

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
mengo
  • 196
  • 1
  • 5
  • How good is it to hand out pointers to the internal memory of a function ? Forget, multithreading, this is bad in serial code. – user426 Nov 14 '21 at 15:44
  • There have been many suggestions in this page on how to solve this problem but I also find using "static" to return arrays to be the best way to go as long as you are aware that the returned value is a global variable from that point onwards. – sueszli Dec 03 '22 at 12:12
8

In your case, you are creating an array on the stack and once you leave the function scope, the array will be deallocated. Instead, create a dynamically allocated array and return a pointer to it.

char * returnArray(char *arr, int size) {
    char *new_arr = malloc(sizeof(char) * size);
    for(int i = 0; i < size; ++i) {
        new_arr[i] = arr[i];
    }
    return new_arr;
}

int main() {

    char arr[7]= {1,0,0,0,0,1,1};
    char *new_arr = returnArray(arr, 7);

    // don't forget to free the memory after you're done with the array
    free(new_arr);

}
Man of One Way
  • 3,904
  • 1
  • 26
  • 41
  • 3
    There is no `new` operator in C. That is C++. – Eric Postpischil Jul 25 '12 at 18:51
  • 1
    And `sizeof(char)` is guaranteed to be `1`, so in this case you can drop that bit from `malloc`. – Ed S. Jul 25 '12 at 18:54
  • ok so If I wanted to print out the contents of the new array, could I just do my 'printf' statement but replace 'returnedArray' with 'arr'? – user1506919 Jul 25 '12 at 18:54
  • You aren't calling the function properly (only one argument when the signature requires two). – Ed S. Jul 25 '12 at 18:54
  • You're passing in `&arr`. You want `arr` to be a `char *`, and pass it in using `arr`. – chris Jul 25 '12 at 18:56
  • @chris according to his code he is passing an array allocated on the stack – Man of One Way Jul 25 '12 at 18:58
  • @ManofOneWay, Oh, my bad. Then `arr` should be a `char[]`, and still passed in by `arr`. – chris Jul 25 '12 at 18:59
  • @ManofOneWay: `&arr` is of the type `pointer to array of char, i.e., char (*arr)[N]`, not `pointer to char`. The array will decay into a pointer, you don't take the address. – Ed S. Jul 25 '12 at 19:00
  • You should check that `malloc` doesn't return a null pointer in case there's a problem allocating. – Aaron Mar 24 '22 at 19:50
3

Your method will return a local stack variable that will fail badly. To return an array, create one outside the function, pass it by address into the function, then modify it, or create an array on the heap and return that variable. Both will work, but the first doesn't require any dynamic memory allocation to get it working correctly.

void returnArray(int size, char *retArray)
{
  // work directly with retArray or memcpy into it from elsewhere like
  // memcpy(retArray, localArray, size); 
}

#define ARRAY_SIZE 20

int main(void)
{
  char foo[ARRAY_SIZE];
  returnArray(ARRAY_SIZE, foo);
}
Michael Dorgan
  • 12,453
  • 3
  • 31
  • 61
1

You can use code like this:

char *MyFunction(some arguments...)
{
    char *pointer = malloc(size for the new array);
    if (!pointer)
        An error occurred, abort or do something about the error.
    return pointer; // Return address of memory to the caller.
}

When you do this, the memory should later be freed, by passing the address to free.

There are other options. A routine might return a pointer to an array (or portion of an array) that is part of some existing structure. The caller might pass an array, and the routine merely writes into the array, rather than allocating space for a new array.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312