Why can't I access a pointer to pointer for a stack array?

Related searches

Please take a look at the following code. It tries to pass an array as a char** to a function:

#include <stdio.h>
#include <stdlib.h>

static void printchar(char **x)
{
    printf("Test: %c\n", (*x)[0]);
}

int main(int argc, char *argv[])
{
    char test[256];
    char *test2 = malloc(256);

    test[0] = 'B';
    test2[0] = 'A';

    printchar(&test2);            // works
    printchar((char **) &test);   // crashes because *x in printchar() has an invalid pointer

    free(test2);

    return 0;
}

The fact that I can only get it to compile by explicitly casting &test2 to char** already hints that this code is wrong.

Still, I'm wondering what exactly is wrong about it. I can pass a pointer to a pointer to a dynamically allocated array but I can't pass a pointer to a pointer for an array on the stack. Of course, I can easily work-around the problem by first assigning the array to a temporary variable, like so:

char test[256];
char *tmp = test;
test[0] = 'B';
printchar(&tmp);

Still, can someone explain to me why it doesn't work to cast char[256] to char** directly?

Because test is not a pointer.

&test gets you a pointer to the array, of type char (*)[256], which is not compatible with char** (because an array is not a pointer). This results in undefined behavior.

Why Can't I? (Original Version), Provided to YouTube by Universal Music Group Why Can't I? (Original Version) � Liz Phair Liz Duration: 3:29 Posted: Oct 3, 2018 Being overweight, getting too little exercise, and smoking all can work against the good blood flow that is key to erections. For some men, a little alcohol may help take the edge off.

Why can't the Cubs hit with the bases loaded?, Those numbers are all far below the MLB bases-loaded average so far this year, which is .774. That seems to be about normal — last year's MLB� More Americans can vote by mail in November than before the pandemic; find out which states have changed rules. Barring a landslide, we may not have a result in the presidential election on Nov. 3.

The type of test2 is char *. So, the type of &test2 will be char ** which is compatible with the type of parameter x of printchar(). The type of test is char [256]. So, the type of &test will be char (*)[256] which is not compatible with the type of parameter x of printchar().

Let me show you the difference in terms of addresses of test and test2.

#include <stdio.h>
#include <stdlib.h>

static void printchar(char **x)
{
    printf("x = %p\n", (void*)x);
    printf("*x  = %p\n", (void*)(*x));
    printf("Test: %c\n", (*x)[0]);
}

int main(int argc, char *argv[])
{
    char test[256];
    char *test2 = malloc(256);

    test[0] = 'B';
    test2[0] = 'A';

    printf ("test2 : %p\n", (void*)test2);
    printf ("&test2 : %p\n", (void*)&test2);
    printf ("&test2[0] : %p\n", (void*)&test2[0]);
    printchar(&test2);            // works

    printf ("\n");
    printf ("test : %p\n", (void*)test);
    printf ("&test : %p\n", (void*)&test);
    printf ("&test[0] : %p\n", (void*)&test[0]);

    // Commenting below statement
    //printchar((char **) &test);   // crashes because *x in printchar() has an invalid pointer

    free(test2);

    return 0;
}

Output:

$ ./a.out 
test2 : 0x7fe974c02970
&test2 : 0x7ffee82eb9e8
&test2[0] : 0x7fe974c02970
x = 0x7ffee82eb9e8
*x  = 0x7fe974c02970
Test: A

test : 0x7ffee82eba00
&test : 0x7ffee82eba00
&test[0] : 0x7ffee82eba00

Point to note here:

The output (memory address) of test2 and &test2[0] is numerically same and their type is also same which is char *. But the test2 and &test2 are different addresses and their type is also different. The type of test2 is char *. The type of &test2 is char **.

x = &test2
*x = test2
(*x)[0] = test2[0] 

The output (memory address) of test, &test and &test[0] is numerically same but their type is different. The type of test is char [256]. The type of &test is char (*) [256]. The type of &test[0] is char *.

As the output shows &test is same as &test[0].

x = &test[0]
*x = test[0]       //first element of test array which is 'B'
(*x)[0] = ('B')[0]   // Not a valid statement

Hence you are getting segmentation fault.

“Why Can’t We Be Friends?” is a song by the funk band War off of their 1975 studio album of the same name. The song reached #6 on the Billboard Hot 100 in the summer of 1975.

You cannot access a pointer to a pointer because &test is not a pointer—it's an array.

If you take the address of an array, cast the array and the address of the array to (void *), and compare them, they will (barring possible pointer pedantry) be equivalent.

What you're really doing is similar to this (again, barring strict aliasing):

putchar(**(char **)test);

which is quite obviously wrong.

The nation’s government is deeply in debt, and this debt matters because Italy doesn’t have its own currency; this means that it can’t do what we do, and print lots of money in a crisis.

Your code expects the argument x of printchar to point to memory that contains a (char *).

In the first call, it points to the storage used for test2 and is thus indeed a value that points to a (char *), the latter pointing to the allocated memory.

In the second call, however, there is no place where any such (char *) value might be stored and so it is impossible to point to such memory. The cast to (char **) you added would have removed a compilation error (about converting (char *) to (char **)) but it would not make storage appear out of thin air to contain a (char *) initialized to point to the first characters of test. Pointer casting in C does not change the actual value of the pointer.

In order to get what you want, you have to do it explicitly:

char *tempptr = &temp;
printchar(&tempptr);

I assume your example is a distillation of a much larger piece of code; as an example, perhaps you want printchar to increment the (char *) value that the passed x value points to so that on the next call the next character is printed. If that isn't the case, why don't you just pass a (char *) pointing to the character to be printed, or even just pass the character itself?

Why can we see the Moon in daylight this week? People only tend to notice the Moon during daylight hours when it’s: In their line of sight, so about 10º to 20º above the horizon.

Can’t go? You’re not alone. About 20% of Americans have occasional constipation-- bowel movements less than three times a week.Or if they do poop, the output is hard, small, and painful to

That’s why some companies try to play it safe by forbidding employees from conveying any messaging, political or otherwise, through what they wear or have on their desks, he said. "But they can

The use of the chemical agent to disperse crowds has come under criticism lately.

Comments
  • But why does the C compiler then allow passing something of type char (*)[256] to char**?
  • @ComFreek I suspect that with max warnings and -Werror, it doesn't allow that.
  • @ComFreek: It doesn't really allow it. I have to force the compiler to accept it by explicitly casting it to char**. Without that cast, it doesn't compile.
  • Is this list of three exceptions exhaustive?
  • @Ruslan: Yes, per C 2018 6.3.2.1 3.
  • Oh, and in C11 there was also the _Alignof operator mentioned in addition to sizeof and &. I wonder why they removed it...
  • @Ruslan: That was removed because it was a mistake. _Alignof only accepts a type name as an operand and never accepted an array or any other object as an operand. (I do not know why; it seems syntactically and grammatically it could be like sizeof, but it is not.)
  • Good answer; I agree the easiest way to keep this straight is to think about whether or not there's a C object that holds the address of the array, i.e. a pointer object that you can take the address of to get a char **. Array variables/objects simply are the array, with the address being implicit, not stored anywhere. No extra level of indirection to access them, unlike with a pointer variable that points to other storage.
  • Taking the address of test is not the same as taking the address of test[0]. The former has type char (*)[256], and the latter has type char *. They are not compatible, and the C standard permits them to have different representations.
  • When formatting a pointer with %p, it should be converted to void * (again for reasons of compatibility and representation).