我有以下 C/C++ 代码片段:
#define ARRAY_LENGTH 666
int g_sum = 0;
extern int *g_ptrArray[ ARRAY_LENGTH ];
void test()
{
unsigned int idx = 0;
// either enable or disable the check "idx < ARRAY_LENGTH" in the while loop
while( g_ptrArray[ idx ] != nullptr /* && idx < ARRAY_LENGTH */ )
{
g_sum += *g_ptrArray[ idx ];
++idx;
}
return;
}
Run Code Online (Sandbox Code Playgroud)
当我使用版本 12.2.0 中的 GCC 编译器编译上述代码时,并选择-Os两种情况:
g_ptrArray[ idx ] != nullptrg_ptrArray[ idx ] != …如果 C 程序有未定义的行为,任何事情都可能发生。因此编译器可能会假设任何给定的程序不包含 UB。因此,假设我们的程序包含以下内容:
\nx += 5;\n/* Do something else without x in the meantime. */ \nx += 7;\nRun Code Online (Sandbox Code Playgroud)\n当然,这可以优化为
\n/* Do something without x. */\nx += 12;\nRun Code Online (Sandbox Code Playgroud)\n或类似的其他方式。
\n如果 x 具有类型,unsigned int则上述程序中不可能出现 UB。另一方面,如果 x 有类型signed int,则有可能溢出,从而产生 UB。由于编译器可能会假设我们的程序不包含UB,因此我们可以进行与上面相同的优化。事实上,在这种情况下,编译器甚至可以假设x - 12 <= MAX_INT.
然而,这似乎与 Jens Gustedt 著名的“Modern C”(第 42 页)相矛盾:
\n\n\n但这样的优化也可以被禁止,因为编译器无法证明某个操作不会强制程序终止。在我们的示例中,很大程度上取决于 x 的类型。如果 x 的当前值可能接近类型的上限,则看似无辜的操作 x += 7 可能会产生溢出。此类溢出根据类型的不同而有不同的处理方式。正如我们所看到的,无符号类型的溢出不是问题,并且压缩运算的结果将始终与两个单独的结果一致。对于其他类型,例如有符号整数类型(signed)和浮点类型(double),溢出可能会引发异常并终止程序。在这种情况下,无法执行优化。
\n
(强调我的)。如果编译器可以(并且确实)假设我们的程序没有 UB,为什么不能执行此优化?
\n …c optimization integer-overflow compiler-optimization undefined-behavior
在关于未定义行为(UB)的许多讨论中,已经提出了这样的观点:在程序中存在任何具有UB的任何构造的程序都要求一致的实现做任何事情(包括什么都没有).我的问题是,即使在UB与代码执行相关联的情况下,是否应该采取这种方式,而标准中规定的行为(否则)规定不应执行相关代码(这可能是对于程序的特定输入;它可能在编译时不可判定).
更加非正式地说,UB的气味是否要求一致的实现来决定整个程序发臭,并且拒绝正确执行甚至行为完全明确定义的程序部分.一个示例程序将是
#include <iostream>
int main()
{
int n = 0;
if (false)
n=n++; // Undefined behaviour if it gets executed, which it doesn't
std::cout << "Hi there.\n";
}
Run Code Online (Sandbox Code Playgroud)
为清楚起见,我假设程序格式正确(因此特别是UB与预处理无关).事实上,我愿意将UB限制为与"评估"相关联,而"评估"显然不是编译时实体.我认为,与给出的例子相关的定义(重点是我的):
之前排序的是由单个线程(1.10)执行的评估之间的不对称,传递,成对关系,这导致这些评估之间的部分顺序
在运算符的结果的值计算之前,对运算符的操作数的值计算进行排序.如果对标量对象的副作用相对于...或使用相同标量对象的值进行值计算未被排序,则行为未定义.
隐含地清楚的是,最后一句中的主语"副作用"和"价值计算"是"评价"的实例,因为这就是定义"之前排序"的关系.
我认为在上述程序中,标准规定不进行评价,以满足最后一句中的条件(相对于彼此和所描述的类型),并且该程序不具有UB; 这不是错误的.
换句话说,我确信我的标题问题的答案是否定的.但是,我会很感激其他人对此事的(动机)意见.
对于那些主张肯定答案的人来说,可能还有一个问题是,当编译错误的程序时,会强制重新格式化硬盘吗?
本网站上的一些相关指针:
我知道未定义的行为可能会导致任何事情,这使得任何包含UB的程序都可能毫无意义.我想知道是否有任何方法可以确定程序中最早的一点,即未定义的行为可能会导致问题.这是一个例子来说明我的问题.
void causeUndefinedBehavior()
{
//any code that causes undefined behavior
//every time it is run
char* a = nullptr;
*a;
}
int main()
{
//code before call
//...
causeUndefinedBehavior();
//code after call
//...
}
Run Code Online (Sandbox Code Playgroud)
根据我的理解,可能引发未定义行为的可能时间(不一定表现出来)是:
causeUndefinedBehavior()编译.main()编译.causeUndefinedBehavior()执行.或者,对于每个案例和每个实现,未定义的行为都会被完全不同?
另外,如果我注释掉causeUndefinedBehavior()调用的行,是否会消除UB,或者它是否仍然在程序中,因为编译了包含UB的代码?
来自msdn博客的这篇文章的一个例子让我做了自动收报机:
它说这个功能:
void unwitting(bool door_is_open)
{
if (door_is_open) {
walk_on_in();
} else {
ring_bell();
// wait for the door to open using the fallback value
fallback = value_or_fallback(nullptr);
wait_for_door_to_open(fallback);
}
}
Run Code Online (Sandbox Code Playgroud)
可以优化到这一个:
void unwitting(bool door_is_open)
{
walk_on_in();
}
Run Code Online (Sandbox Code Playgroud)
因为调用value_or_fallback(nullptr)是未定义的行为(这在本文前面已经证明).
现在我不明白的是:运行时只有在到达该行时才进入未定义的行为.在运行时进入UB之前,第一段的所有可观察效果是否都应该被解决,这不应该发生在之前/之后发生的概念吗?
假设程序员忘记初始化他的一个自动变量,并且他使用了它的值,从而调用了未定义的行为.
...
int i = 0, j;
...
printf("value of 'j': %d\n", j);
...
...
char buf[256];
fputs("Enter query:", stdout);
fgets(buf, sizeof(buf), stdin);
... //process input
... perform other tasks
Run Code Online (Sandbox Code Playgroud)
程序员注意到屏幕上出现了乱码,并意识到他的程序是错误的,但它并没有崩溃,反正还在继续.
假设在此之后,程序提示用户输入并期望处理它,显示结果并执行所有独立于未初始化变量的其他任务,鼓励程序员停止使用该程序,修复错误,重新编译和运行?该计划的其余部分是否会不一致?
操作系统:ubuntu2204
编译器:gcc 11.2 x86_64
这是一个简单的代码:
#include <cstdlib>
int func(int val) {
if (val == 1) {
} else {
abort();
}
}
int main(int argc, char* argv[]) {
func(argc);
}
Run Code Online (Sandbox Code Playgroud)
当我在没有任何优化的情况下编译它并运行它时,它工作正常。
但是当我用 编译它时g++ tmp.cpp -O3,结果func会忽略输入值,而只调用abort.
当然,我可以通过在 的末尾添加 return 语句来修复它func,但是,为什么呢?
这是objdump -d a.out优化函数的一些输出func:
0000000000001060 <_Z4funci>:
1060: f3 0f 1e fa endbr64
1064: 50 push %rax
1065: 58 pop %rax
1066: 50 push %rax
1067: e8 e4 ff ff ff …Run Code Online (Sandbox Code Playgroud) c++ optimization assembly compiler-optimization undefined-behavior