1

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?

0

4 Answers 4

2

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).

Sign up to request clarification or add additional context in comments.

2 Comments

Okay perfect, but I'm just very confused about the segmentation fault. If in my friend's function the compiler expects a pointer to a pointer but receives a pointer to an array, when dereferencing the pointer I get an array object and yet (*arr_p)[0] in the old function leads to a seg fault.. any idea on why?
Given the declaration int **arr_p, the expression (*arr_p)[0] first dereferences the arr_p pointer to obtain an int * pointer, then computes the address of the first element of the would-be array of ints and tries to dereference that. Since what actually resides at arr_p is an array of ints and not an address of an array of ints, and the bytes of the array contents don't happen to look like a valid memory address, a segmentation fault occurs.
2

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].

Comments

1

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]);
}

1 Comment

Alright this explains it for the most part, I'd just like to ask why is there a segmentation fault in my friend's code. If I dereference the pointer to an array then im left with the array object.. Why am I not allowed to say (*arr_p)[0]?
0

Because in C array is passed as pointer to the caller function, i.e. the values can be modified, which will be visible to the callee i.e. main function.

So, the edited code for your second function - is.

The problem with second function is that - you need to do pointer arithmatic to access the next element i.e. *(arr+1) for 0 indexed second element.

#include <stdio.h>

void foo(int *arr_p)
{
    *(arr_p) = 111; //arr_p is the first element
    *(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]);
}

Comments

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.