【问题标题】:ISO C90 forbids mixing declarations and code... but allows it in certain instances?ISO C90 禁止混合声明和代码......但在某些情况下允许它?
【发布时间】:2019-11-26 11:54:25
【问题描述】:

我正在使用以下标志(其中 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;
}

【问题讨论】:

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

标签: c declaration c89 statements


【解决方案1】:

在这段代码中sn-p

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

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

只有声明。没有声明。用于初始化变量的函数调用是表达式。它们不是陈述。

好像是这个警告

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

令人困惑。这样写会更正确

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

例如,即使是多余的分号也会引入空语句。因此,通常编译器即使对以下代码 sn-p 也应该发出警告

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

【讨论】:

  • 哦,对了。我假设调用一个函数作为初始化程序仍然算作一个语句。似乎这样的事情也是有效的: vec3 a = Vec3((1 == 1 ? 0 : 0), 1, 2);
  • @KarstenPedersen 这里使用了条件运算符和逗号运算符即表达式。
  • @VladfromMoscow:vec3 a = Vec3((1 == 1 ? 0 : 0), 1, 2); 中没有逗号运算符 - 只有逗号用于分隔函数参数(不是逗号运算符;在评估参数时没有强制的从左到右评估顺序一个函数,而使用逗号运算符,则在评估 RHS 之前完全评估 LHS)。
  • @JonathanLeffler 你是对的。我没有专心。
【解决方案2】:

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

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

这就是报告问题的 GCC 选项是 -Wdeclaration-after-statement 的原因。

【讨论】:

    【解决方案3】:

    您误解了约束。我们可以使用初始化器进行声明;第一个非声明语句标志着声明的结束,在那之后我们不允许在该范围内进行更多声明。

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-10-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-08-08
      相关资源
      最近更新 更多