简体   繁体   中英

Passing pointer of an array to a function in C

So I'd like to ask the following question about some code my friend is asking me about

For the most part if I ever find myself working with Arrays I just allow myself to send the array's name as it decays to a pointer for example:

#include <stdio.h>

void foo(int *arr_p)
{
    arr_p[0] = 111;
    arr_p[1] = 222;
    arr_p[2] = 333;
}

int main()
{
    int arr[] = {1,2,3};
    foo(arr);
    printf("%d %d %d",  arr[0], arr[1], arr[2]);
}

and as expected this will print 111, 222, 333

However I have a friend who asked me what would happen if for whatever reason I'd like to pass a pointer to a pointer of this array.

#include <stdio.h>

void foo(int **arr_p)
{
    *arr_p[0] = 111;
    *arr_p[1] = 222;
    *arr_p[2] = 333;
}

int main()
{
    int arr[] = {1,2,3};
    foo(&arr);
    printf("%d %d %d",  arr[0], arr[1], arr[2]);
}

now this is the code he sent me... it has a segmentation fault and to perfectly honest I can't really figure out why. I'd imagine dereferencing the pointer to the array would leave me with a pointer to the array would it not?

While plain array names indeed decay to pointers, pointers to arrays do not decay to pointers to pointers. The array-to-pointer conversion comes from the fact that the address of an array is the same as the address of its first element, after which the other elements follow in memory. A pointer to an array cannot be used as a pointer to a pointer of its elements' type: at the array's address reside the array's elements (in your example, of type int ), not pointers.

To receive a pointer-to-array argument in a function, you have to explicitly declare it as such:

void foo(int (*arr_p)[])
{
    (*arr_p)[0] = 111;
    (*arr_p)[1] = 222;
    (*arr_p)[2] = 333;
}

Note the added parentheses; this is because the indexing operator [] has higher precedence in C than the dereferencing operator * ; so *arr_p[i] would be first indexing an array of pointers, and then dereferencing the pointer obtained (and likewise int *arr_p[] would be declaring an array-of-pointers parameter).

Some more insights on your segfault. Just look at the compiled assebly for the difference between int** and int(*)[] on GodBolt

When compiling the line (*arr_p)[0] = 111; with int** you get:

mov     rax, QWORD PTR [rbp-8]
mov     rax, QWORD PTR [rax]
mov     DWORD PTR [rax], 111

while with int(*)[] you get

mov     rax, QWORD PTR [rbp-8]
mov     DWORD PTR [rax], 111

Do you see (and understand) the difference? With int** it's applying a double derefence, because you're telling the compiler you are passing a pointer to a pointer as argument. In the case int(*)[] the compiler understands that it's a pointer to an array, and correctly dereferences once.

In C++ this code wouldn't even compile because of this issue. Actually, int(*arr)[] is not even allowed for type safety: it should be int(*arr)[3] .

This happens because in other words you declare arr as this:

int arr[3];
arr[0]=1;arr[1]=2;arr[2]=3;
foo(&arr);

Then when you pass the address to foo it is actually a pointer to an array of 3.

void foo(int (*arr_p)[3])
{
    *arr_p[0] = 111;
    *arr_p[1] = 222;
    *arr_p[2] = 333;
}

Then arr_p[0] is the first array of 3, arr_p[1] is the second array of 3, and arr_p[2] the third.

So when you write *arr_p[1] = 222 you are writing outside of the allocated memory you pass into foo and you have undefined behaviour.

They are not individual indices in the one array

This would work but it would make no sense:

void foo(int (*arr_p)[3])
{
    *arr_p[0] = 111;
    *arr_p[1] = 222;
    *arr_p[2] = 333;
}

int main()
{
    int arr[3][3];
    foo(arr);

    printf("%d %d %d",  arr[0][0], arr[1][0], arr[2][0]);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM