为什么auto a = 1;用C编译?


125

代码:

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

文件扩展名为.c时,MS Visual Studio 2012编译器将编译该文件而不会出现错误。我一直认为,当您使用.c扩展名时,应根据C语法而不是C ++进行编译。而且,据我所知,自C ++ 11起,只有在C ++中允许不带类型的auto ,这意味着从初始化程序推导出类型。

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


8
您使用C ++模式(可能)进行编译,或者MS仍停留在最后一个千年中。隐式int已于1999
。– Jens Gustedt

16
@JensGustedt MSVC ++仅支持C89(以及C99的一些功能)。更多的是C ++编译器。
ntoskrnl 2014年

3
@Brandin:使用/ Wall选项,它将发出警告C4431,表示缺少类型说明符,并且C中不再支持默认的int(请参阅Jens的评论)。这有点矛盾,因为显然此编译器支持它……
lee77年

4
@JensGustedt按照这种方法,2012年发布的GCC 4.7(以及以后的版本,我怀疑-我手边没有这些)也“塞入了上一个千年”。当没有给出任何标志时,它甚至在没有通知的情况下编译OP的代码。

3
@delnan,我至少是假设OP已打开警告级别。我显然是错的。从某种意义上说,这确实是正确的,因为它们仍然没有默认的C99(或变体),因此gcc仍然停留在该位置。clang警告该构造,即使没有标志。
延斯·古斯特

Answers:


240

auto是一个旧的C关键字,表示“本地范围”。auto a与相同auto int a,并且因为局部作用域是函数内部声明的变量的默认范围,所以它也与int a此示例相同。

这个关键字实际上是C的前身B(没有基类型)的剩余部分:所有内容都是int,指向。(*)的int数组的指针,int声明为autoor或extrn[sic]。C继承了“ everything is int”作为默认规则,因此您可以使用

auto a;
extern b;
static c;

ISO C摆脱了这一点,但是许多编译器仍然接受它以实现向后兼容性。如果您不熟悉,那么您应该意识到相关规则正在实施中

unsigned d;  // actually unsigned int

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

C ++ 11重用了该关键字以进行类型推断,该关键字很少(如果有的话,C ++程序员使用的是原始含义)。这是最安全的,因为int在C ++ 98中已经删除了C 的“一切都是”规则。唯一中断的是auto T a,无论如何都没有人使用过。(在他关于语言历史的论文中的某个地方,Stroustrup对此发表了评论,但我现在找不到确切的参考。)

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


7
不,自1999年以来,这不是合法的C语言。没有像样的现代C编译器允许这样做。
延斯·古斯特

18
@JensGustedt VS并未声称提供现代C编译器。从各个方面看,C编译器的工作在很多年前就停止了。他们只提供它,以便人们可以继续编译遗留代码。(当然,任何体面的现代C编译器都将具有支持旧版代码的选项。包括K&R C的选项。)
James Kanze 2014年

23
@JensGustedt:确定吗?GCC和Clang都在C99模式下对此进行了警告,但除之外,他们都不认为这是错误-Werror
福雷德(Fred Foo)

2
@larsman,是的,在6.7.2中有一个明确的约束:每个声明中的声明说明符中至少应有一个类型说明符……
Jens Gustedt 2014年

40
@JensGustedt-re 不,自1999年以来这不是合法的C语言。没有像样的现代C编译器允许这样做。第一个陈述是正确的;自1999年以来就是非法的。恕我直言,第二个说法不正确。任何体面的现代C编译器都必须允许这样做。查看所有遗留代码,如果他们不允许的话,必须重写它们。我写了一个答案,扩展了此评论。
大卫·哈门

35

这既是答案,又是对No的扩展注释。自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”规则。该规则将强制编译器进行解释,auto a=1就像auto int a=1在C90或K&R模式下使用编译器时一样。

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


1
@larsmans-我可以看到你来自哪里。您需要一个-ffs-please-stop-allowing-constructs-from-some-previous-millennium编译器选项,或更简洁地说是一个-fstrict-compliance选项。抱怨编译器:“当我使用-std = c11时,我没想到可以编译古老的K&R kruft。实际上,我希望它不被编译!”
大卫·哈门

1
其实也没什么,我想必须把一个标志,以获得最糟糕的克鲁夫特编译。但是-std=c99更加严格将是朝正确方向迈出的一步:)
弗雷德·福

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和其他一些人可能是有用的,但是这是开始设置选项好。
乔纳森·勒夫勒

3
@DavidHammen:圈复杂性,项目经理或公司政策都不是语言的要素。
杰里B

3
让你在GCC想要的行为的标志-pedantic-errors
τεκ

29

auto具有2011年标准之内CC++之前的含义。这意味着变量具有自动生存期,也就是由范围决定的生存期。这与例如static生命周期相反,在生命周期中,变量会“永远”持续,而不管作用域如何。auto是默认生存期,几乎永远不会明确指出。这就是为什么可以安全地更改中的含义的原因C++

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

因此,auto a = 1;您要声明(并定义)一个int变量,其生存期由范围决定。

(“寿命”更恰当地称为“存储期限”,但我认为可能不太清楚)。


好的,因此C中实际上允许auto a = 1,这意味着具有自动存储持续时间的int变量。
lee77年

1
适当地,“存储持续时间”采用值列表之一,即“自动”,“静态”,“动态”,“线程”。“寿命”是对象的实际寿命。因此,变量的存储持续时间为“自动”,生存期为“ main功能范围的持续时间”。
史蒂夫·杰索普

@Steve是的,我不是故意暗示auto,并static是只有两种可能性。我试图以针对发问者的方式来写我的答案,发问者对于C++(和C)似乎还很陌生,所以我稍微详细介绍了一下。也许那是个坏主意;他们迟早需要掩盖。
BoBTFish 2014年

1
@BoBTFish:哦,我不是在抱怨。我只是想扩展“生存期”(即持续时间)与“存储持续时间”(可能更准确地称为“存储持续时间类别”)之间的语义差异。
史蒂夫·杰索普

int自1999
该隐

8

在C和C ++的历史方言中,autoa具有自动存储功能的关键字。由于只能将其应用于默认情况下为自动的局部变量,因此没有人使用它。这就是C ++现在重新使用关键字的原因。

从历史上看,C允许没有类型说明符的变量声明。类型默认为int。所以这个声明相当于

int a=1;

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

warning: type defaults to int in declaration of a


5

在C中,auto意味着register在C ++ 11中做同样的事情:这意味着变量具有自动存储持续时间。

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

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


1
在C ++ 11中是否不推荐使用register关键字?
2014年

@sordid是的,是的。在C ++ 11之前,auto并且register具有完全相同的含义(我之前评论说,使用register-qualified变量的地址有限制,但是对于C ++是不正确的)。register虽然已弃用,但现在仍保留其旧含义。

5
@JensGustedt:答案没有说是。它说auto在C中的含义与register在C ++中的含义相同(均表示自动存储持续时间,而没有其他含义)。
2014年

3

可以使用Visual Studio编译类型right click on file -> Properties -> C/C++ -> Advanced -> Compile As。为了确保将其编译为C force /TC选项,然后在这种情况下这就是larsmans所说的(旧的C auto关键字)。可能会在您不知情的情况下将其编译为C ++。


3

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

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

auto
register
static
extern

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

{
        int Count;
        auto int Month;
}

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

intauto以下代码中的默认类型:

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

下面的代码也是合法的:

/* Default-int */
main()
{
    reurn 0;
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.