以下情况是否属于未定义行为?
我记得在两个序列点之间从同一基础内存中写入和读取数据是未定义的行为,但我不确定。
union {
int foo;
float bar;
} baz;
baz.foo = 3.14 * baz.bar;
我记得在两个序列点之间从同一基础内存中写入和读取数据是未定义的行为,但我不确定。
union {
int foo;
float bar;
} baz;
baz.foo = 3.14 * baz.bar;
baz.foo = 3.14 * baz.bar;
如果在初始化之前定义了bar
,则其行为是被明确定义的。原因是对baz.foo
的副作用相对于对象baz.foo
和baz.bar
的值计算进行了排序。
[...] 更新左操作数存储值的副作用在左右操作数的值计算后进行排序。操作数的评估是无序的。
声明:本回答涉及C++。
您正在访问一个尚未开始生命周期的对象 - baz.bar
- 这会通过[basic.life]/(6.1)导致未定义行为。
假设bar
已经被初始化,那么您的代码就没问题了;在赋值之前,foo
不需要存活,因为没有执行依赖于其值的操作,在其期间,通过重用内存并有效地初始化它来更改活动成员。目前的规则对后者不太清楚,请参见CWG #1116。然而,现状是这样的,这种赋值确实将目标成员设置为活动(= 存活)。
请注意,赋值在操作数的值计算之后被排序(即保证发生),请参见[expr.ass]/1。
u.a = u.b
是未定义的,但 u.a = B(u.b)
就可以了? - Barrybaz.bar
已经初始化且baz.foo
没有被写入,该行为在C中是明确定义的。鉴于2011年标准中C/C++协调努力的成果,我会非常惊讶地发现相同的代码在C++中具有未定义的行为。 - John Bollinger回答关于 C 语言的问题,不是 C++
我原本认为这是定义行为,但后来我读到了以下段落,它来自于ISO C2x(我猜在旧的 C 标准中也存在,但没有检查):
6.5.16.1/3 (赋值运算符::简单赋值::语义):
如果将要存储在一个对象中的值从与第一个对象的存储有任何重叠的另一个对象中读取,则重叠必须是精确的,并且两个对象必须具有兼容类型的限定或非限定版本;否则,行为未定义。
因此,考虑以下情况:
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()。
+
运算符会创建一个新对象,但这是否意味着它重新定义了变量呢?例如,ub1.a = ub1.b;
明显是未定义行为,但 ub1.a = ub1.b + 1;
呢?我在编译该代码时收到了警告,但我并不确定。 - alx - recommends codidactlong1 = long2 + 1;
执行中间复制将大大增加代码大小和执行时间。 - supercatlong1 = long2+long3;
的最快方法是将代码处理为long1 = long2; long1+=long3;
或long1 = long3; long1+=long2;
,但第一个替换仅在已知long1
和long3
标识不同存储区域时才有效,而第二个替换仅在long1
和long2
不同的情况下才有效。 - supercatsomeInt16 += *someUnsignedCharPtr << 4;
,如果不必考虑指针可能指向someInt16
的低字节,最有效的处理方式是获取*someUnsignedCharPtr
,将其左移四位,加到someInt16
的LSB上(捕获进位),重新加载*someUnsignedCharPtr
,将其右移四位,再加上之前的进位和someInt16
的MSB。两次从*someUnsignedCharPtr
中加载值比先加载它,将其值存储到临时位置后进行移位,然后再从该临时位置加载其值要快。但是,如果someUnsignedCharPtr
指向someInt16
的低字节,则在第二次加载someUnsignedCharPtr
之前修改该低字节会破坏该字节的高位,这些高位在移位后会被加到someInt16
的高字节上。