选项-Wdeclaration-after-statement
仅具有风格性吗?我的意思是,如果我在我的C代码宏所有的情况下,其中一个变量被定义,我初始化他们在他们以相同的方式从这种旧风格的C90迁移到新的C99风格,代码是字节对字节相同吗?
选项-Wdeclaration-after-statement
是如何被记录的(来自man gcc
):
在块中的语句后面发现声明时发出警告。这个构造来自c++,是在ISO C99中引入的,默认情况下在GCC中被允许。ISO C90不支持。
它允许你输入像
这样的代码int a;
{
a = 42;
printf( "%d", a );
}
并把它变成
int a = 42;
printf( "%d", a );
这是我刚才问题的后续。
我可能在这里感到困惑,但我认为我们遗漏了一些东西。
在C99之前,所有变量声明必须出现在块中的任何语句之前。在哪里给它赋值并不重要(除非是在生成的汇编代码中)。int a;
do_something();
a = 7;
do_something_else();
在C中不能做但在c++中完全合法的是混合声明和语句:
do_something();
int a = 7;
do_something_else();
随着C99的出现,您现在可以在C中做与c++相同的事情,并在块中的任何位置声明变量,甚至在非声明语句之后。
最终,这是一个设计决策,基于编写一个泄漏到语言规范中的编译器更容易。编译器现在更复杂了(也更大了)。
语句在此上下文中指的是以;
结尾的完整表达式,或者是复合语句,{ }
。这些术语取自正式的C语法。
声明在语句不适用于问题中的代码。因为…在之前放置了声明一个(复合)语句。您的两个示例与这个gcc设置无关,因此您似乎误解了它的作用。
相反,相关的例子应该是:{
int a = 42;
}
和
{
puts("hello");
int a = 42;
}
前一个例子在任何C版本中都可以。后者在标准C中没问题,但在已弃用的C90标准中就不行了。因此,当前警告的唯一目的是为强制执行某种编码风格的标准C程序提供诊断消息。
绝大多数程序员不应该使用此警告,并坚持使用ISO 9899:2018中定义的标准C。
编辑:这仅仅是关于你是否可以将代码从C90重写为C99约定并获得二进制兼容性
是的,你可以重写成C99风格,没有任何后果。这些都不会影响变量在"抽象机器"中的处理方式。正如C所说。以下是一些例子,2个相关,1个不相关:
void relevant_c99_example (void) {
if ( 1 ) {
int a = 42;
printf( "%d", a );
}
else {
int a = 42;
printf( "%d", a );
}
}
void relevant_c90_example (void) {
int a = 42;
if ( 1 ) {
printf( "%d", a );
}
else {
printf( "%d", a );
}
}
void irrelevant_example (void) {
int a;
if ( 1 ) {
a = 42;
printf( "%d", a );
}
else {
a = 42;
printf( "%d", a );
}
}
https://godbolt.org/z/1oqvoesx7
当在gcc和clang上测试x86_64时,这3个产生100%相同的机器码。当禁用优化时,它们甚至产生100%相同的机器码!这是不应该期望的,因为这不是正确基准测试C代码的方式。但这里恰好是这样。
不,它不是。例如,上面的两个代码段将逐字节编译为相同的。下面的foo
和bar
也是如此,但是baz
不会。这是GodBolt的链接。这表明将初始化提升到声明中可能不会产生相同的代码
void foo () {
int a;
{
if ( 1 ) {
a = 42;
printf( "%d", a );
}
else {
a = 42;
printf( "%d", a );
}
}
}
void bar () {
int a = 42;
{
if ( 1 ) {
printf( "%d", a );
}
else {
printf( "%d", a );
}
}
}
void baz () {
int a;
{
if ( rand() > 0 ) {
a = 42;
printf( "%d", a );
}
else {
a = 42;
printf( "%d", a );
}
}
}