我最近尝试过这段代码并且有点困惑。请参阅以下声明:
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位拱门上)。
但为什么这些错误与const
和volatile
不同?
答案 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中)。 const
或volatile
本身就是一个类型名称,分别相当于const int
和volatile int
。从语法上讲,const
和volatile
是类型限定符。
sizeof(auto)
sizeof(static)
sizeof(register)
区别在于:
const int x = 42;
将x
定义为const int
类型的对象,而这是:
static int x = 42;
将x
定义为int
类型的静态对象(static
不属于该类型)。
这些都是语法错误,因为auto
,static
和register
不是类型名称。这些关键字是存储类说明符。
这解释了为什么前两个sizeof
表达式似乎有效,而其他表达式则无效。但是这一点并不是特别有用,因为如果你指定类型int
(你总是应该这样),那么sizeof(const)
恰好是有效的(在C90中,而不是在C99中)并不重要。
最重要的是,您应该始终在任何声明中指定类型。虽然您可以合法地写sizeof (const int)
,但它保证与sizeof (int)
相同,因此在该上下文中使用const
没有多大意义。
答案 1 :(得分:10)
在 C99 之前,如果您未指定类型,那么 int 将隐含在您的代码中发生的事情。在实践中看起来即使在 C99 模式gcc
和clang
也只会产生警告。这是编译器警告是你的朋友的情况,我在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)
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 int
和const int
。所以你可以对它们使用sizeof
。
请注意,当您声明变量时,编译器会假定int
为其基础类型。如果你依赖这种行为,大多数编译器(GCC,Clang)都会发出警告。
答案 3 :(得分:1)
extern
,static
,auto
,register
被称为存储类说明符,而const
,{{ 1}},restrict
称为 type-qualifier 。
对于类型限定符,在没有类型说明符的情况下使用时,在C89中隐式指定volatile
。
C89§3.5.2类型说明符
int
,int
,signed
或没有类型说明符
列出的这些类型彼此相同。虽然在同一部分的C99中删除了没有类型说明符:
C99§6.7.2类型说明符
signed int
,int
或signed
答案 4 :(得分:0)
您的声明都无效,因此结果基本上无关紧要。
变量/对象的大小取决于其数据类型,例如int
或float
。您尝试的关键字会修改编译器处理变量/对象的方式,但它们不会改变或指示其类型(因此它们与其大小无关)。
对于const
和volatile
声明,编译器可能默认为int
类型(但这不是您应该依赖的行为)。