0

考虑这段代码:

static char a[2][2] = {
    { 1, 2 },
    { 3, 4 },
};

int main()
{
    char **p = (char**)a; // needs cast, or compiler complains (which makes sense)

    printf("%p\n", p);

    printf("%p\n", &a[1][0]);
    printf("%d\n",  a[1][0]);
    printf("%p\n", &p[1][0]); // why null?  why doesn't compiler complain about this?
    printf("%d\n",  p[1][0]); // segfault, of course

    return 0;
}

这产生了这个输出:

0x804a018
0x804a01a
3
(nil)
Segmentation fault

我知道数组可以衰减为指针。我不明白为什么编译器(g++)会让我尝试做相反的事情。如果 p 是一个 char**,为什么它让我使用 p[x][x] 而没有太多警告?它显然甚至没有接近工作,因为结果指针为空。

顺便说一句,我问的是来自第 3 方的代码,这显然对他们有用。(在 Windows 中编译,而不是使用 g++)。所以,我不是在寻找有关如何修复此代码的建议,我已经知道如何做到这一点。我只是想了解为什么编译器不抱怨,为什么结果是空指针。

谢谢。

4

3 回答 3

6

您根本无法开始处理charchar**. 在内存中,数组看起来像这样:

| 1 | 2 | 3 | 4 |

每个元素都跟在前一个元素之后。数组名称将隐式转换为指向其第一个元素的指针:

| 1 | 2 | 3 | 4 |
  ^
  |

现在,如果您将此指针转换为 a char**,您是在说“如果您取消引用此指针,您会发现 a char*”,这是一个彻头彻尾的谎言。如果您取消引用指针,您将得到一个charwith value 1,而不是指针。

然后,当您这样做时p[1][0],您将 at 的值p[1](实质上将指针p移动sizeof(char*))视为指针并尝试取消引用它。当然,这会直接导致您出现未定义的行为。

编译器不允许你做那个演员,因为这是一个愚蠢的演员。不要这样做。仅仅因为 C 风格的演员表允许你这样做,这并不意味着它是一个好的操作。如果没有其他演员可以工作, C 风格的演员将回退到一个reinterpret_cast,在这种情况下,你几乎肯定会遇到未定义的行为。

于 2013-02-22T19:08:16.787 回答
2

实际给出一个答案:a是一个数组。对于像这样的语句char* p = a;a会自动衰减到指向第一个元素的指针{ 1, 2 },并且由于这是一个数组,因此也会衰减到它的第一个元素1然而,仍然衰减到 that char**p = a,然后你将整个数组转换为: (它解释为),这根本没有意义。它是指向数组的指针,而不是指向指针的指针。 就是你需要演员阵容的原因,因为它没有意义。 aarray of array of charan array of pointers to chars{0x01020304, 0x????????}

Second, when you type p[1], it treats that data (and a few bytes after it) as if they were an array of char pointers, {0x01020304, 0x00000000}, and returns the second element. (We can see it's all zeros in this particular case, because that's what printed on the screen later), Then the [0] dereferences that second mystery unknown pointer that happens to be NULL, giving you a segfault.

于 2013-02-22T19:11:22.500 回答
0

当你这样说时:

char a[2][2];
char **p = (char**)a;

这是一个错误。 a不是指向字符的指针数组。它是一个存储块数组,每个块都是一个字符数组。

于 2013-02-22T19:07:15.813 回答