【问题标题】:Why does auto a=1; compile in C?为什么 auto a=1;用C编译?
【发布时间】:2014-06-17 20:14:49
【问题描述】:

代码:

int main(void)
{
    auto a=1;
    return 0;
}

当文件具有 .c 扩展名时,MS Visual Studio 2012 编译器编译时不会出错。我一直认为,当你使用 .c 扩展名时,编译应该按照 C 语法,而不是 C++。此外,据我所知,自 C++11 以来,在 C++ 中允许没有类型的 auto,这意味着类型是从初始化程序推导出来的。

这是否意味着我的编译器没有坚持使用 C,或者代码在 C 语言中实际上是正确的?

【问题讨论】:

  • 要么使用 C++ 模式编译(可能),要么 MS 仍然停留在上个千年。隐式 int 已于 1999 年从 C 标准中删除。
  • @JensGustedt MSVC++ 仅支持 C89(以及 C99 的一些功能)。它更像是一个 C++ 编译器。
  • @Brandin:使用 /Wall 选项会发出警告 C4431,表示缺少类型说明符并且 C 不再支持默认 int (请参阅 Jens 的评论)。这有点矛盾,因为显然这个编译器支持它......
  • @JensGustedt 以这种方式衡量,2012 年发布的 GCC 4.7(以及更高版本,我怀疑——我手头没有这些)也“停留在上个千年”。它在没有给出任何标志的情况下编译 OP 的代码,甚至没有通知。
  • @delnan,我至少假设 OP 已打开警告级别。我显然错了。从某种意义上说,这是真的,gcc 也仍然卡在那里,因为它们仍然没有 C99(或变体)作为默认值。即使没有标志,clang 也会对构造发出警告。

标签: c auto c11


【解决方案1】:

auto 是一个旧的 C 关键字,意思是“本地范围”。 auto aauto int a 相同,并且由于局部范围是在函数内声明的变量的默认值,因此在此示例中也与 int a 相同。

这个关键字实际上是 C 的前身 B 的遗留物,其中没有基本类型:一切都是 int,指向 int 的指针,int 的数组。(*)声明将是 autoextrn [原文如此]。 C继承了“一切都是int”作为默认规则,所以你可以用

声明整数
auto a;
extern b;
static c;

ISO C 摆脱了这一点,但许多编译器仍然接受它以实现向后兼容性。如果它看起来不熟悉,那么你应该意识到一个相关的规则正在起作用

unsigned d;  // actually unsigned int

这在现代代码中仍然很常见。

C++11 重用了关键字,如果有的话,很少有 C++ 程序员使用它的原始含义来进行类型推断。这在很大程度上是安全的,因为 C 中的“一切都是 int”规则已经在 C++98 中被删除;唯一中断的是auto T a,反正没人用。 (在他的papers on the history of the language 某处,Stroustrup 对此进行了讨论,但我现在找不到确切的参考资料。)

(*) B 中的字符串处理很有趣:您将使用int 的数组并在每个成员中打包多个字符。 B 实际上是BCPL,语法不同。

【讨论】:

  • 不,自 1999 年以来这不是合法的 C。没有像样的现代 C 编译器允许这样做。
  • @JensGustedt VS 并未声称提供现代 C 编译器。从表面上看,C 编译器的工作多年前就停止了。他们只提供它,以便人们可以继续编译遗留代码。 (当然,任何体面的现代 C 编译器都可以选择支持遗留代码。包括 K&R C 的选项。)
  • @JensGustedt:你确定吗? GCC 和 Clang 都在 C99 模式下发出警告,但除了 -Werror 之外,他们不认为这是一个错误。
  • @larsman,是的,在 6.7.2 中有一个明确的约束:每个声明的声明说明符中至少应给出一个类型说明符...
  • @JensGustedt - re 不,自 1999 年以来这不是合法的 C。没有像样的现代 C 编译器允许这样做。 第一个陈述是正确的;自 1999 年以来它是非法的。恕我直言,第二个陈述是不正确的。任何体面的现代 C 编译器都必须允许这一点。看看所有如果他们不允许的话就必须重写的遗留代码。我已经写了一个扩展此评论的答案。
【解决方案2】:

这既是对的答案,也是对的扩展评论

是的,auto a=1; 在 C1999(以及 C2011)中是非法的。仅仅因为这现在是非法的,并不意味着现代 C 编译器应该拒绝包含此类结构的代码。我的观点恰恰相反,一个体面的现代 C 编译器仍然必须允许这样做。

在针对 1999 或 2011 版本的标准编译问题中的示例代码时,clang 和 gcc 都是这样做的。两个编译器都会发出诊断信息,然后继续执行,就好像令人反感的语句是 auto int a=1;

在我看来,这是一个体面的编译器应该做的。通过发出诊断,clang 和 gcc 完全符合标准。该标准没有说编译器必须拒绝非法代码。该标准只是说,如果翻译单元包含违反任何语法规则或约束 (5.1.1.3),则符合标准的实现必须产生至少一个诊断消息。

给定包含非法结构的代码,任何体面的编译器都会尝试理解非法代码,以便编译器可以找到代码中的下一个错误。在第一个错误处停止的编译器不是一个很好的编译器。有一种方法可以理解auto a=1,即应用“隐式int”规则。当编译器用于 C90 或 K&R 模式时,此规则强制编译器将 auto a=1 解释为如同 auto int a=1

大多数编译器通常会拒绝包含非法语法的代码(拒绝:拒绝生成目标文件或可执行文件)。这是编译器作者认为编译失败不是最佳选择的情况。最好的办法是发出诊断,修复代码,然后继续。有太多遗留代码充满了诸如register a=1; 之类的结构。编译器应该能够在 C99 或 C11 模式下编译该代码(当然需要诊断)。

