为什么这段代码编译时没有错误?这是一个gcc错误吗?
下面的代码正在成功编译,即使结构中的char*name声明没有分号为什么这段代码编译时没有错误?这是一个gcc错误吗?,c,C,下面的代码正在成功编译,即使结构中的char*name声明没有分号 #include<stdio.h> typedef struct map { int id; char *name }map; int main() { return 0; } #包括 类型定义结构映射 { int-id; 字符*名称 }地图; int main() { 返回0; } 我怀疑问题可能与C11标准有关(我没有旧版本,但可能是相同的),其中规定(6.7.2.1第8段): 结构或联
#include<stdio.h>
typedef struct map
{
int id;
char *name
}map;
int main()
{
return 0;
}
#包括
类型定义结构映射
{
int-id;
字符*名称
}地图;
int main()
{
返回0;
}
我怀疑问题可能与C11标准有关(我没有旧版本,但可能是相同的),其中规定(6.7.2.1第8段):
结构或联合说明符中的结构声明列表在翻译单元中声明了一个新类型。结构声明列表是结构或联合成员的一系列声明。如果结构声明列表不包含任何命名成员(直接或通过匿名结构或匿名联合),则行为未定义该类型在终止列表的}
之后不完整,之后完成
这意味着正在执行两个操作:
}
可以通过充当终止结构声明节点的分号闭包来终止结构声明列表
否则,人们可能会争辩说,应该有一个操作(类型的完成)而不是两个操作(结构声明列表的终止+类型的完成)
这种允许性解释(可能不反映作者的意图,IMHO)使其成为一个有问题的允许性语言解释特征,而不是一个问题
无论哪种方式,clang
和gcc
的当前版本都会报告如果缺少分号(使用c11标志时)的警告。这显示了行为的一致性,这表明这是对标准的解释,而不是错误
考虑到这种可能的模糊性以及声明后的}
对代码的含义没有任何疑问,我认为编译器开发人员倾向于提出警告而不是错误是绝对合法的,并且不需要错误报告
编辑
我阅读了约翰·博德的答案和奥拉夫的评论,并相应地编辑了我的答案,使我的观点更加清晰
尽管约翰和奥拉夫都比我聪明得多,尽管我看到他们观点的优点,但我认为在这一点上,标准还不够明确
的确,John回答中的详细引用将每个结构声明定义为以分号(;
)结尾,这意味着列表应该以分号结尾
然而,同一节中的文本解释似乎表明,}
可能具有双重功能
这种建议的解释空间可能过于宽松,但是这是一个足够好的理由,在编译代码(IMHO)时发出警告而不是错误,尤其是代码本身不能被误解。从C2011开始的struct
和union
声明的语法:
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 ) ;
当我们试图声明blah
成员时,struct foo
类型尚未完成;编译器不知道要为它留出多少空间struct foo
在看到其定义的结尾之前是不完整的,因此在此之前我们无法创建struct foo
的实例
为了扩展我在评论中提出的一点,C标准没有区分“警告”和“错误”——它只要求对语法错误或约束冲突发布某种诊断:
5.1.1.3诊断
1一致性实施应产生至少一条诊断信息(在
一种实现定义的方式)如果是预处理翻译单元或翻译单元
包含违反任何语法规则或约束的行为,即使该行为也是显式的
指定为未定义或实现已定义。诊断消息不需要
在其他情况下产生。9)
9) 其目的是一个实现应该识别每一个的性质,并在可能的情况下进行本地化
违反当然,一个实现可以自由地生成任意数量的诊断,只要
有效的程序仍被正确翻译。它还可以成功地翻译无效的程序。
个别编译器可以决定某个特定诊断是算作“警告”还是“错误”,还可以决定是否停止对某个特定错误的翻译。正如上文所说,编译器可以翻译无效的程序 gcc版本是4.8.4((Ubuntu4.8.4-2ubuntu1~14.04.1)),考虑到}
在声明对代码的含义没有任何疑问之后,gcc忽略错误的事实可能被认为是一个特性,而不是一个问题……;-)gcc版本5.4.0将其报告为警告(“test.c:7:1:warning:struct或union末尾没有分号”)。考虑到丢失的分号不会改变代码的含义,我认为对任何关心它的人来说,它都是正确报告的。@savram:1)4.8.4已经很旧了。2) 它确实会生成一条错误消息。如果快速检查显示有不同的错误,则不要推荐错误报告。@Taelsin:是的。(并不是说一个人无论如何都应该编译为一个18年的过时版本)我认为在任何gcc版本中,它都应该在编译时给出错误。@JayBhaskar-尽管我从维护人员的角度同意。。。主流编译器的行为基本上是一致的(产生警告而不是错误),这一事实使它足够好。如果某个东西编译(带有警告)
struct foo;
struct bar {
struct foo blah;
};