混淆'sizeof'运算符的结果

时间:2013-10-31 15:00:42

标签: c gcc declaration sizeof

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

 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不同?

5 个答案:

答案 0 :(得分: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没有多大意义。

答案 1 :(得分: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 type

我们可以看到 C99 removed the implicit int假设:

  

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

如果我们查看draft C99 standard 转发部分, 5 包含以下内容:

  

[...]上一版的主要变化包括:

并有以下子弹:

  

- 删除隐式int

更新

那么为什么sizeof不喜欢存储类说明符,如 static auto ,但类型限定符是可以的 const volatile ,行为似乎与声明的工作方式不一致,隐式int 假设是否仍然有用?

如果我们在草案标准部分sizeof中查看6.5.3的语法,则如下所示:

sizeof unary-expression
sizeof ( type-name )

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

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

一元表达式()合作,正如我之前解释的那样here

答案 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)都会发出警告。

答案 3 :(得分:1)

externstaticautoregister被称为存储类说明符,而const,{{ 1}},restrict称为 type-qualifier

对于类型限定符,在没有类型说明符的情况下使用时,在C89中隐式指定volatile

  

C89§3.5.2类型说明符

     

intintsigned或没有类型说明符

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

  

C99§6.7.2类型说明符

     

signed intintsigned

答案 4 :(得分:0)

您的声明都无效,因此结果基本上无关紧要。

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

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