当询问C中常见的未定义行为时,灵魂比我提到的严格别名规则更加开明.
他们在说什么?
采取以下代码
#include <iostream>
void func() {
int i = 2147483640;
while (i < i + 1)
{
std::cerr << i << '\n';
++i;
}
return;
}
int main() {
func();
}
Run Code Online (Sandbox Code Playgroud)
这段代码显然是错误的,因为while循环仅在有符号的int i溢出(即UB)时才能终止,因此编译器可以例如将其优化为无限循环(Clang在上进行-O3)或执行其他类型的时髦操作。现在我的问题是:从我对C ++标准的阅读中,等同于签名的类型可能会别名(即指针int*和unsigned*别名)。为了进行一些时髦的签名“包装”,以下内容是否具有未定义的行为?
#include <iostream>
static int safe_inc(int a)
{
++reinterpret_cast<unsigned&>(a);
return a;
}
void func() {
int i = 2147483640;
while (i < safe_inc(i))
{
std::cerr << i << '\n';
++i;
}
return;
}
int main() {
func(); …Run Code Online (Sandbox Code Playgroud) 我正在阅读ISO/IEC 9899:TC2中第6.5段的第7段.
它通过以下方式宽恕对对象的左值访问:
一种聚合或联合类型,包括其成员中的上述类型之一(包括递归地,子聚合或包含联合的成员),
请参阅文档,了解"前面提到的"类型,但它们肯定包含对象的有效类型.
它的部分标注为:
此列表的目的是指定对象可能或可能没有别名的情况.
我读这是说(例如)以下定义很好:
#include <stdlib.h>
#include <stdio.h>
typedef struct {
unsigned int x;
} s;
int main(void){
unsigned int array[3] = {73,74,75};
s* sp=(s*)&array;
sp->x=80;
printf("%d\n",array[0]);
return EXIT_SUCCESS;
}
Run Code Online (Sandbox Code Playgroud)
该程序应输出80.
我不是在提倡这是一个好的(或非常有用的)想法,并且承认我在某种程度上解释它是因为我无法想到其他意味着什么并且不能相信它是一个毫无意义的句子!
也就是说,我看不出有理由禁止它.我们所知道的是该位置的对齐和内存内容是否兼容,sp->x为什么不呢?
它似乎甚至可以说,如果我double y;在结构的末尾添加(说)a ,我仍然可以array[0]通过sp->x这种方式访问它.
但是,即使数组大于sizeof(s)任何访问尝试sp->y都是'所有下注'未定义的行为.
可能我礼貌地要求人们说出那句话宽恕而不是进入一个扁平的旋转喊"严格混淆UB严格别名UB"似乎经常是这些事情的方式.
标准说:
1.3.24 [defns.undefined] undefined behavior behavior for which this International Standard imposes no requirements [ Note: Undefined behavior may be expected when this International Standard omits any explicit definition of behavior or when a program uses an erroneous construct or erroneous data. Permissible undefined behavior ranges from ignoring the situation completely with unpredictable results, to behaving during translation or program execution in a documented manner characteristic of the environment (with or without the issuance of a diagnostic message), to …
在c ++标准中,在[basic.lval] /11.6中说:
如果程序试图通过以下类型之一以外的glvalue访问对象的存储值,则行为未定义:[...]
- 聚合或联合类型,包括其元素或非静态数据成员中的上述类型之一(包括递归地,子聚合或包含联合的元素或非静态数据成员),[...]
这句话是严格别名规则的一部分.
它可以允许我们访问非现有联盟的非活动成员吗?如:
struct A{
int id :1;
int value :32;
};
struct Id{
int id :1;
};
union X{
A a;
Id id_;
};
void test(){
A a;
auto id = reinterpret_cast<X&>(a).id_; //UB or not?
}
Run Code Online (Sandbox Code Playgroud)
注意:对标准中我没有掌握的内容进行解释,以及为什么上面的例子可能有用.
我想知道[basic.lval] /11.6有什么用处.
[class.mfct.non-static]/2禁止我们调用"已转换为"联合或聚合的成员函数:
如果为非X类型的对象或从X派生的类型调用类X的非静态成员函数,则行为未定义.
考虑到静态数据成员访问,或静态成员函数可以直接使用qualified-name(a_class::a_static_member)执行,唯一有用的用例[basic.lval] /11.6,可能是访问"casted to"union的成员.我想过使用这个最后的标准规则来实现"优化变体".此变体可以包含A类对象或B类对象,这两个对象以大小为1的位域开头,表示类型:
class A{
unsigned type_id_ :1;
int value :31;
public:
A():type_id_{0}{}
void bar{};
void baz{};
};
class B{
unsigned type_id_ :1;
int value …Run Code Online (Sandbox Code Playgroud)