7

我最近尝试了这段代码,有点困惑。请参阅以下声明:

 static   st;
 auto     au;
 register reg;
 volatile vl;
 const    cn;

它们都在分配 4 个字节的内存(在 32 位 GCC 上)。但是当我尝试打印(使用printf函数)它们的大小时,它们不工作并给出错误。

  sizeof(const)      // worked and printed 4
  sizeof(volatile)   // worked and printed 4

  sizeof(auto)       // error: expected expression before ‘auto’
  sizeof(static)     // error: expected expression before ‘static’
  sizeof(register)   // error: expected expression before ‘register’

我的疑问是auto, static, register 关键字也分配了 4 个字节的内存(在 32 位拱形上)。

但是为什么这些给出的错误与constand不同volatile

4

5 回答 5

17

在 1999 标准之前的 C 中,未指定的类型int在许多情况下默认为。

C99 放弃了该规则,并且省略类型现在是非法的(严格来说,这是一个约束违反,需要诊断——这可能是一个非致命警告)。无论如何,省略int类型一直是个坏主意。(它可以追溯到 C 的前身语言 BCPL 和 B,它们在很大程度上是无类型的。)

static   st;
auto     au;
register reg;
volatile vl;
const    cn;

这些声明在 C90 中都是合法的(并且所有变量都是 type int),但在 C99 中它们是无效的。

sizeof(const)      
sizeof(volatile)

令我惊讶的是,这些实际上在 C90 中是合法的(但在 C99 中不合法)。constorvolatile本身是一个类型名称,分别相当于const intvolatile int。语法上,constvolatile类型限定符

sizeof(auto)
sizeof(static)
sizeof(register)

区别在于:

const int x = 42;

定义x为 type 的对象const int,而这个:

static int x = 42;

定义x为类型的静态对象intstatic不是类型的一部分)。

这些都是语法错误,因为auto,staticregister不是类型名称。这些关键字是存储类说明符

这就解释了为什么前两个sizeof表达式似乎有效,而其他表达式则无效。但这并不是特别有用,因为如果您指定类型int(您总是应该这样做),那么它是否sizeof(const)有效(在 C90 中,而不是在 C99 中)并不重要。

底线是您应该始终在任何声明中指定类型。尽管您可以合法地编写sizeof (const int),但它可以保证与 相同sizeof (int),因此在这种情况下使用没有太大const意义。

于 2013-10-31T15:15:06.977 回答
10

C99之前,如果您没有指定类型,那么int将被暗示,这就是您的代码中发生的情况。即使在C99模式下,它看起来也像在实践中gccclang只会产生警告。在这种情况下,编译器警告是您的朋友,我在以下情况下尝试过clang -Wall

printf( "%zu\n", sizeof(const) ) ;  

它警告我:

warning: type specifier missing, defaults to 'int' [-Wimplicit-int]

这里的所有声明:

static   st;
auto     au;
register reg;
volatile vl;
const    cn;

也有一个隐含的int 类型

我们可以看到C99 移除了隐含的 int假设:

缺少类型说明符的声明不再隐式假定为 int。C 标准委员会认为编译器诊断类型说明符的疏忽遗漏比静默处理依赖于隐式 int 的遗留代码更有价值。实际上,编译器可能会显示警告,然后假定为 int 并继续翻译程序。

如果我们看一下C99 标准草案, 前向部分第5段包括以下内容:

[...]与上一版本相比的主要变化包括:

并具有以下项目符号:

— 删除隐式 int

更新

那么为什么不sizeof喜欢staticauto之类的存储类说明符,但可以使用constvolatile之类的类型限定符,这种行为似乎与声明的工作方式不一致,隐式 int假设是否仍然有效?

好吧,如果我们看一下标准草案部分sizeof中的语法,它如下所示:6.5.3

sizeof unary-expression
sizeof ( type-name )

因此,类型限定符存储类说明符都不是表达式,但类型限定符type-name,如果我们查看部分type-name6.7.6的语法如下:

type-name:
  specifier-qualifier-list abstract-declaratoropt

6.7.2.1为我们提供了specifier-qualifier-list的语法,如下所示:

specifier-qualifier-list:
  type-specifier specifier-qualifier-listopt
  type-qualifier specifier-qualifier-listopt      <- Bingo allows type qualifier

所以我们可以看到即使类型被显式指定为intsizeof ,也只是不接受存储类说明符,所以即使以下是错误的:

printf( "%zu\n", sizeof(static int) ) ;

clang告诉我们:

error: expected expression
   printf( "%zu\n", sizeof(static int) ) ;
                           ^

而且我们可以进一步看到,如果没有,类型名称将无法sizeof使用()

printf( "%zu\n", sizeof  int ) ;

产生错误:

error: expected expression

但正如我之前在这里解释的那样,一元表达式可以使用。()

于 2013-10-31T15:03:31.520 回答
1

, auto,关键字不标识任何类型,但static修改该类型变量的存储或访问方式。register

所以:

sizeof(auto)       // error: expected expression before ‘auto’
sizeof(static)     // error: expected expression before ‘static’
sizeof(register)   // error: expected expression before ‘register’

没有意义,因为您没有要求任何类型的大小。反而:

sizeof(const)      // worked and printed 4
sizeof(volatile)   // worked and printed 4

这些标识类型:volatile intconst int。所以你可以sizeof在他们身上使用。

请注意,当您声明变量时,编译器假定它们是int它们的基础类型。如果您依赖这种行为,大多数编译器(GCC、Clang)都会发出警告。

于 2013-10-31T15:10:06.147 回答
1

extern, static, auto,register称为storage-class-specifier,而const, restrict,volatile称为type-qualifier

对于类型限定符,当不使用type-specifier时,int在 C89 中隐式指定。

C89 §3.5.2 类型说明符

int, signed, signed int, 或没有类型说明符

列出的这些类型彼此相同。虽然在同一部分的 C99 中没有删除类型说明符:

C99 §6.7.2 类型说明符

int, signed, 或signed int

于 2013-10-31T15:15:14.430 回答
0

您的声明都是无效的,因此结果在很大程度上是无关紧要的。

变量/对象的大小取决于其数据类型,例如intfloat。您尝试的关键字修改了编译器处理变量/对象的方式,但它们不会改变或规定其类型(因此它们与其大小无关)。

对于您的constvolatile声明,编译器可能默认为 type int(但这不是您应该依赖的行为)。

于 2013-10-31T15:17:40.537 回答