如何解释以下C代码片段?

时间:2014-05-02 10:54:17

标签: c gcc

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是否正常。

注意:该代码仅用于研究!

4 个答案:

答案 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的实际值,以便崩溃。