每个变量在 C 中都有一个存储 class 吗?

Does every variable have a storage class in C?

天真地认为它有,因为通常在未提供存储 class 关键字时假定 auto

不过,对于文件范围的变量,在它们前面放置 auto 会产生错误。

#include <stdio.h>

auto int x;

int main(void){
  x = 7;
  printf("x = %d", x);
}

Clang 抱怨:

3:10: error: illegal storage class on file-scoped variable
auto int x;

在没有任何存储 class 关键字的情况下声明 x 并编译:

#include <stdio.h>

int x;

int main(void){
  x = 7;
  printf("x = %d", x);
}

现在我想知道在上面的例子中 class x 有什么存储空间?它有名字吗?

来自 C 标准 § 6.2.4 第 3 段:

An object whose identifier is declared without the storage-class specifier _Thread_local, and either with external or internal linkage or with the storage-class specifier static, has static storage duration. Its lifetime is the entire execution of the program and its stored value is initialized only once, prior to program startup.

强调我的。反向引用 § 6.2.2 第 5 段:

If the declaration of an identifier for a function has no storage-class specifier, its linkage is determined exactly as if it were declared with the storage-class specifier extern. If the declaration of an identifier for an object has file scope and no storage-class specifier, its linkage is external.

再次强调我的。

因此,全局变量默认具有静态存储期限。即使没有标准来保证这一点,它也是唯一对全局变量有意义的存储持续时间类型。

Does every variable have a storage class in C?

是的,尽管标准实际上为此使用了术语 "storage duration"。这是同一件事,而且标准对关键字 autostatic 等使用术语 "storage class specifier" 也有些不一致。

Naively one could think it has, because usually auto is assumed when a storage class keyword is not supplied.

没有。绝对不。对于函数和在文件范围内声明的任何内容,默认值为 extern。只有在块范围内声明的对象的标识符默认为 auto.

Still, for file-scoped variables putting an auto in front of them gives an error.

应该的。该标准明确规定

The storage-class specifiers auto and register shall not appear in the declaration specifiers in an external declaration.

[C11, paragraph 6.9/2]

Declaring x without any storage class keyword and it compiles [....]

当然可以。

Now I wonder what storage class x has in the example above? Does it have a name?

它的存储class是关键字extern对应的。正如我已经说过的,这是文件范围声明的默认设置。但是,尽管该标准使用了术语 "storage class specifier",但它并没有将 "storage class" 作为一个独立的概念使用。它说话而不是存储持续时间。所有具有外部或内部链接的变量都具有静态存储持续时间,这意味着它们在程序的整个生命周期中都存在。

关键字 autostaticexternregister_Thread_local 在标准中称为 storage class 说明符,但是"objects"(这是我们通常所说的"variables" 的标准术语)没有存储classes。相反,它们具有 linkage(外部、内部、none)和 storage duration(静态、自动、线程)。此外,对象的任何声明可能是也可能不是定义。存储 class 说明符,以及声明对象的范围,以及它是否具有初始化程序(int fooint foo = 3)一起控制这些属性。使用 table:

可以最简单地展示它是如何工作的
sc-specifier scope initialized   linkage    storage duration    is a definition
------------ ----- -----------   -------    ----------------    ---------------
auto         file  no            [constraint violation]
auto         file  yes           [constraint violation]
auto         block no            none       automatic           yes
auto         block yes           none       automatic           yes

none         file  no            external   static              yes
none         file  yes           external   static              yes
none         block no            none       automatic           yes
none         block yes           none       automatic           yes

static       file  no            internal   static              yes
static       file  yes           internal   static              yes
static       block no            none       static              yes
static       block yes           none       static              yes

extern       file  no            external   static              no
extern       file  yes           external   static              yes
extern       block no            external   static              no
extern       block yes           external   static              yes

术语 "storage class specifier" 有意不同于术语 "storage duration" 和 "linkage" 以提醒您说明符不会让您独立控制存储期限和链接。

该语言无法让您独立控制存储持续时间、链接和定义,因为不可用的组合没有意义。自动存储持续时间仅对在块范围内声明的变量有意义,而不是定义仅对具有外部链接的变量有意义(因为只有它们可以在另一个文件中定义),等等。

我将 register_Thread_local 排除在 table 之外,因为它们很特殊。 register 就像 auto 一样,除了它还意味着您不能获取对象的地址。 _Thread_local 使变量的存储期限为 "thread" 并且不改变链接;它可以单独使用,也可以与 externstatic 一起使用,但将其与 "auto" 结合使用是违反约束的。如果您单独在块范围内使用它,我不确定它会做什么。