C89 和变量初始化

C89 and variable initialization

C89(C90、ANSI-C)不允许在代码中混用变量声明。我想知道变量初始化在多大程度上被认为是 "code".

也许只有用常量表达式初始化才有效?

具体来说,如果我正在编写 C 代码并且我想安全行事(最大化与 ANSI-C 编译器的兼容性),是否应该将以下内容视为安全的?

  void f1(void) { 
      int x = 30;
      int y = 40;
      int z;
      /* ... */
  }

  void f2(void) { 
      int x = 30, y = 40;
      int z;
      /* ... */
  }

  #define MYCONST (90)
  void f3(void) { 
      int x = 3; 
      int y = 4 + MYCONST;
      int z;
      /* ... */
  }

  void f4(void) { 
      int x = 3;
      int y = time(NULL);
      int z = 10 + x;
      /* ... */
  }

should the following be considered safe?

您发布的所有代码都是安全的。

  1. 您可以在任何范围内声明任意数量的变量。
  2. 初始化变量的代码可以使用语言提供的任何方法。

但是,在非变量声明代码之后声明变量是不合法的。

void foo()
{
   int i = 0;
   i = 2;      // Does not declare a variable.
   int j = 10; // Not legal.
}

以上代码适用于 gcc。但是,如果您使用 -pedantic 标志,您将看到一条类似于以下内容的警告消息:

soc.c:5:4: warning: ISO C90 forbids mixed declarations and code [-Wpedantic]
    int j = 10;

F1、f2、f3 有效,但 f4 无效,因为声明和定义是混合的

您的所有示例都是合法的,可以安全地与符合标准的 ANSI-C 编译器一起使用。

使用 -std=c89-pedantic 编译 gcc 中的代码不会生成任何警告。

关于 C89/99 版本差异的额外观察,使用以下代码:
(C 标准的 C99 扩展似乎在附加声明的放置位置上变得不那么迂腐了)

int main() 
{ 
    int a; //declaration only
    int b; //declaration only
    b = 5; //assignment only (no declaration)
    int c; //declaration only  (fails C89, Compiles C99)

    return 0; 
}

下图说明了使用 C89 设置并在 assignment 语句后尝试 declaration 时出现的错误。 (设置为使用C99时,代码编译无错误)

C89 不允许在同一范围内混合声明和语句。我们在这里指的是函数内部的声明和语句,因为 C 中不允许在函数外部使用语句。

int a = foo(a, b);  /* declared at block-scope */

上面一行是(有效的 C89)声明,而不是语句,因此您的所有函数 f1、f2、f3 和 f4 都是有效的 C89。

但是您可以在不同的范围内混合声明和语句:

void bar(void)
{
    int i = 0;  /* declaration */
    i++;  /* statement */
    {
        int j = 0;  /* declaration */
        j++;  /* statement */
     }
 }

以上函数C89有效

C99 标准的前言本身提到 "mixed declarations and code" 作为对 C90 的更改之一。恕我直言,这是一个糟糕的用词选择,因为根本不清楚 "code" 是什么意思。它可以轻松引用 C 源文件中可能出现的所有内容。

C99 所做的实际更改是允许在一个块中混合声明和语句声明声明之间的区别由语言语法明确定义。

您的示例之一中的行:

int y = time(NULL);

是一个声明,而不是一个语句,即使它导致某些代码在 运行 时执行。是否存在初始化器,以及该初始化器是否为常量表达式,不会影响某物是否被视为声明。

您问题中的所有四个示例在 C89/C90、C99 和 C11 中均有效。在每种情况下,该块仅包含声明,不包含语句。

如果您想在 C90 中混合声明和语句,可以通过引入嵌套块来实现:

void func(void) {
    int x = 1; /* a declaration */
    x = 2;     /* a statement; no declarations may follow in C90 */
    {
        int y = 3; /* a declaration */
        y = 4;     /* a statement */
    }
}

内部块本身就是一个语句。因为它是一个声明,所以它可以出现在那个上下文中。因为它是复合语句,所以它本身可以包含一系列声明,后跟一系列语句。

即使在 C99 或 C11 中,像这样引入嵌套块也是有利的。 y 的范围和生命周期在包含其声明的块的结束 } 处结束。限制声明的范围可以使代码更容易理解。

(一些背景知识:C89 是 ANSI 于 1989 年发布的标准。ISO 采用了它,但在文档中做了一些更改,但它描述的语言没有像 C90 一样。ISO 发布了更新的标准 C99,然后被ISO采纳,ISO发布了另一个更新的标准C11,也被ANSI采纳了。根据ANSI和ISO的说法,2011标准是当前标准,之前的所有版本都已经过时了。但是由于历史原因,短语 "ANSI C" 通常指的是 1989 和 1990 版本描述的语言。我通常尽量避免使用短语 "ANSI C",而是指 "ISO C",如果相关的话加上出版年份。)