Click here to Skip to main content
15,887,027 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
Hello. Why is address of array (i.e. &) not convertible to ** in C?

What I have tried:

I tried the following..

C
#include <stdio.h>


int main()
{
    int** a;
    int b[5];
    b[1] = 123;
    
    //a = &b; //< results in output of warning "assignment to ‘int **’ from incompatible pointer type ‘int (*)[5]’"
    int* c = b;
    a = &c; //< this works
    printf("*a is %i\n", (*a)[1]);
    
    return 0;
}


I would expect that, as b can be converted to int*, it would be possible to convert &b to int**. However, this results in the warning mentioned in the code block above.

Using mingw gcc on windows this (writing and then reading from a ** that was assigned to the address of an array) actually worked once but not a second time.
Posted

b is an array: the name of an array is a pointer to the first element, so technically, &b is a pointer to a pointer to an integer. But ... your compiler doesn't agree because it (justifiably) considers it to be a pointer to an array of integers. And arrays are a syntactical sugar for the pointer arithmetic going on in the background so they don't quite work as you might think.
You can get round it by making b a pointer to an int instead:
C
#include <stdio.h>
#include <malloc.h>
int main()
    {
    int** a;
    int* b = malloc(sizeof(int) * 5);
    b[1] = 123;
    
    a = &b; 
    printf("*a is %i\n", (*a)[1]);

    free(b);
    return 0;
    }
 
Share this answer
 
Comments
Maarten ten Velden 17-Jan-24 7:57am    
Well avoiding malloc and free is wanted. You said "justifiably". How is this "justifiably"? I can work around it by assigning b to an int* and then assigning the address of that int* to a, but I don't see why that is workaround is required.
OriginalGriff 17-Jan-24 8:22am    
Don't avoid malloc and free - they move your array to the heap instead of being on the stack, which means you can use a much, much bigger array - the total stack size is generally 1Mb (though I've seen 256 byte stacks in some compilers) while the heap is basically the size of your machines ram (or larger, but that means paging to HDD which is slow).

Because an array is syntactic sugar the compiler writer has decided that a pointer to an array is not a pointer to a pointer an integer, even though the C specification states that the name of an array (in this case b) is a pointer to the first element in the array it also states that the name of an array is NOT an lvalue so you can't use it in operations that require one:
    int b[5] = {0, 1, 2, 3, 4};
    b++;
    printf("%u\n", *b);
Will give you an error to that effect, and the address of operator explicitly requires an lvalue to avoid things like this compiling:
int* a = &(2 + 3);
The compiler writer is trying to be friendly and helpful by explicitly stating the problem instead of a generic lvalue error message.
Maarten ten Velden 17-Jan-24 8:53am    
I didn't mean avoiding malloc and free in general, I meant in this particular case for which I am asking the question.

The following is the workaround I meant which compiles..

int** a;
int b[5];
int* c = b;
a = &c;
(*a)[1] = 123;
printf("b[1] == %i\n", (*a)[1]);


The cases you mention seem to be arbitrary rules, as though printf("%u\n", *b) doesn't work, printf("%u\n", b[0]) and int* c = b; printf("%u\n", c); would both achieve the exact same. The last example is entirely different than taking the address of an array, which is a variable, as there you are taking the address of an intermediate result. I still don't see a reason why a C compiler would not be able to directly use the & and * operators on an array.
Such warning means that you should explicitly set the resulted casting type this means that you understand that result of usage such variable in the code can cause crash or memory leaks. As an example the comments code from this discussion https://www.codeproject.com/Answers/5370174/C-language-I-dont-understand-multi-dimensional-arr#answer3[^]:
int mat[3][3] = { {0,1,2},{3,4,5},{6,7,8} };
int **ptr = (int **)&mat;
printf("%d\n", mat[1][1]); /* prints "4" */
printf("%d\n", ptr[1][1]); /* seg faults! */ 

Here you have multi-dimensional array and cast it to the int**. This code will work on x86 platform and will crash on x64 at the place as specified in the comment.

So, the casting warning just shows you that you should takes care of usage resulted variable - that's it.
 
Share this answer
 
Comments
Maarten ten Velden 18-Jan-24 7:38am    
You are talking about a multidimensional array though, I am using a non multidimensional array. The problem in your example is that ptr[1] results in a int* and then ptr[1][1] tries to dereference a pointer whereas mat[1][1] calculates an offset right?

My case with a non multidimensional array also causes an error -1073741819, which I assume is STATUS_ACCESS_VIOLATION (thus the segmentation fault you mentioned). Alignment shouldn't be a problem as I am not using a multidimensional array?

I think I do understand now why this doesn't work. I wrote my own solution.
Maxim Kartavenkov 18-Jan-24 7:48am    
Yes, because from point of multidimensional array is easy to understand why the warning can appear and why is can cause the crash .
Thanks to Maxim Kartavenkov in particular because pointing out the access violation ended up helping me in the right direction.

Using my example..

C
#include <stdio.h>


int main()
{
    int a[5];
    a[1] = 123;
    a[2] = 456;
    
    printf("&a is %p\n", &a); //< type of &a is int(*)[], which apparently is some sort of special type
    printf("a is %p\n", a); //< type is int*
    // ^
    // both will print the same address

    printf("&(&a)[1] is %p\n", &(&a)[1]);
    printf("((char*)a) + sizeof(a) is %p\n", ((char*)a) + sizeof a);
    // ^
    // both will print the same address

    int** b = &a;

    printf("sizeof(int) is %i\n", sizeof(int));
    printf("sizeof(int**) is %i\n", sizeof(int**));
    // ^
    // in test case this printed..
    // sizeof(int) is 4
    // sizeof(int**) is 8
    
    printf("b[1] is %p\n", b[1]);
    // ^
    // printed..
    // b[1] is 0x1c8
    printf("b[1] is %i\n", b[1]); //< as indexing into array of int** goes to a[2] rather than a[1] because int** is twice the size of int
    // ^
    // printed..
    // b[1] is 456

    return 0;
}


Thus the confusion I had arose from the expectation "that &a (where typeof(a) is int[5]) would be a different pointer address even though that is not what happened".

Maybe &a would require creating another variable of type int** implicitly as an array on the stack doesn't require such a variable because it can store the elements directly instead? Maybe that is why &a instead only returns the same pointer but of a different type, to avoid creating a hidden variable?
 
Share this answer
 
Comments
Maxim Kartavenkov 18-Jan-24 7:53am    
try to compare the results you have with the build of x86 platform...

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900