Returning an array using C
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.
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)
}