sizeof运算符的结果令人困惑

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 位架构上)?但为什么它们与 constvolatile 不同,会产生错误呢?

类型默认为int。当您声明“unsigned a;”时,发生的事情是相同的。 - StoryTeller - Unslander Monica
标题是“C”,标签是C和C ++。 C语言有auto吗?请挑选一种语言,而不是乱试。 - crashmstr
@texasbruce 我期望的结果是4个字节。例如,register关键字存储在CPU寄存器中。它可以直接给出cpu register size作为结果。 - gangadhars
4
是的,C语言中有auto关键字。它表示自动存储。 - peppe
1
经典的C语言(甚至B语言)带来了臭名昭著的“未声明类型即为int”的特性,这就是你在这里看到的,所以你所有的东西都是int类型。 - PlasmaHH
显示剩余15条评论
5个回答

17

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

C99取消了该规则,省略类型现在是非法的(严格来说,这是一种“限制违反”,需要诊断 - 这可能是一种非致命警告)。无论如何,省略 int 类型总是一个坏主意。(它源自于C语言的前身BCPL和B,那里基本上没有类型。)

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

这些声明在C90中都是合法的(所有变量都是int类型),但在C99中无效。

sizeof(const)      
sizeof(volatile)

有些令人惊讶的是,在 C90 中这些实际上是合法的(但在 C99 中不是)。constvolatile 单独使用时是类型名称,分别相当于 const intvolatile int。从语法上讲,constvolatile类型限定符
sizeof(auto)
sizeof(static)
sizeof(register)

区别在于这个:
const int x = 42;

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

static int x = 42;

定义x为类型为int静态对象(其中static不是类型的一部分)。

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

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

最重要的是,在任何声明中都应该始终指定类型。虽然您可以合法地编写sizeof(const int),但它保证与sizeof(int)相同,因此在该上下文中使用const并没有太大意义。


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标准Forward 章节第 5 段,则包括以下内容:

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

并列出以下要点:

— 移除隐式int

更新

那么为什么sizeof不喜欢存储类别说明符例如staticauto,但是对类型修饰符例如constvolatile没问题呢?这种行为似乎与声明的工作方式不一致,隐式int假设仍然有效吗?

如果我们查看标准草案中sizeof的语法,在 draft standard 章节 6.5.3 中如下:

sizeof unary-expression
sizeof ( type-name )

因此,一个类型限定符和一个存储类说明符都不是一个表达式,但是如果我们看一下第6.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

因此,我们可以看出sizeof不接受存储类修饰符,即使类型明确指定为int,所以以下内容也是错误的:

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

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


没有任何警告和错误。使用GCC编译器,结果为4。 - gangadhars
1
请启用-Wall。GCC会警告您。 - peppe
@SGG 我刚刚尝试使用“-Wall”,它给了我一个类似的警告。 - Shafik Yaghmour
答案明确简洁...只需尝试,你就会看到 :) - Rerito
对于 C,你会收到一个警告,而对于 *C++*,你会收到一个错误。 - Shafik Yaghmour
显示剩余2条评论

1
autostaticregister 关键字不标识任何类型,但是会修改对该类型变量的存储或访问方式。

因此:

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)会发出警告,如果您依赖于此行为。

1

externstaticautoregister 被称为存储类说明符,而 constrestrictvolatile 则被称为类型限定符

对于类型限定符,在没有类型说明符的情况下,在 C89 中隐式指定为 int

C89 §3.5.2 类型说明符

intsignedsigned int 或没有类型说明符

这些列出的类型彼此相同。而在 C99 的同一章节中,没有类型说明符已被删除:

C99 §6.7.2 类型说明符

intsignedsigned int


0
你的声明全部无效,所以结果大部分都不相关。
一个变量/对象的大小取决于它的数据类型,例如int或float。你尝试使用的关键字修改编译器处理变量/对象的方式,但它们不会改变或指示其类型(因此它们对其大小没有影响)。
对于你的const和volatile声明,编译器可能默认为int类型(但这不是你应该依赖的行为)。

1
这不是C++。在C语言中,auto表示自动存储。 - Arpit
我的错误。谢谢Arpit。我会把它删除的。 - Peter Bloomfield

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