以下是在dev c ++ windows中编译的代码:
#include <stdio.h>
int main() {
int x = 5;
printf("%d and ", sizeof(x++)); // note 1
printf("%d\n", x); // note 2
return 0;
}
执行注1 后,我希望x
为6。但是,输出是:
4 and 5
任何人都可以解释为什么x
在注1 之后不会增加?
答案 0 :(得分:515)
来自C99 Standard(强调是我的)
6.5.3.4/2
sizeof运算符产生其操作数的大小(以字节为单位),该操作数可以是表达式或类型的带括号的名称。大小由操作数的类型确定。结果是整数。如果操作数的类型是可变长度数组类型,则计算操作数;否则,不评估操作数,结果是整数常量。
答案 1 :(得分:181)
sizeof
是 编译时运算符 ,因此在编译sizeof
时,其操作数将被结果值替换。 操作数未评估(除非是可变长度数组);只有结果的类型才重要。
short func(short x) { // this function never gets called !!
printf("%d", x); // this print never happens
return x;
}
int main() {
printf("%d", sizeof(func(3))); // all that matters to sizeof is the
// return type of the function.
return 0;
}
输出:
2
因为short
在我的机器上占用2个字节。
将函数的返回类型更改为double
:
double func(short x) {
// rest all same
将8
作为输出。
答案 2 :(得分:47)
sizeof(foo)
尝试在编译时很难发现表达式的大小:
6.5.3.4:
sizeof运算符产生其操作数的大小(以字节为单位),可以是 表达式或类型的括号名称。大小由类型确定 操作数。结果是整数。如果操作数的类型是可变长度数组 type,操作数被评估;否则,不评估操作数,结果是 整数常数。
简而言之:可变长度数组,在运行时运行。 (注意:Variable Length Arrays是一个特定功能 - 不是使用malloc(3)
分配的数组。)否则,只计算表达式的类型,并在编译时计算。< / p>
答案 3 :(得分:33)
sizeof
是一个编译时内置运算符,不是一个函数。在没有括号的情况下使用它会变得非常清楚:
(sizeof x) //this also works
答案 4 :(得分:19)
注意
这个答案是从副本中合并而来,这解释了迟到的日期。
原始
除了variable length arrays sizeof 之外,它不会评估其参数。我们可以从草案C99标准部分6.5.3.4
sizeof运算符段 2 中看到:
sizeof运算符产生其操作数的大小(以字节为单位),可以是 表达式或类型的括号名称。大小由类型确定 操作数。结果是整数。 如果操作数的类型是可变长度数组 type,操作数被评估;否则,不评估操作数,结果是 整数常量。
评论(现已删除)询问是否会在运行时评估此类内容:
sizeof( char[x++] ) ;
而且确实如此,这样的事情也可行( See them both live ):
sizeof( char[func()] ) ;
因为它们都是可变长度数组。虽然,我认为其中任何一个都没有太多实际用途。
请注意,draft C99 standard部分6.7.5.2
数组声明符第4段中涵盖了可变长度数组:
[...]如果size是一个整型常量表达式,并且元素类型具有已知的常量大小,则数组类型不是可变长度数组类型; 否则,数组类型是可变长度数组类型。
更新
在C11中,VLA案例的答案发生了变化,在某些情况下,未指定是否评估了大小表达式。来自6.7.6.2
数组声明符的部分,其中包含:
[...] size表达式是sizeof的操作数的一部分 运算符和更改size表达式的值不会 影响操作员的结果,是否未指定 评估大小表达式。
例如在这种情况下( see it live ):
sizeof( int (*)[x++] )
答案 5 :(得分:10)
编译期间不能执行。因此++i
/ i++
不会发生。同样sizeof(foo())
将不执行该函数,但返回正确的类型。
答案 6 :(得分:10)
由于未评估sizeof
运算符的操作数,您可以执行以下操作:
int f(); //no definition, which means we cannot call it
int main(void) {
printf("%d", sizeof(f()) ); //no linker error
return 0;
}
也就是说,如果仅在f
中使用它,则不需要定义函数sizeof
。这种技术主要用于C ++模板元编程,因为即使在C ++中,也不会评估sizeof
的操作数。
为什么这样做?它起作用是因为sizeof
运算符不对值进行操作,而是对表达式的类型进行操作。因此,当您编写sizeof(f())
时,它会对表达式f()
的类型进行操作,而这只是函数f
的返回类型。返回类型总是相同的,无论函数实际执行时返回的值是什么值。
在C ++中,你甚至可以这样:
struct A
{
A(); //no definition, which means we cannot create instance!
int f(); //no definition, which means we cannot call it
};
int main() {
std::cout << sizeof(A().f())<< std::endl;
return 0;
}
然而看起来,在sizeof
中,我首先通过编写A
创建A()
的实例,然后在实例上调用函数f
,通过写A().f()
,但没有发生这样的事情。
这是另一个解释sizeof
的其他有趣属性的主题:
答案 7 :(得分:0)
sizeof()
运算符仅给出数据类型的大小,它不评估内部元素。
答案 8 :(得分:0)
sizeof
在编译时运行,但是x++
仅在运行时可以求值。为了解决这个问题,C ++标准规定sizeof
的操作数不进行评估(VLA除外)。 C标准说:
如果[
sizeof
的操作数的类型是可变长度数组类型,则对操作数求值;否则,不对操作数求值,结果为整数常量。
答案 9 :(得分:0)
这里的这一行:
Oracle (any version) org.hibernate.dialect.OracleDialect
Oracle9i org.hibernate.dialect.Oracle9iDialect
Oracle10g org.hibernate.dialect.Oracle10gDialect
导致UB。 printf("%d and ", sizeof(x++)); // note 1
需要类型 %d
而不是 int
。获得 UB 后,行为未定义,包括写入标准输出的字节。
如果您将 size_t
替换为 %d
或将值强制转换为 %zu
来解决该问题,但不能同时使用这两种方法,您仍然不会增加 int
,但这是一个不同的问题,应该在不同的问题中提出。