指向无效内存时 sizeof(*ptr) 是否未定义行为?
我们都知道取消引用空指针或指向未分配内存的指针会调用未定义的行为。
但是,在传递给 sizeof
的表达式中使用时,规则是什么?
例如:
int *ptr = 0;
int size = sizeof(*ptr);
这也是未定义的吗?
We all know that dereferencing an null pointer or a pointer to unallocated memory invokes undefined behaviour.
But what is the rule when used within an expression passed to sizeof
?
For example:
int *ptr = 0;
int size = sizeof(*ptr);
Is this also undefined?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
在大多数情况下,您会发现
sizeof(*x)
实际上根本不计算*x
。而且,由于它是对调用未定义行为的指针的求值(取消引用),因此您会发现它基本上没问题。 C11 标准在6.5.3.4 中有这样的规定。 sizeof 运算符 /2
(我在所有这些引号中强调):这与 C99 中的同一部分的措辞相同。 C89 的措辞略有不同,因为当时还没有 VLA。来自
3.3.3.4。 sizeof 运算符
:因此,在 C 中,对于所有非 VLA,不会发生取消引用,并且语句定义良好。如果
*x
的类型是 VLA,则被视为执行阶段sizeof
,需要在代码执行时计算出来。 running - 所有其他都可以在编译时计算。如果x
本身就是VLA,则与其他情况相同,当使用*x
作为sizeof()。
C++ 的规则(正如预期的那样,因为它是一种不同的语言)略有不同,如标准的各个迭代所示:
首先,
C++03 5.3.3。 /1 的大小:
在 C++11 5.3.3 中。 Sizeof /1,你会发现措辞略有不同,但效果相同:
C++11 5. 表达式/7
(上面提到的第 5 条)将术语“未求值的操作数”定义为可能是我读过一段时间以来最无用、最冗余的短语之一,但我不知道ISO人写的时候脑子里在想什么:C++14/17 与 C++11 具有相同的措辞,但不一定位于相同的部分,因为在相关部分。它们位于
5.3.3 中。大小为 /1
和5。 C++14 和 8.3.3 的表达式 /8
。大小为 /1 和8。 C++17 的表达式 /8
。因此,在 C++ 中,
sizeof(*x)
中的*x
的求值永远不会发生,因此它是明确定义的,只要您遵循所有其他规则,例如提供完整类型。但是,底线是没有进行解引用,这意味着它不会导致问题。实际上,您可以在以下程序中看到这种不求值:
您可能认为最后一行将输出与
42
截然不同的内容(< code>774,根据我的粗略计算),因为x
已经改变了很多。但实际上情况并非如此,因为这里重要的只是sizeof
中表达式的类型,并且该类型归结为任何类型x
是。您所看到的(除了第一行和最后一行以外的行上可能存在不同的指针大小)是:
In most cases, you will find that
sizeof(*x)
does not actually evaluate*x
at all. And, since it's the evaluation (de-referencing) of a pointer that invokes undefined behaviour, you'll find it's mostly okay. The C11 standard has this to say in6.5.3.4. The sizeof operator /2
(my emphasis in all these quotes):This is identical wording to the same section in C99. C89 had slightly different wording because, of course, there were no VLAs at that point. From
3.3.3.4. The sizeof operator
:So, in C, for all non-VLAs, no dereferencing takes place and the statement is well defined. If the type of
*x
is a VLA, that's considered an execution-phasesizeof
, something that needs to be worked out while the code is running - all others can be calculated at compile time. Ifx
itself is the VLA, it's the same as the other cases, no evaluation takes place when using*x
as an argument tosizeof()
.C++ has (as expected, since it's a different language) slightly different rules, as shown in the various iterations of the standard:
First,
C++03 5.3.3. Sizeof /1
:In,
C++11 5.3.3. Sizeof /1
, you'll find slightly different wording but the same effect:C++11 5. Expressions /7
(the above mentioned clause 5) defines the term "unevaluated operand" as perhaps one of the most useless, redundant phrases I've read for a while, but I don't know what was going through the mind of the ISO people when they wrote it:C++14/17 have the same wording as C++11 but not necessarily in the same sections, as stuff was added before the relevant parts. They're in
5.3.3. Sizeof /1
and5. Expressions /8
for C++14 and8.3.3. Sizeof /1
and8. Expressions /8
for C++17.So, in C++, evaluation of
*x
insizeof(*x)
never takes place, so it's well defined, provided you follow all the other rules like providing a complete type, for example. But, the bottom line is that no dereferencing is done, which means it does not cause a problem.You can actually see this non-evaluation in the following program:
You might think that the final line would output something vastly different to
42
(774
, based on my rough calculations) becausex
has been changed quite a bit. But that is not actually the case since it's only the type of the expression insizeof
that matters here, and the type boils down to whatever typex
is.What you do see (other than the possibility of different pointer sizes on lines other than the first and last) is:
不。
sizeof
是一个运算符,适用于类型,而不是实际值(不进行求值)。为了提醒您它是一个运算符,我建议您养成在可行的情况下省略括号的习惯。
No.
sizeof
is an operator, and works on types, not the actual value (which is not evaluated).To remind you that it's an operator, I suggest you get in the habit of omitting the brackets where practical.
对于 C 来说,答案可能会有所不同,其中
sizeof
不一定是编译时构造,但在 C++ 中,提供给sizeof
的表达式永远不会被求值。因此,永远不可能出现未定义的行为。通过类似的逻辑,您还可以“调用”从未定义的函数[因为该函数从未实际被调用,因此不需要定义],这是 SFINAE 规则中经常使用的事实。The answer may well be different for C, where
sizeof
is not necessarily a compile-time construct, but in C++ the expression provided tosizeof
is never evaluated. As such, there is never a possibility for undefined behavior to exhibit itself. By similar logic, you can also "call" functions that are never defined [because the function is never actually called, no definition is necessary], a fact that is frequently used in SFINAE rules.sizeof
和decltype
不评估其操作数,仅计算类型。sizeof
anddecltype
do not evaluate their operands, computing types only.在这种情况下,
sizeof(*ptr)
与sizeof(int)
相同。sizeof(*ptr)
is the same assizeof(int)
in this case.由于 sizeof 不计算其操作数(除非您使用的是 C99 或更高版本,否则在可变长度数组的情况下),因此在表达式
sizeof (*ptr)
中,不计算 ptr,因此它是没有解除引用。 sizeof运算符只需要确定表达式*ptr
的类型即可获得合适的大小。Since sizeof does not evaluate its operand (except in the case of variable length arrays if you're using C99 or later), in the expression
sizeof (*ptr)
, ptr is not evaluated, therefore it is not dereferenced. The sizeof operator only needs to determine the type of the expression*ptr
to get the appropriate size.