为什么编译这段代码没有错误?这是一个gcc bug吗?

时间:2017-09-11 15:20:14

标签: c

以下代码正在成功编译,即使struct中的char * name声明没有分号。

#include<stdio.h>

typedef struct map
{
   int id;
   char *name
}map;

int main()
{
    return 0;
}

2 个答案:

答案 0 :(得分:2)

自C2011起的structunion声明的语法:

struct-or-union-specifier:
    struct-or-union identifieropt { struct-declaration-list }
    struct-or-union identifier

struct-or-union:
    struct
    union

struct-declaration-list:
    struct-declaration
    struct-declaration-list struct-declaration

struct-declaration:
    specifier-qualifier-list struct-declarator-listopt ;
    static_assert-declaration

specifier-qualifier-list:
    type-specifier specifier-qualifier-listopt
    type-qualifier specifier-qualifier-listopt

struct-declarator-list:
    struct-declarator
    struct-declarator-list , struct-declarator

struct-declarator:
    declarator
    declaratoropt : constant-expression

static_assert-declaration:
    _Static_assert ( constant-expression , string-literal ) ;

C 2011 Online Draft,6.7.2.1

非常明确 - struct-declaration-list 中的每个声明都必须;终止 - 在此之前不允许删除分号关闭}。除非你以某种方式压制它,否则gcc 在这里发出诊断信息。 “

修改

Myst在这里提出了一个有效点 - gcc发出警告,但不会停止翻译并仍然生成可执行文件(除非我指定-Werror或类似的东西)。但是,为什么它与6.7.2.1/8无关。原因是这个特定的语法错误相对容易恢复 - 缺少所需的分号,但由于下一个标记是}struct定义的结束union,编译器可以安全地假设它被视为有效的成员声明并继续前进。 gcc发出警告(因为诊断必须在语法违规时发出),但此时没有理由停止翻译。

Myst引用的段落与

等情况有关
struct foo;

struct bar {
  struct foo blah;
};

当我们尝试声明struct foo成员时,blah类型尚未完成;编译器不知道要为它预留多少空间。在我们看到其定义的结束struct foo之前,}将无法完成,因此在此之前我们无法创建struct foo实例

为了扩展我在评论中提出的观点,C标准没有区分“警告”和“错误” - 它只要求发布某种诊断 语法错误或约束违规:

5.1.1.3诊断

1符合要求的实施应至少产生一条诊断信息(在 如果是预处理翻译单元或翻译单元,则是实现定义的方式 包含违反任何语法规则或约束的行为,即使行为也是明确的 指定为未定义或实现定义。诊断消息不一定是 在其他情况下产生。 9) 9)意图是实现应该识别每个的性质,并在可能的情况下进行本地化 违反。当然,只要a,实现就可以自由地产生任意数量的诊断 有效的程序仍然正确翻译。它也可能成功翻译无效程序。

个别编译器可以决定特定诊断是否被视为“警告”或“错误”,并且他们可以决定是否停止对特定错误的转换。与上面的段落一样,允许编译器翻译无效程序。

答案 1 :(得分:-1)

我怀疑这个问题可能与C11标准有关(我没有更旧的版本,但它可能是相同的),它指定了(6.7.2.1第8段):

  

struct-or-union-specifier中struct-declaration-list的存在在转换单元中声明了一个新类型。 struct-declaration-list是结构或联合成员的一系列声明。如果struct-declaration-list不包含任何命名成员(直接或通过匿名结构或匿名联合),则行为未定义。 类型不完整,直到终止列表的}之后,之后完成

这意味着正在执行两项操作:

  1. 终止列表(struct-declaration-list)。

  2. 完成类型(结构或联合)。

  3. 我认为这可能(错误地)被构造为意味着}可以通过作为终止结构声明节点的分号闭包来终止struct-declaration-list。

    否则,有人可能会争辩说,应该有一个动作(类型的完成)而不是两个(结束声明列表的结束+类型的完成)。

    这种宽松的解释(可能不反映作者的意图,恕我直言)使这成为一个可疑的宽松语言解释特征而不是问题。

    无论哪种方式,如果缺少分号,则clanggcc的当前版本报告警告(使用c11标志时) )。这表明行为的一致性,这表明这是对标准的解释而不是错误。

    考虑到这种可能的歧义以及声明后}不会对代码含义产生任何疑问这一事实,我认为编译器开发人员倾向于提出警告而不是错误,这肯定是合法的,没有错误报告是必需的。

    修改

    我阅读了John Bode的回答和Olaf的评论并相应地编辑了我的答案,以使我的观点更加清晰。

    虽然约翰和奥拉夫都比我自己聪明,虽然我看到了他们观点的优点,但我认为标准在这一点上并不够明确。

    John的答案中的详细引用确实将每个struct-declaration定义为以分号(;)结尾,这意味着列表应以分号结尾。

    然而,同一部分的文字解释似乎表明}可能具有双重功能。

    这个建议的解释空间可能过于宽松,但是在编译代码(恕我直言)时提出警告而不是错误是一个足够好的理由 - 特别是代码本身可以'被误解了。