5

Pointers-to-array (not array pointers) are a lesser-known feature of the C programming language.

int arr[] = { 3, 5, 6, 7, 9 };
int (*arr_ptr)[5] = &arr;

printf("[2]=%d", (*arr_ptr)[2]);

They allow you to "un-decay" a dynamically allocated array pointer.
(which is pretty cool in my opinion)

int *ptr = malloc(sizeof(int) * 5);
int(*arr_ptr)[5] = (int(*)[5])ptr;

printf("[2]=%d", (*arr_ptr)[2]);

I'm trying to define a function to return an array pointer, without success.
I tried something like this:

int (*)[5] create_arr_5(void) 
{
    int(*arr_ptr)[5] = malloc(sizeof(int) * 5);
    return arr_ptr;
}
6
  • 1
    Please don't tag c++ if you ask about C. These are two quite distinct programming languages. I removed the tag. Commented Feb 29, 2024 at 21:32
  • Also, please always copy-paste error messages or explain what behavior you see and expect. My guess is that you are asking because your compiler complains about the return type in the function declaration with a syntax error. The correct syntax for the function declaration is int (*create_arr_5(void))[5]. Commented Feb 29, 2024 at 21:37
  • Understood @user17732522. My apology for the trouble. Commented Feb 29, 2024 at 21:38
  • Does this answer your question? Function returning pointer to array 5 of pointer to int - found using a site search. Note that, as explained in the answer there with its example of int(* g())[5], your particular case would be something like int (*create_arr_5(void))[5], as stated in user17732522's comment. Commented Feb 29, 2024 at 21:39
  • 2
    "are a lesser-known feature of the C programming language." - no they are not, simply are more advanced for the beginner courses Commented Feb 29, 2024 at 21:49

5 Answers 5

3

One option is to use a typedef, which can make usage a lot easier:

typedef int int_arr_5_t[5];

int_arr_5_t* create_int_arr_5(void) 
{
    return malloc(sizeof(int_arr_5_t));
}

Example:

int main(void)
{
    int_arr_5_t* arr = create_int_arr_5();
    printf("size=%zu count=%zu\n",
           sizeof(*arr),
           sizeof(*arr) / sizeof((*arr)[0]));
}

Output:

size=20 count=5
Sign up to request clarification or add additional context in comments.

8 Comments

hiding pointers and arrays behind pointers is a very bad practice
@gulpr "hiding pointers and arrays behind pointers is a very bad practice" --> it depends - sometimes it is the rights idea.
@paddy pointer to array and typedefed pointers are completely different things
@chux-ReinstateMonica except for function pointers (I personally prefer function types) it is a very bad idea
I agree with @gulpr here, this is bad practice. You may use a typedef in some situations, but then do it proper: typedef int arr_5_ptr_t [5]; And then: arr_5_ptr_t* create_arr_5(void) ... arr_5_ptr_t* ptr = malloc(sizeof *ptr);. That's readable code, hiding the pointer syntax behind typedef is not.
|
2

The function should be defined like this:

int (*create_arr_5(void))[5]
{
    int(*arr_ptr)[5] = malloc(sizeof(int) * 5);
    return arr_ptr;
}

Talking through the declaration, create_arr_5 is a function:

create_arr_5()

That takes no parameters:

create_arr_5(void)

And return a pointer:

*create_arr_5(void)

To an array of size 5:

(*create_arr_5(void))[5]

Of int:

int (*create_arr_5(void))[5]

2 Comments

This is exactly what I needed. Thanks for your help. as soon as stack overflow allows me to accept this solution, I'll do it.
I would rather suggest malloc(sizeof(*arr_ptr))
2

Cdecl can help you with such things, though only to some extent, because you need to know its dialect of "English". Given this input:

declare f as function (void) returning pointer to array 5 of int

it emits this:

int (*f(void ))[5]

, which is correct. You can read it from the inside out:

  • f - the identifier being declared
  • (void) - is a function accepting no arguments
  • * - its return value is a pointer
  • ( ... )[5] - to an array of five elements
  • int - of type int

1 Comment

