为什么sizeof(x++)不增加x?

下面是在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;
}

我期望x在执行注1后为6。然而,输出结果是:

4 and 5

谁能解释一下为什么x注1之后不增加?

33890 次浏览

sizeof是一个< em >编译时操作符< / em >,所以在编译时sizeof及其操作数将被结果值替换。的操作数为not evaluate (除非它是一个变长数组);只有结果的type才重要。

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

as short在我的机器上占用2个字节。

将函数的返回类型更改为double:

double func(short x) {
// rest all same

将给出8作为输出。

C99标准(重点是我的)

6.5.3.4/2

sizeof操作符产生其操作数的大小(以字节为单位),该操作数可以是表达式或带括号的类型名。大小由操作数的类型决定。结果是一个整数。如果操作数的类型是变长数组类型,则计算该操作数;否则,不计算操作数,结果是一个整数常量。

sizeof(foo)非常努力地在编译时发现表达式的大小:

6.5.3.4:

sizeof操作符产生其操作数的大小(以字节为单位),该操作数可以是一个 表达式或类型的圆括号名称。尺寸是由类型决定的 操作数。结果是一个整数。如果操作数的类型是可变长度数组 类型,则对操作数求值;否则,操作数不求值,结果为 整数常数。< / p >

简而言之:可变长度数组,在运行时运行。(注意:可变长度数组是一个特定的特性——不是用malloc(3)分配的数组。)否则,只计算表达式的类型,并且在编译时计算。

sizeof是一个编译时内置操作符,是一个函数。在不带括号的情况下,这一点变得非常明显:

(sizeof x)  //this also works

在编译期间不能执行。所以++i/i++不会发生。而且sizeof(foo())不会执行函数,而是返回正确的类型。

由于sizeof操作符的操作数没有被求值,你可以这样做:

int f(); //no definition, which means we cannot call it


int main(void) {
printf("%d", sizeof(f()) );  //no linker error
return 0;
}

在线演示:http://ideone.com/S8e2Y

也就是说,如果函数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的实例,然后通过编写A().f()调用实例上的f函数,但没有发生这样的事情。

演示:http://ideone.com/egPMi

下面是另一个主题,它解释了sizeof的其他一些有趣的属性:

sizeof()操作符只给出数据类型的大小,它不计算内部元素。

请注意

这个答案是从一个副本合并而来的,所以日期晚了。

原始

除了可变长度数组运算符不计算它的实参。我们可以从C99标准草案6.5.3.4 sizeof操作符段落2中看到这一点,该段落说:

sizeof操作符产生其操作数的大小(以字节为单位),该操作数可以是一个 表达式或类型的圆括号名称。尺寸是由类型决定的 操作数。结果是一个整数。如果操作数的类型是一个变长数组 类型,则对操作数求值;否则,操作数不求值,结果为 整数常数。< /强> < / p >

一个注释(现在删除)询问这样的东西是否会在运行时求值:

sizeof( char[x++]  ) ;

而且确实如此,类似这样的东西也可以工作(看到他们都活):

sizeof( char[func()]  ) ;

因为它们都是变长数组。不过,我觉得这两种方法都没什么实际用处。

注意,可变长度数组包含在C99标准草案部分6.7.5.2 数组声明符第4段中:

[…如果size是一个整数常量表达式,并且元素类型有一个已知的常量大小,则数组类型不是变长数组类型;否则,数组类型为变长数组类型。

更新

在C11中,VLA情况的答案会发生变化,在某些情况下,是否计算大小表达式是未指定的。6.7.6.2 数组声明符节中说:

< p >[…其中,size表达式是sizeof操作数的一部分 操作符和改变大小表达式的值将不会 影响运算结果的,不确定是否

例如,在这样的情况下(see it live):

sizeof( int (*)[x++] )

sizeof在编译时运行,但x++只能在运行时求值。为了解决这个问题,c++标准规定不计算sizeof的操作数。C标准说:

如果操作数[of sizeof]的类型是变长数组类型,则计算该操作数;否则,操作数不计算,结果为整数常数。

这一行:

printf("%d and ", sizeof(x++)); // note 1

导致乌兰巴托。%d期望类型int而不是size_t。获得UB后,行为是未定义的,包括写入标准输出的字节。

如果你想通过用%zu替换%d或将值转换为int来解决这个问题,但不是两者都用,你仍然不会增加x,但这是一个不同的问题,应该在不同的问题中问。