GCC 选项 -Wdeclaration-after-statement 是否纯粹是风格化的?
Is the GCC option -Wdeclaration-after-statement purely stylistic?
-Wdeclaration-after-statement
选项是否仅用于文体?我的意思是,如果我在我的 C 代码中定义了一个变量的所有情况都被宏化了,并且我以相同的方式在它们中初始化了它们,从这个旧样式的 C90 迁移到更新的 C99 样式,那么该代码是否是 byte-for -字节相同?
以下是选项 -Wdeclaration-after-statement
的记录方式(来自 man gcc
):
Warn when a declaration is found after a statement in a block. This construct, known from C++, was introduced with ISO C99 and is by default allowed in GCC. It is not supported by ISO C90.
而且它允许你使用像
这样的代码
int a;
{
a = 42;
printf( "%d", a );
}
然后变成
int a = 42;
printf( "%d", a );
这是一个。
不,不是。例如,上面的两个片段将逐字节编译相同。下面的foo
和bar
也会,但baz
不会。这里有一个link to 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 );
}
}
}
我可能在这里感到困惑,但我认为我们遗漏了一些东西。
在 C99 之前,所有变量声明都必须在块中的任何语句之前出现。在何处为其赋值并不重要(可能在生成的汇编代码中除外)。
int a;
do_something();
a = 7;
do_something_else();
您在 C 中不能做但在 C++ 中一直完全合法的是混合声明和语句:
do_something();
int a = 7;
do_something_else();
随着 C99 的出现,您现在可以在 C 中执行与在 C++ 中相同的操作,并在块中的任何位置声明变量,即使是在非声明语句之后。
最终,这是一个基于使编写泄漏到语言规范中的编译器更容易的设计决策。编译器现在更复杂一些(而且更大)。
在此上下文中的语句要么指代以 ;
结尾的完整表达式,要么指代 复合语句 、{ }
。这些术语取自正式的 C 语法。
声明after声明不适用于您问题中的代码。因为...您将声明 放在了 一个(复合)语句之前。您的两个示例与此 gcc 设置无关,因此您可能误解了它的作用。
相反,相关的例子是:
{
int a = 42;
}
对比
{
puts("hello");
int a = 42;
}
前面的例子在任何 C 版本中都可以。后者适用于标准 C,但不适用于已弃用的 C90 标准。因此,现在警告的唯一目的是为强制执行某种编码风格的标准 C 程序提供诊断消息。
那里的绝大多数程序员不应使用此警告并坚持使用 ISO 9899:2018 中定义的标准 C。
编辑:
This is merely about whether or not you can rewrite code from a C90 to C99 conventions and obtain binary compatibility
是的,您可以重写为 C99 风格而不会产生任何后果。 None 其中影响变量在 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
这 3 个在 x86_64 的 gcc 和 clang 上测试时产生 100% 相同的机器代码。当禁用优化时,它们甚至会生成 100% 相同的机器代码!哪一个不应该期望,因为那不是您正确基准测试 C 代码的方式。但是这里恰好是这样。
-Wdeclaration-after-statement
选项是否仅用于文体?我的意思是,如果我在我的 C 代码中定义了一个变量的所有情况都被宏化了,并且我以相同的方式在它们中初始化了它们,从这个旧样式的 C90 迁移到更新的 C99 样式,那么该代码是否是 byte-for -字节相同?
以下是选项 -Wdeclaration-after-statement
的记录方式(来自 man gcc
):
Warn when a declaration is found after a statement in a block. This construct, known from C++, was introduced with ISO C99 and is by default allowed in GCC. It is not supported by ISO C90.
而且它允许你使用像
这样的代码int a;
{
a = 42;
printf( "%d", a );
}
然后变成
int a = 42;
printf( "%d", a );
这是一个
不,不是。例如,上面的两个片段将逐字节编译相同。下面的foo
和bar
也会,但baz
不会。这里有一个link to 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 );
}
}
}
我可能在这里感到困惑,但我认为我们遗漏了一些东西。
在 C99 之前,所有变量声明都必须在块中的任何语句之前出现。在何处为其赋值并不重要(可能在生成的汇编代码中除外)。
int a;
do_something();
a = 7;
do_something_else();
您在 C 中不能做但在 C++ 中一直完全合法的是混合声明和语句:
do_something();
int a = 7;
do_something_else();
随着 C99 的出现,您现在可以在 C 中执行与在 C++ 中相同的操作,并在块中的任何位置声明变量,即使是在非声明语句之后。
最终,这是一个基于使编写泄漏到语言规范中的编译器更容易的设计决策。编译器现在更复杂一些(而且更大)。
在此上下文中的语句要么指代以 ;
结尾的完整表达式,要么指代 复合语句 、{ }
。这些术语取自正式的 C 语法。
声明after声明不适用于您问题中的代码。因为...您将声明 放在了 一个(复合)语句之前。您的两个示例与此 gcc 设置无关,因此您可能误解了它的作用。
相反,相关的例子是:
{
int a = 42;
}
对比
{
puts("hello");
int a = 42;
}
前面的例子在任何 C 版本中都可以。后者适用于标准 C,但不适用于已弃用的 C90 标准。因此,现在警告的唯一目的是为强制执行某种编码风格的标准 C 程序提供诊断消息。
那里的绝大多数程序员不应使用此警告并坚持使用 ISO 9899:2018 中定义的标准 C。
编辑:
This is merely about whether or not you can rewrite code from a C90 to C99 conventions and obtain binary compatibility
是的,您可以重写为 C99 风格而不会产生任何后果。 None 其中影响变量在 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
这 3 个在 x86_64 的 gcc 和 clang 上测试时产生 100% 相同的机器代码。当禁用优化时,它们甚至会生成 100% 相同的机器代码!哪一个不应该期望,因为那不是您正确基准测试 C 代码的方式。但是这里恰好是这样。