ISO C90禁止将声明和代码混合...但在某些情况下允许吗?

7

我正在使用以下标志(其中cc是gcc 4.2或clang 8.0):

$ cc -Wall -Werror -pedantic -ansi -std=c89 main.c

(我知道-ansi标志在这种情况下有些多余)

以下命令会给出预期的错误:

main.c:31:8: warning: ISO C90 forbids mixing declarations and code [-Wdeclaration-after-statement]
  vec3 abc = {0};

int main()
{
  vec3 a = {0};
  vec3 b = {0};

  Vec3(2, 2, 2);

  vec3 abc = {0}; // Declared after a function call

  return 0;
}

然而,以下内容并不符合要求。
int main()
{
  vec3 a = Vec3(0, 1, 2);
  vec3 b = Vec3(0, 1, 2);

  vec3 abc = {0}; // Declared after a function call

  return 0;
}

使用函数初始化变量仍然算作声明和代码的混合吗?

Vec3函数非常基础,没有设置内联标志等。

vec3 Vec3(float x, float y, float z)
{
  vec3 rtn = {0};

  rtn.x = x;
  rtn.y = y;
  rtn.z = z;

  return rtn;
}

2
这个规则没有任何意义,它被从语言中移除是有原因的。 - Lundin
@Lundin:如果一个实现将自动对象初始化器限制为编译时常量表达式,那么可以简化单次编译器。该规则的另一个作用是禁止在自动对象生命周期内的声明位置之前分支,这会创建一些奇怪的边角情况。 - supercat
@supercat 关于单次编译器,C++ 在80年代早期就取消了这个规则,所以即使在那个时代也不可能是什么大问题。关于分支,正确的解决方案应该是删除非条件向上分支。此外,这符合 C90 标准:fail: { int x = 5; goto fail; }。更不用提 setjmp 了。 - Lundin
@Lundin:C++一直被设计为需要多遍编译。在对象未声明的块之前分支不会创建问题的边角情况,因为分支结束了对象的生命周期。例如void test(short i=0; loop: i++; int j=i; if (j < 10) goto loop; },在其生命周期内,存储的值是否发生变化?这是否意味着以某种方式访问了存储并对其进行了更改?是否使用任何类型为int的lvalue表达式来修改j?请注意,setjmp允许与自动持续时间对象交互。 - supercat
3个回答

9

在这段代码片段中

  vec3 a = Vec3(0, 1, 2);
  vec3 b = Vec3(0, 1, 2);

  vec3 abc = {0}; // Declared after a function call

只有声明,没有语句。用于初始化变量的函数调用是表达式,而不是语句。
这个警告似乎很令人困惑。

警告:ISO C90禁止混合声明和代码

更准确的写法应该是:

警告:ISO C90禁止混合声明和语句

例如,即使是多余的分号也会引入一个空语句。因此,通常编译器应该为以下代码段发出警告。
  vec3 a = Vec3(0, 1, 2);;
                       ^^^^
  vec3 b = Vec3(0, 1, 2);

没错。我以为调用函数作为初始化器仍然算作语句。那么,即使像这样的东西也是有效的: vec3 a = Vec3((1 == 1 ? 0 : 0), 1, 2); - Karsten Pedersen
1
@KarstenPedersen 在这里使用了条件运算符和逗号运算符,它们都是表达式。 - Vlad from Moscow
@VladfromMoscow:在 vec3 a = Vec3((1 == 1 ? 0 : 0), 1, 2); 中没有逗号运算符,只有用于分隔函数参数的逗号(它们不是逗号运算符;在评估函数参数时没有强制的从左到右的评估顺序,而对于逗号运算符,LHS 在 RHS 评估之前完全被评估)。 - Jonathan Leffler
@JonathanLeffler 你是对的。我没有注意到。 - Vlad from Moscow

5

第二个函数有三个连续的带初始值的变量定义,这不是问题。

C90(C89)不允许在语句后面进行声明,在给定的语句块(在{}之间)中,声明必须先于任何语句(非声明语句)。一个普通的函数调用,不是初始化程序的一部分,就是一个语句。

这就是为什么GCC报告该问题的选项是-Wdeclaration-after-statement


3

你误解了这个约束条件。我们可以有带初始化器的声明;第一个非声明语句标志着声明结束,在此之后,我们在该作用域中不允许有更多的声明。

非声明语句可以是表达式语句(如上所示),复合语句(例如ifwhile)或块。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接