请考虑以下代码:
static char a[2][2] = {
{ 1, 2 },
{ 3, 4 },
};
int main()
{
char **p = (char**)a; // needs cast, or compiler complains (which makes sense)
printf("%pn", p);
printf("%pn", &a[1][0]);
printf("%dn", a[1][0]);
printf("%pn", &p[1][0]); // why null? why doesn't compiler complain about this?
printf("%dn", p[1][0]); // segfault, of course
return 0;
}
产生以下输出:
0x804a018
0x804a01a
3
(nil)
Segmentation fault
我知道数组可以衰减到指针。 我不明白的是为什么编译器(g++(会让我尝试做相反的事情。 如果 p 是一个字符**,为什么它让我在没有警告的情况下使用 p[x][x]? 它显然甚至无法正常工作,因为生成的指针为空。
顺便说一句,我问的是来自第三方的代码,这显然对他们有用。 (在Windows中编译,而不是使用g ++(。 因此,我不是在寻求有关如何修复此代码的建议,我已经知道如何做到这一点。 我只是想了解为什么编译器不抱怨,以及为什么结果是空指针。
谢谢。
您根本无法像char**
一样开始处理 2D char
数组。在内存中,数组如下所示:
| 1 | 2 | 3 | 4 |
每个元素都跟在前一个元素之后。数组名称将被隐式转换为指向其第一个元素的指针:
| 1 | 2 | 3 | 4 |
^
|
现在,如果你把这个指针转换成一个char**
,你说"如果你取消引用这个指针,你会发现一个char*
",这是一个彻头彻尾的谎言。如果取消引用指针,您将获得值为 1
的char
,而不是指针。
然后当你做p[1][0]
时,你把p[1]
处的值(实质上是将足尖p
移动了sizeof(char*)
(作为指针,并试图取消引用它。当然,这会直接导致您出现未定义的行为。
编译器不让你做那个转换,因为这是一个愚蠢的转换。不要这样做。仅仅因为 C 型强制转换允许您这样做,这并不意味着这是一个可以操作的操作。如果没有其他演员表工作,C 型演员表将回退到reinterpret_cast
,在这种情况下,您几乎肯定会遇到未定义的行为。
实际上给出一个答案:a
是一个数组。 对于像char* p = a;
这样的语句,a
会自动衰减到指向第一个元素{ 1, 2 }
的指针,并且由于这是一个数组,它也将衰减到它的第一个元素1
。然而,对于char**p = a
,a
仍然衰减到那个array of array of char
,然后你把整个数组投射到an array of pointers to chars
:(它解释为{0x01020304, 0x????????}
(,这根本没有意义。 它是指向数组的指针,而不是指向指针的指针。 这就是为什么你需要演员阵容,因为它没有意义。
其次,当你键入 p[1]
时,它会将该数据(以及它后面的几个字节(视为char
指针数组,{0x01020304, 0x00000000}
,并返回第二个元素。 (在这种特殊情况下,我们可以看到它都是零,因为这是稍后打印在屏幕上的内容(,然后[0]
取消引用第二个恰好为 NULL 的神秘未知指针,给你一个段错误。
当你这样说时:
char a[2][2];
char **p = (char**)a;
这是一个错误。 a
不是指向字符的指针数组。它是一个存储块数组,每个存储块都是一个字符数组。