为什么在C语言中auto a=1;可以编译通过?

129

代码:

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

当文件扩展名为.c时,使用MS Visual Studio 2012编译器可以无错误编译。我一直认为如果使用.c扩展名,则应根据C语法而不是C++进行编译。此外,据我所知,自C++11以来,只有在C++中允许使用没有类型的auto,其中它表示类型是从初始化程序推断出来的。

那是否意味着我的编译器没有遵循C语言,还是代码实际上在C语言中是正确的?


8
无论是使用C ++模式(可能的)还是不变,微软仍然停留在上一千年。隐式int已于1999年从C标准中删除。 - Jens Gustedt
16
MSVC++只支持C89标准(以及来自C99的一些特性),它更像是一个C++编译器。 - ntoskrnl
3
使用/Wall选项会出现警告C4431,提示缺少类型说明符并且默认int不再受C语言支持(参见Jens的评论)。这有点矛盾,因为显然这个编译器确实支持它... - lee77
4
按照这个标准,GCC 4.7于2012年发布(以及之后的版本,我猜也是一样的——我手头没有那些版本)也被认为是“停留在上个世纪”。它可以在没有给出任何编译选项的情况下编译 OP 的代码,甚至不会有任何提示。 - user395760
3
@delnan,我至少认为OP已经切换了警告级别。很明显我错了。某种意义上来说,这是真的,因为gcc仍然停留在那里,因为他们仍然没有将C99(或其变体)作为默认值。即使没有标志,clang也会对此结构发出警告。 - Jens Gustedt
显示剩余5条评论
7个回答

241
auto是一个旧的C关键字,意思是“局部作用域”。auto aauto int a相同,并且因为在函数内声明变量时,局部作用域是默认的,所以在这个例子中它也与int a相同。
这个关键字实际上是从C的前身B中遗留下来的,在那里没有基本类型:一切都是int、指向int的指针、int数组。声明可以是autoextrn[sic]。C继承了“一切都是int”作为默认规则,因此您可以使用auto声明整数。
auto a;
extern b;
static c;

ISO C已经摒弃了这种用法,但许多编译器仍为了向后兼容而接受它。如果您对此感到陌生,那么您应该意识到在相关规则中也起到了作用。
unsigned d;  // actually unsigned int

这种用法在现代编程中仍然很常见。

C++11重复使用了这个关键字,但几乎没有C ++程序员在使用原始含义,而是用于类型推断。这基本上是安全的,因为从C中删除了“一切都是int”规则已经在C ++ 98中实现;唯一的问题是auto T a,但是任何人都不会使用它。(Stroustrup在他的papers on the history of the language中的某个地方对此发表了评论,但我现在找不到确切的参考文献。)

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


7
不,这不是自1999年以来合法的C语言。没有一个像样的现代C编译器允许这样做。 - Jens Gustedt
18
VS并不声称提供一款现代化的C编译器。从各方面来看,很多年前就停止了对C编译器的开发;他们只是提供它以便人们可以继续编译传统代码。(当然,任何一个好的现代C编译器都会有支持传统代码的选项,包括K&R C的选项。) - James Kanze
23
您确定吗?GCC和Clang在C99模式下都会发出警告,但除非使用“-Werror”选项,否则它们不会将其视为错误。 - Fred Foo
2
@larsman,是的,在6.7.2中有一个明确的限制:在每个声明中,声明说明符中必须至少给出一个类型说明符... - Jens Gustedt
41
@JensGustedt - re 不,自1999年以来这已经不是合法的C语言了。没有一个像样的现代C编译器允许这样做。第一个陈述是正确的;自1999年以来,这是非法的。在我看来,第二个声明是不正确的。任何像样的现代C编译器都必须允许这样做。看看所有必须重写的遗留代码。我已经写了一个答案来扩展这个评论。 - David Hammen
显示剩余20条评论

36

这既是一个答案,也是对“不,自1999年以来这不是合法的C。没有一个像样的现代C编译器允许这样做。”的延伸评论。

是的,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模式下编译该代码(当然还要发出诊断)。


