In C you can declare a variable that points to an array like this:
int int_arr[4] = {1,2,3,4};
int (*ptr_to_arr)[4] = &int_arr;
Although practically it is the same as just declaring a pointer to int:
int *ptr_to_arr2 = int_arr;
But syntactically it is something different.
Now, how would a function look like, that returns such a pointer to an array (of int e.g.) ?
CodePudding user response:
A declaration of int is int foo;.
A declaration of an array of 4 int is int foo[4];.
A declaration of a pointer to an array of 4 int is int (*foo)[4];.
A declaration of a function returning a pointer to an array of 4 int is int (*foo())[4];. The () may be filled in with parameter declarations.
CodePudding user response:
The typedef keyword can make things a lot clearer/simpler in this case:
int int_arr[4] = { 1,2,3,4 };
typedef int(*arrptr)[4]; // Define a pointer to an array of 4 ints ...
arrptr func(void) // ... and use that for the function return type
{
return &int_arr;
}
CodePudding user response:
As already mentioned, the correct syntax is int (*foo(void))[4]; And as you can tell, it is very hard to read.
Questionable solutions:
Use the syntax as C would have you write it. This is in my opinion something you should avoid, since it's incredibly hard to read, to the point where it is completely useless. This should simply be outlawed in your coding standard, just like any sensible coding standard enforces function pointers to be used with a
typedef.Oh so we just
typedefthis just like when using function pointers? One might get tempted to hide all this goo behind atypedefindeed, but that's problematic as well. And this is since both arrays and pointers are fundamental "building blocks" in C, with a specific syntax that the programmer expects to see whenever dealing with them. And the absensce of that syntax suggests an object that can be addressed, "lvalue accessed" and copied like any other variable. Hiding them behindtypedefmight in the end create even more confusion than the original syntax.Take this example:
typedef int(*arr)[4]; ... arr a = create(); // calls malloc etc ... // somewhere later, lets make a hard copy! (or so we thought) arr b = a; ... cleanup(a); ... print(b); // mysterious crash hereSo this "hide behind typedef" system heavily relies on us naming types
somethingptrto indicate that it is a pointer. Or lets say...LPWORD... and there it is, "Hungarian notation", the heavily criticized type system of the Windows API.A slightly more sensible work-around is to return the array through one of the parameters. This isn't exactly pretty either, but at least somewhat easier to read since the strange syntax is centralized to one parameter:
void foo (int(**result)[4]) { ... *result = &arr; }That is: a pointer to a pointer-to-array of
int[4].If one is prepared to throw type safety out the window, then of course
void* foo (void)solves all of these problems... but creates new ones. Very easy to read, but now the problem is type safety and uncertainty regarding what the function actually returns. Not good either.
So what to do then, if these versions are all problematic? There are a few perfectly sensible approaches.
Good solutions:
Leave allocation to the caller. This is by far the best method, if you have the option. Your function would become
void foo (int arr[4]);which is readable and type safe both.Old school C. Just return a pointer to the first item in the array and pass the size along separately. This may or may not be acceptable from case to case.
Wrap it in a struct. For example this could be a sensible implementation of some generic array type:
typedef struct { size_t size; int arr[]; } array_t; array_t* alloc (size_t items) { array_t* result = malloc(sizeof *result sizeof(int[items])); return result; }
