Returning an array using C

459,081

Solution 1

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)
}

Solution 2

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 it's "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]);
  ...
}

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.

Solution 3

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;
    }

I invite comments on the strengths and weaknesses of this technique. I have not bothered to do so.

Solution 4

How about 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;
}

Solution 5

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 everytime 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 than 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!

Share:
459,081

Related videos on Youtube

user1506919
Author by

user1506919

Updated on July 13, 2020

Comments

  • user1506919
    user1506919 almost 4 years

    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 (lets 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. Any help explaining this is appreciated.

    Proposed Code Format for Array Returning Function

    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?
    } 
    

    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

    • strangefreeworld
      strangefreeworld almost 12 years
      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.
    • user1506919
      user1506919 almost 12 years
      Yes, I know the size of the incomming array at all times. The size of the input and output array wont change.
    • x4444
      x4444 over 6 years
      The Development of the C Language* - bell-labs.com/usr/dmr/www/chist.html
  • Eric Postpischil
    Eric Postpischil almost 12 years
    There is no new operator in C. That is C++.
  • Ed S.
    Ed S. almost 12 years
    And sizeof(char) is guaranteed to be 1, so in this case you can drop that bit from malloc.
  • user1506919
    user1506919 almost 12 years
    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'?
  • Ed S.
    Ed S. almost 12 years
    You aren't calling the function properly (only one argument when the signature requires two).
  • moooeeeep
    moooeeeep almost 12 years
    Option 3: (a static array)
  • Ed S.
    Ed S. almost 12 years
    @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.
  • chris
    chris almost 12 years
    You're passing in &arr. You want arr to be a char *, and pass it in using arr.
  • Man of One Way
    Man of One Way almost 12 years
    @chris according to his code he is passing an array allocated on the stack
  • chris
    chris almost 12 years
    @ManofOneWay, Oh, my bad. Then arr should be a char[], and still passed in by arr.
  • user1506919
    user1506919 almost 12 years
    I like your first method. So how would I then reference ret in my main?
  • Ed S.
    Ed S. almost 12 years
    @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.
    Ed S. almost 12 years
    @user1506919: I would actually prefer option 2 as it is clear who allocates and deallocates memory, but I'll add an example for you.
  • user1506919
    user1506919 almost 12 years
    @Ed S. In your second example, where does ret come from? Should that be buf?
  • Ed S.
    Ed S. almost 12 years
    @user1506919: Yes, sorry, typo
  • Unheilig
    Unheilig over 10 years
    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.
  • pyrospade
    pyrospade over 10 years
    @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; };.
  • Todd Lehman
    Todd Lehman almost 9 years
    Option 4: Return a struct that contains a fixed-size array.
  • Dr.Queso
    Dr.Queso about 8 years
    Your link to "the development of C" has broken... looks like it should direct us here: bell-labs.com/usr/dmr/www/chist.html
  • John Bode
    John Bode about 8 years
    @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.
  • Nick Matteo
    Nick Matteo about 8 years
    @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.
  • CrazySynthax
    CrazySynthax over 7 years
    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?
  • sqr163
    sqr163 about 7 years
    Option 5: Return a union that contains a fixed-size array.
  • Frungi
    Frungi almost 7 years
    @CrazySynthax: Put simply, because arrays are weird.
  • Ed S.
    Ed S. over 6 years
    @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.
  • cmaster - reinstate monica
    cmaster - reinstate monica over 6 years
    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.
  • urkon
    urkon over 6 years
    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 :)
  • Jack G
    Jack G over 6 years
    Object oriented-esk prepossessing mix-mash. I like it.
  • Jarkid
    Jarkid over 6 years
    What is I do this? char returned[10]; char* ptr = returned; return ptr ?
  • Ed S.
    Ed S. over 6 years
    @Jarkid: same problem. you're returning a pointer to an array which is no longer valid once the function exits.
  • Gherman
    Gherman about 6 years
    Leaving out static arrays is not justified. Some varieties of C do not allow pointers. Think GLSL.
  • Ed S.
    Ed S. about 6 years
    @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.
  • Seyfi
    Seyfi over 5 years
    @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.
  • Antti Haapala -- Слава Україні
    Antti Haapala -- Слава Україні almost 5 years
    I'd really add the * sizeof *p here... as this is used generically
  • Admin
    Admin about 4 years
    unclear why this is not the accepted answer. The question was not if it is possible to return a pointer to an array.
  • Minh Tran
    Minh Tran almost 4 years
    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
    Minh Tran almost 4 years
    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?
  • user3139868
    user3139868 about 3 years
    Beautiful. I'm impressed
  • KokoEfraim
    KokoEfraim about 3 years
    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.
  • chux - Reinstate Monica
    chux - Reinstate Monica almost 3 years
    Suggest ret = malloc(count); --> . ret = malloc(sizeof *ret * count); to handle cases where the destination type is not a char *.
  • ekipan
    ekipan almost 3 years
    @MinhTran Refer 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).
  • user426
    user426 over 2 years
    @Gherman "Some varieties of C do not allow pointers" What are you doing in C without pointers ?
  • user426
    user426 over 2 years
    How good is it to hand out pointers to the internal memory of a function ? Forget, multithreading, this is bad in serial code.
  • Gherman
    Gherman over 2 years
    @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.
  • Aaron
    Aaron about 2 years
    You should check that malloc doesn't return a null pointer in case there's a problem allocating.