假设我有一个类
class A {
int x;
int y;
public:
getSum1() const {
return getx() + y;
}
getSum2() const {
return y + getx();
}
getx() const {
return x;
}
}
然后我有
int main(int argc, char **argv) {
A *a = 0;
switch(argc) {
case 0:
a->getsum1();
break;
default:
a->getsum2();
break;
}
return 1;
}
这个程序将是段错误的。我注意到在我的机器上,当getsum1执行时,核心 dump表示段错误是由getx引起的,当getsum2执行时,它表示错误发生在getsum2中。
这是有道理的。我有两个问题:
1.是指定了这种行为,还是依赖于实现?
最重要的是:
2.核心转储是否可以说当一个被解除引用时,段错误发生在主要的? (即在a-> getsum *)
感谢。
答案 0 :(得分:1)
当您在空指针上调用这些函数时,您得到了undefined behavior。这就是应该说的一切;任何事情都可能发生,不要这样做。
它出现段错误的原因是因为没有A
为空。尝试访问这些成员正试图访问无效地址。 (这发生在getx
和getSum2
中,因此是段错报告。)
不,它不能说在main
中发生了段错误,因为在main中没有访问null。 (毫无疑问,您仍然在main中输入了未定义的行为,但实际上它只是将this
设置为null的函数调用。)您在这些函数中访问它。实际上,如果函数从不使用this
,则它不会因空指针而崩溃。
但不要。
答案 1 :(得分:0)
调用访问成员并在空指针上调用成员函数是未定义的行为。
当程序试图访问x或y时会发生段错误,这就是它在第一个中的getx()和第二个中的getSum2()中发生的原因。
答案 2 :(得分:0)
当coredump发生时,你不再是C ++了。编译代码中的一些语句会导致不良事件发生,例如尝试访问无效的内存地址,在您的情况下通过空指针。
我想你希望核心转储中的诊断可能会指出问题,而不是(看似在这种情况下)给出稍微不确定的结果。
问题在于从编译的二进制文件返回到源代码行是有点棘手的,因为语言中的范围非常广泛,可用于某些“as-if”类型的语句重新排序和其他聪明。
因此,C ++本身,在语言规范中,将不会指定核心转储将告诉您的内容,并且正如已经观察到的那样,在取消引用空指针时显式地给出了未定义的行为。
事情是我并不认为这太重要了。对于这种错误,得到一个“足够接近”的指示器,问题很快就会导致问题的根源。更糟糕的是堆腐败问题,其中症状和原因可能是彼此远离的许多步骤,并且核心转储使用较少。
答案 3 :(得分:0)
两件重要的事情:
您有未定义的行为,因为a
为空。未定义的行为意味着任何事情都会发生,C ++标准对具有未定义行为的程序没有任何限制。
算术运算符的操作数的评估顺序是未指定。这意味着编译器可能会生成在两个getX()
函数中首先调用getSum
的代码,即使您对它们进行了不同的排序。在您的特定情况下,操作数的评估顺序是一致的。
§5/ 5 “表达式”:除非另有说明,否则单个运算符的操作数和各个表达式的子表达式的评估顺序以及副作用发生的顺序是未指定的。
答案 4 :(得分:0)
"什么时候抛出了segfault?"有时会抛出从不,什么时候抛出。 最多实施的基础是具有内存保护的操作系统。当操作系统不存在(嵌入式)时,或者非常简单(CP / M,DOS),或者具有低调的#34;目标(嵌入式)或CPU避免此类功能(< 80186)问题被隐藏或延迟。这是个坏消息。 休斯顿,我们有大看不见的问题。
注意: 受保护环境中的C ++也可以实现隐藏问题的情况(指针不好但在有效区域内)
一般规则如何理解C / C ++是关键"未定义的行为" (如许多答案所说),有时会出现此类异常