Gcc版本:
gcc 4.4.3
代码段:
#include <stdio.h>
struct str {
int len;
char s[0];
};
struct test {
struct str *p_str;
};
int main()
{
struct test t = { 0 };
if (t.p_str->s) // FLAG_0
printf("here!");
printf(t.p_str->s); // FLAG_1
return 0;
}
运行代码时出错:分段错误
我使用gdb进行调试。我发现它在 FLAG_1
时崩溃了我很困惑。
它在FLAG_0上运行正常,但在FLAG_1处崩溃。为什么?
同时,我发现 t.p_str的值是0x00 。我不明白if case是否正常。
注意:该代码仅用于研究!
答案 0 :(得分:4)
由于s
是一个数组,而不是指针,因此它永远不会为空。所以编译器可以省略检查,并假设它不是空的。如果它这样做,那么FLAG_0
将不会尝试取消引用空指针,因此您不会在该点获得分段错误。
当然,它可以自由地做任何其他感觉,因为程序有不确定的行为。
答案 1 :(得分:1)
如果你在谈论C ++,那么声明一个长度为0的数组是非法的.C ++ 11 §8.3.4数组:
如果存在常量表达式,则它应该是一个整数 常量表达式及其值应大于零。
这里的常量表达式是数组长度。但是,该标准允许使用new
动态创建零长度数组。
如果您正在谈论C,那么它已经在 FLAG_0 中输入了未定义的行为区域,因此该语言无法保证之后发生的事情。
答案 2 :(得分:0)
您声明了一个包含0个元素的数组。 char s[0];
。
空字符串至少有一个元素,即二进制零。
此struct test t = { 0 };
不会将您的结构初始化为0.它会将您的第一个元素初始化为0.在这种特殊情况下,您只需创建一个空指针。
您可以使用memset初始化。
#include <string.h>
typedef struct{
int len;
char s[10];
}MyStr;
int main()
{
MyStr str;
memset(&str, 0, sizeof(MyStr));
}
一个语句取消引用nullpointer:printf(t.p_str->s);
尝试使用t.p_str->s
beeing t.p_str
解析0
。
答案 3 :(得分:-1)
首先,代码是完全垃圾并在多个地方调用未定义的行为,因此任何事情都可能发生,包括崩溃或非崩溃,是否意外。
什么是t.p_str-&gt; s?
是一个数组。如果t.p_str是有效指针,则t.p_str-&gt; s将是s数组中第一个字符的地址。这是一个指针,它不是一个空指针,所以对于&#34; if&#34;作为真实结果的陈述。编译器实际上并不需要评估整个表达式,因为它并不关心它是什么指针,只是它不是一个空指针。这就是为什么t.p_str-&gt; s在这里不会崩溃的原因,因为程序永远不会对它进行评估。
在printf语句中,需要t.p_str-> s的实际值,以便崩溃。