1
@larsmans -- 我能理解你的想法。你想要一个“-ffs-please-stop-allowing-constructs-from-some-previous-millennium”编译器选项,或者更简洁地说,一个“-fstrict-compliance”选项。对编译器抱怨:“当我使用-std=c11时,我并不希望那些古老的K&R kruft被编译。事实上,我希望它不要被编译!” - David Hammen
1
实际上不是,我想要打开一个标志来编译最糟糕的代码。但是让“-std=c99”更严格是朝着正确方向迈出的一步 :) - Fred Foo
1
如果你使用 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和其他一些选项可能很有用,但这是一个很好的起点选项集。 - Jonathan Leffler
3
@DavidHammen: "复杂度、项目经理和公司政策都不是编程语言的组成部分。" - Jerry B
3
在GCC中,获取你想要的行为的标志是“-pedantic-errors”。 - τεκ
显示剩余5条评论

30

auto在2011年之前的CC++中有一个含义。它表示变量具有自动存储期,即由范围确定的存储期。这与例如static存储期相对,后者表示变量将“永久”存在,无论范围如何。 auto是默认存储期,几乎从不显式地拼写出来。这就是为什么在C++中更改其含义是安全的原因。

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

所以,使用auto a = 1;声明(和定义)了一个int变量,并且其存储期由范围确定。

(“存储期”更准确地称为“存储持续时间”,但我认为这可能不太清晰)。


好的,实际上在C语言中允许使用“auto a=1”语句,它表示一个自动存储期限的int变量。 - lee77
1
正确地说,“存储期”取值为“自动”,“静态”,“动态”,“线程”之一。“生命周期”是对象实际存在的时间。因此,该变量具有“自动”存储期和“main函数作用域的持续时间”生命周期。 - Steve Jessop
@Steve 是的,我并不是想暗示 autostatic 是仅有的两种可能性。我试图以针对提问者的方式来回答问题,他似乎对 C++(和 C)还很陌生,所以我略微忽略了一些细节。也许这是个坏主意;这些内容迟早需要被涵盖到。 - BoBTFish
1
@BoBTFish:哦,我不是在抱怨那个。我只是想进一步阐述“lifetime”和“存储期限”之间的语义差异,“lifetime”是一个持续时间,而“存储期限”可能更准确地被称为“存储期限类别”。 - Steve Jessop
自从1999年以来,C语言中的隐式int已经被移除。 - Jens Gustedt

9
在C语言和历史方言的C++中,auto是一个关键字,意味着a具有自动存储。由于它只能应用于默认为自动的本地变量,因此没有人使用它;这就是为什么C++现在重新定义了该关键字的原因。
在历史上,C允许没有类型说明符的变量声明;类型默认为int。因此,这个声明等同于:
int a=1;

我认为这在现代C语言中已经被废弃(并且可能是被禁止的);但一些流行的编译器默认使用C90(我认为是允许的),令人恼火的是,只有在你明确要求时才会启用警告。使用GCC编译并指定C99选项-std=c99或启用警告-Wall-Wimplicit-int时,会给出一个警告:

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

4
自1999年起,在C语言中的确是被禁止的。 - Jens Gustedt

6
在C语言中,auto的意思和在C++11中register是一样的:它表示变量具有自动存储期。在C99之前的C语言(Microsoft的编译器不支持C99或C11,尽管它可能支持其中的一些部分),在许多情况下可以省略类型,其中默认为int。它根本不从初始化程序获取类型。你只是碰巧选择了一个兼容的初始化程序。

1
在C++11中,register关键字不是被弃用了吗? - sordid
@sordid 是的,它是。在 C++11 之前,autoregister 具有完全相同的含义(我之前评论过一个 register -qualified 变量地址限制的问题,但这对于 C++ 是不正确的)。虽然 register 已被弃用,但现在仍保留其旧的含义。 - user743382
5
答案并没有说它们是一样的,它说在C语言中 auto 和在C++中的 register 意思相同,这是正确的(两者都表示自动存储期限,并且没有其他含义)。 - Mike Seymour

4

存储类定义了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;
}

4

Visual Studio编译类型可在右键单击文件 -> 属性 -> C/C++ -> 高级 -> 编译为中找到。为确保强制编译为C,请使用/TC选项。在这种情况下,它是larsmans所说的(旧的C auto关键字)。否则可能会在您不知道的情况下编译为C++。


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