【讨论】:

  • @larsmans -- 我可以看到你来自哪里。你想要一个-ffs-please-stop-allowing-constructs-from-some-previous-millennium 编译器选项,或者更简洁地说,一个-fstrict-compliance 选项。对编译器抱怨:“当我使用 -std=c11 时,我没想到古老的 K&R kruft 会编译。事实上,我希望它编译!”
  • 实际上不,我想必须打开 on 一个标志来获得最糟糕的编译。但是让-std=c99 更加严格将是朝着正确方向迈出的一步:)
  • 如果你使用gcc -g -O3 -std=c11 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition -Werror(这是我经常使用的,即使是关于 SO 问题的代码),那么你会非常接近你想要的。我希望 GCC 至少默认为-std=c99,最好是-std=c11(或者,-std=gnu11;他们更有可能这样做),但在那之前,……你可以调整这些选项; -pedantic-Wshadow-Wold-style-declaration 和其他一些可能很有用,但这是一个很好的初始选项集。
  • @DavidHammen:圈复杂度、项目经理和公司政策都不是语言的要素。
  • 在 GCC 中获得你想要的行为的标志是 -pedantic-errors
【解决方案3】:

auto 在 2011 标准之前的 CC++ 中具有含义。这意味着变量具有自动生命周期,即lifetime determined by the scope。这与例如 static 生命周期相反,其中变量“永远”持续,而不管范围如何。 auto 是默认生命周期,几乎从未明确说明。这就是为什么在C++ 中更改含义是安全的。

现在C,在99标准之前,如果不指定变量的类型,默认为int

因此,使用auto a = 1;,您正在声明(并定义)一个int 变量,其生命周期由范围决定。

(“生命周期”更恰当地称为“存储持续时间”,但我认为这可能不太清楚)。

【讨论】:

  • 好的,所以实际上在 C 中 auto a=1 是允许的,这意味着一个具有自动存储持续时间的 int 变量。
  • 正确地,“存储持续时间”采用值列表之一,“自动”、“静态”、“动态”、“线程”。 “生命周期”是对象的实际生命周期。因此该变量具有“自动”存储持续时间和“main 函数范围的持续时间”。
  • @Steve 是的,我并不是要暗示 autostatic 是仅有的两种可能性。我试图以一种针对提问者的方式来写我的答案,他对C++(和C)似乎很陌生,所以我稍微掩盖了细节。也许那是个坏主意;它们迟早需要被覆盖。
  • @BoBTFish:哦,我没有抱怨这个。我只是想扩展“生命周期”(一个持续时间)与“存储持续时间”之间的语义差异,“存储持续时间”可能更准确地称为“存储持续时间类别”。
  • 这个隐含的int 内容自 1999 年以来已从 C 中删除。
【解决方案4】:

在 C 和 C++ 的历史方言中,auto 是一个关键字,表示 a 具有自动存储功能。由于它只能应用于局部变量,默认情况下是自动的,所以没有人使用它;这就是为什么 C++ 现在改变了关键字的用途。

从历史上看,C 允许不带类型说明符的变量声明。类型默认为int。所以这个声明等价于

int a=1;

我认为这在现代 C 中已被弃用(并且可能被禁止);但是一些流行的编译器默认使用 C90(我认为确实允许),而且令人讨厌的是,只有在您特别要求时才启用警告。使用 GCC 编译并使用 -std=c99 指定 C99,或者使用 -Wall-Wimplicit-int 启用警告,会给出警告:

warning: type defaults to ‘int’ in declaration of ‘a’

【讨论】:

  • 自 1999 年以来确实在 C 中被禁止。
【解决方案5】:

在 C 中,auto 的含义与 register 在 C++11 中的含义相同:表示变量具有自动存储持续时间。

在 C99 之前的 C 中(微软的编译器不支持 C99 或 C11,尽管它可能支持其中的一部分),在许多情况下可以省略类型,默认为 int

它根本不采用初始化程序的类型。您只是碰巧选择了一个兼容的初始化程序。

【讨论】:

  • 在 C++11 中是否不推荐使用 register 关键字?
  • @sordid 是的。在 C++11 之前,autoregister 具有完全相同的含义(我之前评论说,在获取 register 限定变量的地址方面存在限制,但这对于 C++ 是不正确的)。 register,虽然已被弃用,但现在仍保留其旧含义。
  • @JensGustedt:答案并没有说是。它表示 C 中的 auto 与 C++ 中的 register 的含义相同(两者均表示自动存储持续时间,仅此而已)。
【解决方案6】:

Visual Studio 编译类型可在right click on file -> Properties -> C/C++ -> Advanced -> Compile As 获得。为了确保它被编译为 C force /TC 选项。那么在这种情况下,就是 larsmans 所说的(旧的 C auto 关键字)。它可能在你不知情的情况下被编译为 C++。

【讨论】:

    【解决方案7】:

    存储类定义了 C 程序中变量和/或函数的范围(可见性)和生命周期。

    在 C 程序中可以使用以下存储类

    auto
    register
    static
    extern
    

    auto 是所有局部变量的默认存储类。

    {
            int Count;
            auto int Month;
    }
    

    上面的例子定义了两个具有相同存储类的变量。 auto 只能在函数中使用,即局部变量。

    int 是下面代码中auto 的默认类型:

    auto Month;
    /* Equals to */
    int Month;
    

    以下代码也是合法的:

    /* Default-int */
    main()
    {
        reurn 0;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-11-29
      • 2011-06-25
      • 2019-07-23
      • 2015-01-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多