如果我在常量表达式中除以零,我的玩具编译器会崩溃:
int x = 1 / 0;
Run Code Online (Sandbox Code Playgroud)
C和/或C++标准是否允许这种行为?
c c++ compile-time-constant divide-by-zero constant-expression
如果对表达式的求值导致C中的未定义行为,并且在执行程序时总是计算表达式(例如,如果它出现在开头main),那么如果实现在编译时拒绝它,它是否符合要求?在编译/翻译程序和执行程序之间,C有区别吗?
我知道有C的口译员.他们如何按照C标准处理这种差异?
示例(读取未初始化的本地)
Run Code Online (Sandbox Code Playgroud)int main() { int i; return i; }
在运行它时,在执行的任何阶段(甚至在main调用之前),程序都可以做一些有趣的事情.但是,当我们甚至没有试图运行它时,还会发生一些有趣的事吗?它会在编译器本身导致缓冲区溢出吗?
int *p;
{
int x = 0;
p = &x;
}
// p is no longer valid
{
int x = 0;
if (&x == p) {
*p = 2; // Is this valid?
}
}
Run Code Online (Sandbox Code Playgroud)
在指向它的东西被释放后访问指针是未定义的行为,但是如果稍后的分配发生在同一区域会发生什么,并且您明确地将旧指针与指向新事物的指针进行比较?难道也无妨,如果我投&x,并p以uintptr_t比较之前?
(我知道不能保证这两个x变量占据相同的位置.我没有理由这样做,但我可以想象一个算法,你可以使用一组绝对有效的指针来交叉一组指针指针,删除过程中的无效指针.如果先前失效的指针等于已知的好指针,我很好奇会发生什么.)
考虑
void swap(int* a, int* b)
{
if (a != b){
*a = *a ^ *b;
*b = *a ^ *b;
*a = *a ^ *b;
}
}
int main()
{
int a = 0;
int b = 1;
swap(&a, &b); // after this b is 0 and a is 1
return a > b ? 0 : a / b;
}
Run Code Online (Sandbox Code Playgroud)
swap 试图欺骗编译器不优化程序.
是否定义了此程序的行为?a / b是永远无法达到的,但如果是,那么你会得到除零.
在关于未定义行为(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; 这不是错误的.
换句话说,我确信我的标题问题的答案是否定的.但是,我会很感激其他人对此事的(动机)意见.
对于那些主张肯定答案的人来说,可能还有一个问题是,当编译错误的程序时,会强制重新格式化硬盘吗?
本网站上的一些相关指针:
考虑以下情况
// Global
int x = 0; // not atomic
// Thread 1
x = 1;
// Thread 2
if (false)
x = 2;
Run Code Online (Sandbox Code Playgroud)
根据标准,这是否构成数据竞争?[intro.races] 说:
如果两个表达式求值之一修改内存位置 (4.4),而另一个表达式求值读取或修改同一内存位置,则两个表达式求值会发生冲突。
如果程序的执行包含两个潜在并发冲突的操作,并且至少其中一个操作不是原子操作,并且两者都发生在另一个操作之前,则该程序的执行将包含数据争用,除了下面描述的信号处理程序的特殊情况之外。任何此类数据竞争都会导致未定义的行为。
从语言律师的角度来看是否安全,因为程序永远不能执行“表达式求值” x = 2;?
从技术角度来看,如果某个奇怪、愚蠢的编译器决定对该写入执行推测执行,并在检查实际情况后将其回滚,该怎么办?
激发这个问题的原因是(至少在标准 11 中),允许以下程序的结果完全取决于重新排序/推测执行:
// Thread 1:
r1 = y.load(std::memory_order_relaxed);
if (r1 == 42) x.store(r1, std::memory_order_relaxed);
// Thread 2:
r2 = x.load(std::memory_order_relaxed);
if (r2 == 42) y.store(42, std::memory_order_relaxed);
// This is allowed to result in r1==r2==42 in c++11
Run Code Online (Sandbox Code Playgroud)
他们说,当有UB时,程序可以做任何想做的事情.
但如果我在一个声明中有UB,例如
signed char a = 0x40;
a <<= 2;
Run Code Online (Sandbox Code Playgroud)
或者甚至是一个未使用的(!)零大小的可变长度数组:
int l = 0;
char data[l];
Run Code Online (Sandbox Code Playgroud)
这是否可以忍受,因为只有结果是未定义的,或者这是"坏"吗?
我特别感兴趣的是这样的情况:
signed char a = 0x40;
a <<= 2;
switch (state) {
case X: return
case Y: do something with a; break;
case Z: do something else with a; break;
}
Run Code Online (Sandbox Code Playgroud)
假设情况X涵盖了a未定义值的情况,而其他情况则使用了这种情况.如果我被允许按照我想要的方式计算并稍后进行区分,它会简化事情.
另一种情况是我前几天谈到的那个:
void send_stuff()
{
char data[4 * !!flag1 + 2 * !!flag2];
uint8_t cursor = 0;
if (flag1) {
// fill 4 bytes of data into …Run Code Online (Sandbox Code Playgroud) 考虑以下程序:
struct X {
bool b;
int y;
};
void f(int*);
bool test(X x) {
if (!x.b) {
return false;
}
f(&x.y);
return x.b;
}
Run Code Online (Sandbox Code Playgroud)
看来声明
return x.b;
Run Code Online (Sandbox Code Playgroud)
没有得到优化return true;
所以我的结论是,编译器必须假设可以通过这样做进行f(int*)修改:x.b
void f(int* p) {
bool* q = reinterpret_cast<bool*>(p - 1);
*q = false;
}
Run Code Online (Sandbox Code Playgroud)
f这是真正有效的 C++实现吗?或者换句话说,当指向成员的指针传递给不透明函数时,编译器是否必须始终假设整个对象可能会发生变化?
带有 clang-x86 trunk (-O1) 的编译器输出是这样的:
test(X): # @test(X)
push rax
mov qword ptr [rsp], rdi
test dil, dil
je .LBB0_1
lea rdi, [rsp + 4]
call f(int*)@PLT
cmp …Run Code Online (Sandbox Code Playgroud) 我正在读书C programming a modern approach.我发现了一个问题:
为什么标识符包含多个相邻的下划线并不是一个好主意(例如,在current__balance中)?
任何人都可以向我解释为什么会如此?