C/ c++联合和未定义行为
C/C++ unions and undefined behaviour
以下是未定义的行为吗?
union {
int foo;
float bar;
} baz;
baz.foo = 3.14 * baz.bar;
我记得从两个序列点之间的相同底层内存中写入和读取是UB,但我不确定。
我记得在两个序列点之间从相同的底层内存写入和读取是UB,但我不确定。
对同一表达式中的同一内存位置进行读写操作不会调用未定义行为,除非该位置在两个序列点之间被修改了不止一次,或者其副作用相对于使用同一位置的值计算是不顺序的。
C11: 6.5表达式:
的表达式如果标量对象上的副作用相对于来说是无序的,或者是同一标量对象上的不同副作用,或者是使用同一标量对象的值进行计算,则行为是未定义的。[…]
baz.foo = 3.14 * baz.bar;
如果bar
在之前初始化,具有良好定义的行为。原因是对baz.foo
的副作用是相对于对象baz.foo
和baz.bar
的值计算排序的。
C11: 6.5.16/3赋值操作符:
[…更新左操作数存储值的副作用是在左操作数和右操作数的值计算之后排序。操作数的求值是无序的。
免责声明:此回答针对c++。
你正在访问一个生命期尚未开始的对象- baz.bar
-它通过[basic.life]/(6.1)诱导UB。
假设bar
已经被激活(例如通过初始化它),你的代码是好的;在赋值之前,foo
不需要是活动的,因为没有任何操作依赖于它的值,并且在赋值期间,通过重用内存并有效地初始化它来更改活动成员。目前的规定对后者并不明确;见CWG #1116。然而,现状是这样的赋值实际上是将目标成员设置为活动的(=alive)。
请注意,赋值是在操作数的值计算之后排序的(即保证发生)-参见[expr.ass]/1。
C回答,不是c++
我认为这是定义行为,但后来我从ISO C2x(我猜这也存在于旧的C标准中,但没有检查)中阅读以下段落:
6.5.16.1/3(赋值操作符::Simple Assignment::Semantics):
表示存储在一个对象中的值是从另一个对象中读取的它以任何方式与第一个对象的存储重叠,然后是重叠应准确,两个物体应具有合格的或兼容类型的非限定版本;否则,行为为定义。
那么,让我们考虑以下内容:
union {
int a;
const int b;
} db;
union {
int a;
float b;
} ub1;
union {
uint32_t a;
int32_t b;
} ub2;
然后,定义行为要做:
db.a = db.b + 1;
但这是未定义行为:
ub1.a = ub1.b + 1;
或
ub2.a = ub2.b + 1;
兼容类型的定义在6.2.7/1(兼容类型和复合类型)中。参见:__builtin_types_compatible_p()。
标准使用了"未定义行为"这个短语,在其他事物中,作为许多实现至少以某种可预测的方式处理一个结构(例如,产生一个没有副作用的不一定可预测的值)的情况的包涵,但是标准的作者认为试图预测实现可能做的一切是不切实际的。它并不是要邀请实现做出毫无意义的行为,也不是要表明代码是错误的(短语"不可移植或错误")。包含一些在某些机器上可能会失败的结构,但在不适合这些机器使用的代码上是正确的。
在一些平台上像8051年一样,如果编译器给出了一个构造someInt16 += *someUnsignedCharPtr << 4;
最有效的方式来处理它,如果没有适应的可能性低字节的指针会指向someInt16
将获取*someUnsignedCharPtr
,左四位转变,将它添加到的LSB someInt16
(捕获携带)、重载*someUnsignedCharPtr
,转变对四位,并将其添加在早些时候携带someInt16
的最高有效位。从*someUnsignedCharPtr
加载值两次将比加载它更快,在进行移位之前将其值存储到临时位置,然后必须从该临时位置加载它的值。但是,如果someUnsignedCharPtr
指向someInt16
的下位字节,那么在第二次加载someUnsignedCharPtr
之前对下位字节的修改将破坏该字节的上位,该字节在移位后将被添加到someInt16
的上位字节。
标准将允许编译器生成这样的代码,即使字符指针不受混叠规则的约束,因为它不要求编译器处理所有非顺序读写影响部分重叠存储区域的情况。如果使用联合而不是字符指针执行这样的访问,编译器可能会识别出字符类型访问总是会重叠16位值的最低有效字节,但我不认为标准的作者希望要求编译器投入必要的时间和精力来有意义地处理这种模糊的情况。
- 编译C++时未定义的引用
- vscode g++链路故障:体系结构x86_64的未定义符号
- 如何修复此错误:未定义对"距离(浮点数,浮点数,浮点数,浮点数,浮点数)"的引用
- 我的项目不会像"undefined reference to `grpc::g_core_codegen_interface'"那样使用未定义的引用错误进行编译
- 不知道某个东西是否被忽略会引入未定义的行为吗
- 对C宏的未定义引用,但在定义它时会出现重新定义错误
- 未定义的引用在哪里
- 编译时的 CImg 库返回对"__imp_SetDIBitsToDevice"的未定义引用
- 对Py_Initialize()的未定义引用
- c++11评估顺序(未定义的行为)
- 使用mysql c++连接器的未定义引用
- 从python调用openMP共享库时,未定义opnMP函数
- 在 Mac 上使用 CMAKE 将 FFTW 和 FFTWPP 链接到项目中时未定义的符号
- Cmake 链接问题:未定义对 Button::mousePressEvent(QGraphicsSceneMouseE
- 未定义的引用 .. 使用 OpenCV 编译 C++ 代码时,从命令行
- 具有外部"c"和程序集的未定义函数
- 此增量后语句是否会导致未定义的行为?
- 尝试调用 .h 文件中定义的变量时出现变量未定义错误
- 在C++中使用内联方法时出现未定义的符号错误
- 对 Scalar ::Scalar() 的未定义引用