下面是DEV C++窗口中编译的代码:
1 2 3 4 5 6 7 8 9
| #include <stdio.h>
int main () {
int x = 5;
printf("%d and", sizeof(x ++)); // note 1
printf("%d
", x ); // note 2
return 0;
} |
我希望在执行注释1后,x为6。但是,输出是:
有人能解释为什么在注1之后x不递增吗?
- 我注意到,devc++使用了一个非常过时的编译器,您可能需要升级到一个更新的IDE,例如codeblocks eclipse或Visual Studio。
- ++x产生的结果与x++、cygwin和gcc 3.4.4相同。
- @因为第一个值是变量的sizeof,而不是变量本身。
- 我的回答是由于与这个合并而增加的,我的回答实际上显示了如何在VLAs的情况下获得运行时评估,其他人都没有。
- sizeof(x)表示它的字节存储,即4个字节。因此,不管x的值是多少,它的字节存储都是一样的。既然你声明x是5,那就是第二次显示的。
- %d和size_t之间的类型不匹配可能导致意外结果,但是您看到的是预期的结果。您的代码应该更改为printf("%d and", (int)sizeof(x++));或printf("%zu and", sizeof(x++));。
- 这种写作应该避免,因为它可能会造成不良的副作用。你的问题已经是其中之一了。
根据C99标准(重点是我的)
6.5.3.4/2
The sizeof operator yields the size (in bytes) of its operand, which may be an expression or the parenthesized name of a type. The size is determined from the type of the operand. The result is an integer. If the type of the operand is a variable length array type, the operand is evaluated; otherwise, the operand is not evaluated and the result is an integer constant.
- "如果操作数的类型是可变长度数组类型,则操作数将被计算为"哇!我从没意识到
- 可变长度数组类型是什么意思?这意味着操作数是一个数组?本例中的代码不是数组。你能帮我收拾一下吗?
- 可变长度数组是在编译期间声明的一个值未知的数组,例如,如果从stdin中读取N,并使int array[N]。这是C99的一个特性,在C++中是不可用的。
- @特别是legendofcage,这意味着在像sizeof(int[++x])这样的情况下(不管怎样,这确实是一个坏主意),可以对++进行评估。
- 喔!不知道C有那个特征!只是好奇,编译器究竟是怎么做到的?
- 请参阅http://ideone.com/q89qp上正在使用的VLA示例。
- "原因是表达式x?foo:不评估吧。"什么????
- @好奇心:在编译阶段,编译器注意到x ? foo : bar的结果是double,所以它在最终可执行文件中用8替换整个sizeof。
- 如果评估x ? foo : bar会发生什么?
- 好奇的…同样的事情也会发生。我明白你的意思。这是个坏例子。
- [@pmg]我还是不明白,这不是(a)的类型吗?B:C)与B的类型一样,A是真的,C的类型是真的?表达式如何始终具有单个类型??
- @OMAR:阅读C99标准(PDF文件)中的6.5.15。当B和C是算术类型时,结果的类型基本上是最大的类型。
- 这正是我所猜测的。语言是在一天结束时强类型化的,因此类型不能依赖于值…否则,只能在运行时确定类型。谢谢你的参考。
- @Jensgustedt:ints[++x]将具有类型(例如int),而不是VLA类型,因此不会对其进行评估。你必须做一些类似于sizeof(x++ ? ints1 : ints2)的事情,让两个VLA都成为VLA,以评估符合该规则的副作用。
- @Joe Wresching:由gcc、clang和ideone.com/pf7if进行评估。
- @乔·雷施尼格:这显然是不正确的。int[++x]有vla型int[n](对于N的某些值)。你为什么说它是int????
- 从技术上讲,int[x++]是一种类型,而不是表达式。我认为,如果两个VLA都是int数组,那么其中一个或两个都是VLA的sizeof(x++ ? ints1 : ints2)应该返回sizeof(int *)。因此,sizeof(sizeof(int[x++]))不修改x。
sizeof是编译时运算符,因此在编译时sizeof及其操作数被结果值替换。操作数根本不计算(除非它是可变长度数组);只有结果的类型才重要。
1 2 3 4 5 6 7 8 9 10
| 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;
} |
输出:
因为short在我的机器上占用2个字节。
将函数的返回类型更改为double:
1 2
| double func(short x) {
// rest all same |
将给予8作为输出。
- 只有有时——如果可能的话,现在是编译时间。
- -1,因为这与接受(和正确)的答案相矛盾,不引用标准。
- 使用字符串时,编译sizeof()运算符的时间分辨率有一个很好的好处。如果您有一个初始化为带引号字符串的字符串,而不是使用strlen(),其中包含字符串的字符数组必须在运行时扫描空终止符,那么sizeof(带引号的_字符串)在编译时是已知的,因此在运行时也是已知的。这是一件很小的事情,但是如果您在一个循环中使用被引用的字符串数百万次,那么它在性能上会有很大的不同。
sizeof(foo)试图在编译时发现表达式的大小:
5.5.3.4:
The sizeof operator yields the size (in bytes) of its operand, which may be an
expression or the parenthesized name of a type. The size is determined from the type of
the operand. The result is an integer. If the type of the operand is a variable length array
type, the operand is evaluated; otherwise, the operand is not evaluated and the result is an
integer constant.
简而言之:可变长度数组,在运行时运行。(注意:可变长度数组是一个特定的特性——不是用malloc(3)分配的数组。)否则,只计算表达式的类型,而在编译时计算。
sizeof是编译时内置运算符,不是函数。这在没有括号的情况下变得非常清楚:
1
| (sizeof x) //this also works |
- sizeof((i++)是否有效?
- @加特:是的,多余的括号被忽略了:codepad.org/fhdjyhzv
- 但这是如何回答这个问题的呢?
- @phresnel:这只是为了说明sizeof是"怪异的",不受正常函数规则的约束。不管怎样,我编辑了文章,以消除可能与正常运行时运算符(如(+)和(-)之间的混淆。
注释
这个答案是从一个副本中合并而来的,这解释了延迟的日期。
原件
除了可变长度数组之外,sizeof不计算其参数。我们可以从草案C99标准部分6.5.3.4中看到这一点。操作员第2段的尺寸表示:
The sizeof operator yields the size (in bytes) of its operand, which may be an
expression or the parenthesized name of a type. The size is determined from the type of
the operand. The result is an integer. If the type of the operand is a variable length array
type, the operand is evaluated; otherwise, the operand is not evaluated and the result is an
integer constant.
一条评论(现在已删除)询问这样的东西是否会在运行时进行评估:
事实上,这样的事情也会奏效(看到他们都活下来):
1
| sizeof( char[func()] ) ; |
因为它们都是可变长度的数组。不过,我认为这两种方法都没有多大的实际用途。
注:可变长度阵列包含在草案C99标准节6.7.5.2阵列声明器第4段中:
[...] If the size is an integer constant expression and the element type has a known constant size, the array type is not a variable length array type; otherwise, the array type is a variable length array type.
更新
在C11中,VLA情况的答案会更改,在某些情况下,不确定是否计算大小表达式。从6.7.6.2节数组声明器中,该声明器表示:
[...]Where a size expression is part of the operand of a sizeof
operator and changing the value of the size expression would not
affect the result of the operator, it is unspecified whether or not
the size expression is evaluated.
例如,在这样的情况下(现场查看):
- 这里要记住的重要一点是,大多数时候,sizeof实际上是一个宏——它不创建代码,但预先计算预期值并将其直接存入代码。请注意,在c99之前,这是唯一的行为,因为vbas不存在(在这个答案出现之前,我从来没有听说过它们,信不信由你!)
- 除了确定表达式x++的值和x的新值外,sizeof (char[x++]);将以什么方式使用x的值,这两个值对于该运算符都是正常的?
- @阿尔克…嗯,是的,我的意思是"vla",当然:)shafik-为什么那些会在运行时评估?就像我说的,我从来没有见过VLA,但是它们的类型在编译时都是已知的,不是吗?
- @corleybrigman快速的答案是b/c,标准是这样说的,但是原因是b/c,我们在编译时不知道数组的大小,所以我们必须在运行时评估表达式。VLA是一个有趣的话题,这里有我在这里和这里的两篇文章。
- @沙菲克-啊,好吧,我想我的困惑是我没有意识到char[x++]是一个VLA。在我不熟悉的眼睛看来,它实际上就像一个江户。
- @Corleybrigman我不认为很多人会意识到,考虑到VLA上的问题和答案通常会得到更高的赞成票。
- 这个答案需要对c11进行更新,在某些情况下,子表达式是否被计算是未指定的。
- @MattmcNabb我在看C11,我看不出有什么区别,你能更具体一点吗?
- "并且更改大小表达式的值不会影响运算符的结果"-"不会影响"?但是,如果使用sizeof(char[x++]),它将受到影响。所以他们改变了什么?像sizeof(char[1+0*x++])这样的案例?
由于不计算sizeof运算符的操作数,可以这样做:
1 2 3 4 5 6
| 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
也就是说,如果只在sizeof中使用,则不需要定义函数f。该技术主要用于C++模板元编程,即使在C++中,EDOCX1×0的操作数也没有被评估。
为什么会这样?它的工作是因为sizeof运算符不对值进行操作,而是对表达式的类型进行操作。因此,当您编写sizeof(f())时,它在表达式f()的类型上运行,它只是函数f的返回类型。返回类型总是相同的,无论函数实际执行时返回的值是什么。
在C++中,你甚至可以这样做:
1 2 3 4 5 6 7 8 9 10
| 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的一些其他有趣的属性:
编译期间无法执行。所以不会发生++i或i++的情况。另外,sizeof(foo())不会执行函数,而是返回正确的类型。
- "编译期间不能执行。"什么意思?
- 编译将只创建对象代码…只有当用户执行二进制文件时,才会执行对象代码。当sizeof在编译时发生时,假设i++将递增是错误的。
- "sizeof发生在编译时",您的意思是:"as sizeofis a compile time constant expression"?
- 与预处理期间发生的"define"类似,sizeof也将在编译时发生。在编译期间,所有类型信息都是可用的,因此在编译期间会对sizeof进行计算,并替换值。正如之前C99标准中@pmg提到的。
- 对于不是可变长度数组的对象,"sizeof将在编译时发生"
sizeof在编译时运行,但x++只能在运行时进行评估。为了解决这个问题,C++标准规定EDCOX1〔0〕的操作数不被评估(除了VLAs)。C标准规定:
If the type of the operand [of sizeof] is a variable length array type, the operand is evaluated; otherwise, the operand is not evaluated and the result is an integer constant.
sizeof()运算符只给出数据类型的大小,它不计算内部元素。