You might be right; This one was too hard for me, Maybe a tool would help.
2

As you might have noticed, the C syntax is quite dysfunctional here, as it quickly turns hard to read. A language design decision was made back in the days to never allow arrays to be copied with = or returned from functions. There's no sensible rationale for that, it's just how the language was designed.

Had C allowed arrays to be returned by value, we would perhaps have written the code for returning an array by value like:

int create_arr_5 (void)[5]

But this is invalid C. However, consider that when we have an array int x[5], we get a pointer to that same type by wrapping the identifier x in parenthesis and adding a *. That is: int (*x)[5]. The syntax for returning a pointer to array is similar, wrap the function as well as it's parameters in parenthesis, then add a *:

int (*create_arr_5(void))[5]

However, that's still quite hard to read and it won't get easier for non-trivial functions.


One work-around is to pass the pointer to array as parameter instead... but then we get to deal with array "decay". Because of "decay" (or array adjustment as the C standard calls it formally), then

void create_arr_5 (int arr[5])

is 100% equivalent to:

void create_arr_5 (int* arr)

This is readable, but won't work if we need to malloc inside the function and return that to the caller, because the pointer is just a local copy. We can't do int** either, in case the caller expects an int (*)[5]. Gah!

So what if we let the function take a pointer to array as parameter and then have the caller call it like create_arr_5 (&array)? Sure, that works:

void create_arr_5 (int (*arr)[5])

But we still have the problem that this is a local pointer. We could pass a pointer to an array pointer...

void create_arr_5 (int (**arr)[5])

But that's also quite unreadable! We haven't really improved anything.

Reasonable compromises/pick your poison:

  • One reasonable compromise is to just to ignore this whole mess and have the function return a void*

     void* create_arr_5 (void)
    

    That's readable, but we lose type safety. Depending on how picky we are with type safety, this might either be fine or unacceptable.

  • Another compromise is to typedef the expected array type, which is somewhat bad practice but at least makes the function readable:

    typedef int arr_5 [5];
    
    arr_5* create_arr_5 (void)
    {
      arr_5* ptr = malloc(sizeof *ptr);
      return ptr;
    }
    
  • Replace functions with macros. Generally function-like macros are bad practice, but sometimes they could be justified to hide away flaws of the language. You can make them type safe nowadays even.

    The advantage of a macro is that you can make one which works no matter size and still remains type safe:

      #define create_arr(arr) _Generic( (arr), int(*)[]: malloc(sizeof(*arr)) )
      ...
    
      int(*arr5)[5] = create_arr(arr5);
      int(*arr3)[3] = create_arr(arr3);
    
  • Best solution/call for sanity: you are actually just doing a plain malloc call. Skip all of this obfuscation to begin with!

     int(*arr5)[5] = malloc(sizeof *arr5);
    

Comments

1

In C, returning a pointer to an array from a function involves proper declaration of the return type and memory management.

int (*create_arr_5(void))[5] 
{
    int (*arr_ptr)[5] = malloc(sizeof(int) * 5);
    return arr_ptr;
}

In this function:

  • The return type is declared as int (*)[5], representing a pointer to an array of 5 integers.
  • Inside the function, memory is allocated for an array of 5 integers using malloc.
  • The address of the allocated memory is assigned to arr_ptr.
  • Finally, arr_ptr is returned from the function.

You can then use this function to dynamically allocate an array of integers and obtain a pointer to it. Here's an example of usage:

int (*arr_ptr)[5] = create_arr_5();
if (arr_ptr != NULL) {
    // Access elements of the dynamically allocated array using arr_ptr
    (*arr_ptr)[0] = 1;
    (*arr_ptr)[1] = 2;
    // ...
    
    // Don't forget to free the dynamically allocated memory when done
    free(arr_ptr);
}

1 Comment

Proper declaration: absolutely. But such a function does not necessarily have to perform any memory management. It need not allocate the array to which its return value points. It might instead return a pointer to an array with static storage duration, or to an array accessible from one of it arguments, among other possibilities. But sure, returning a pointer to a dynamically allocated array is one thing it could do.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.