Returning an array using C
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!
Related videos on Youtube
user1506919
Updated on July 13, 2020Comments
-
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 almost 12 yearsIs 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 almost 12 yearsYes, I know the size of the incomming array at all times. The size of the input and output array wont change.
-
x4444 over 6 yearsThe Development of the C Language* - bell-labs.com/usr/dmr/www/chist.html
-
-
Eric Postpischil almost 12 yearsThere is no
new
operator in C. That is C++. -
Ed S. almost 12 yearsAnd
sizeof(char)
is guaranteed to be1
, so in this case you can drop that bit frommalloc
. -
user1506919 almost 12 yearsok 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. almost 12 yearsYou aren't calling the function properly (only one argument when the signature requires two).
-
moooeeeep almost 12 yearsOption 3: (a static array)
-
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 almost 12 yearsYou're passing in
&arr
. You wantarr
to be achar *
, and pass it in usingarr
. -
Man of One Way almost 12 years@chris according to his code he is passing an array allocated on the stack
-
chris almost 12 years@ManofOneWay, Oh, my bad. Then
arr
should be achar[]
, and still passed in byarr
. -
user1506919 almost 12 yearsI like your first method. So how would I then reference
ret
in my main? -
Ed S. almost 12 years@ManofOneWay:
&arr
is of the typepointer to array of char, i.e., char (*arr)[N]
, notpointer to char
. The array will decay into a pointer, you don't take the address. -
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 almost 12 years@Ed S. In your second example, where does
ret
come from? Should that bebuf
? -
Ed S. almost 12 years@user1506919: Yes, sorry, typo
-
Unheilig over 10 yearsThis 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 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 theint
version of this tostruct intArray { int* contents; int size; };
. -
Todd Lehman almost 9 yearsOption 4: Return a struct that contains a fixed-size array.
-
Dr.Queso about 8 yearsYour 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 about 8 years@Kundor: What
bar
recieves is a pointer, not an array. In the context of a function parameter declaration,T a[N]
andT a[]
are both treated asT *a
. -
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 over 7 yearsYou 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 about 7 yearsOption 5: Return a union that contains a fixed-size array.
-
Frungi almost 7 years@CrazySynthax: Put simply, because arrays are weird.
-
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 anint*
which points to anint
local to the function. -
cmaster - reinstate monica over 6 yearsOption 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 over 6 yearsI 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 over 6 yearsObject oriented-esk prepossessing mix-mash. I like it.
-
Jarkid over 6 yearsWhat is I do this? char returned[10]; char* ptr = returned; return ptr ?
-
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 about 6 yearsLeaving out static arrays is not justified. Some varieties of C do not allow pointers. Think GLSL.
-
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 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 insize_t
type notchar
. -
Antti Haapala -- Слава Україні almost 5 yearsI'd really add the
* sizeof *p
here... as this is used generically -
Admin about 4 yearsunclear why this is not the accepted answer. The question was not if it is possible to return a pointer to an array.
-
Minh Tran almost 4 yearsThere'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? Ifmain()
callsbar()
and bar callsfoo()
, and bar creates a local arraybuff
to pass to foo, then when bar returns, the memory forbuff
is freed right? -
Minh Tran almost 4 yearsIs the memory allocated for
CHAR_ARRAY returned
on the heap? It certainly can't the on the stack (in the stack frame ofreturnArray()
right? -
user3139868 about 3 yearsBeautiful. I'm impressed
-
KokoEfraim about 3 yearsYes, 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 almost 3 yearsSuggest
ret = malloc(count);
--> .ret = malloc(sizeof *ret * count);
to handle cases where the destination type is not achar *
. -
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 seemain
reserves 32 bytes on stack for a Mem2 (sub rsp, 32
) and passes its address viardi
toring_slice
. I don't really know my calling conventions but I thinkrdi
is typically the first argument to a function.ring_slice
then stores its results there and returns that same address (mov rax, rdi
). -
user426 over 2 years@Gherman "Some varieties of C do not allow pointers" What are you doing in C without pointers ?
-
user426 over 2 yearsHow good is it to hand out pointers to the internal memory of a function ? Forget, multithreading, this is bad in serial code.
-
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 about 2 yearsYou should check that
malloc
doesn't return a null pointer in case there's a problem allocating.