为什么我不能访问指向堆栈数组指针的指针?

And*_*eas 35 c

请看下面的代码。它尝试将数组作为 achar**传递给函数:

#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;
}
Run Code Online (Sandbox Code Playgroud)

事实上,我只能通过显式转换&test2char**已经暗示这段代码是错误的来编译它。

不过,我想知道它到底有什么问题。我可以传递一个指向动态分配数组的指针的指针,但我不能传递指向堆栈上数组指针的指针。当然,我可以通过首先将数组分配给临时变量来轻松解决该问题,如下所示:

char test[256];
char *tmp = test;
test[0] = 'B';
printchar(&tmp);
Run Code Online (Sandbox Code Playgroud)

不过,有人可以向我解释为什么不能直接投射char[256]char**吗?

Eri*_*hil 38

test is an array, not a pointer, and &test is a pointer to the array. It is not a pointer to a pointer.

You may have been told that an array is a pointer, but this is incorrect. The name of an array is a name of the entire object—all the elements. It is not a pointer to the first element. In most expressions, an array is automatically converted to a pointer to its first element. That is a convenience that is often useful. But there are three exceptions to this rule:

  • The array is the operand of sizeof.
  • The array is the operand of &.
  • The array is a string literal used to initialize an array.

In &test, the array is the operand of &, so the automatic conversion does not occur. The result of &test is a pointer to an array of 256 char, which has type char (*)[256], not char **.

To get a pointer to a pointer to char from test, you would first need to make a pointer to char. For example:

char *p = test; // Automatic conversion of test to &test[0] occurs.
printchar(&p);  // Passes a pointer to a pointer to char.
Run Code Online (Sandbox Code Playgroud)

Another way to think about this is to realize that test names the entire object—the whole array of 256 char. It does not name a pointer, so, in &test, there is no pointer whose address can be taken, so this cannot produce a char **. In order to create a char **, you must first have a char *.

  • @Ruslan:是的,根据 C 2018 6.3.2.1 3。 (8认同)

eml*_*lai 29

因为test不是指针。

&test为您提供一个指向数组的指针,类型为char (*)[256],与不兼容char**(因为数组不是指针)。这会导致未定义的行为。

  • 但是为什么 C 编译器允许将 char (*)[256] 类型的内容传递给 char** 呢? (3认同)

H.S*_*.S. 6

的类型test2char *。因此,类型&test2char **是与参数的类型兼容xprintchar()
的类型testchar [256]。因此,类型&testchar (*)[256]带参数的类型兼容xprintchar()

让我向您展示test和的地址差异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;
}
Run Code Online (Sandbox Code Playgroud)

输出:

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

test : 0x7ffee82eba00
&test : 0x7ffee82eba00
&test[0] : 0x7ffee82eba00
Run Code Online (Sandbox Code Playgroud)

这里要注意:

的输出(存储器地址)test2&test2[0]数字相同,并且它们的类型也是相同的,其是char *
但是test2&test2是不同的地址,它们的类型也不同。
的类型test2char *
的类型&test2char **

x = &test2
*x = test2
(*x)[0] = test2[0] 
Run Code Online (Sandbox Code Playgroud)

的输出(存储地址)test&test并且&test[0]数字相同但它们的类型是不同的
的类型testchar [256]
的类型&testchar (*) [256]
的类型&test[0]char *

如输出所示&test,与&test[0].

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

因此,您遇到了分段错误。