正在向try块抛出本地数组



来自C++Primer 18.1.1:

如果[抛出]表达式具有数组或函数类型,则该表达式为转换为其对应的指针类型。

这个程序是如何产生9876543210(g++5.2.0)的正确输出的?

#include <iostream>
using namespace std;
int main(){
    try{
        int a[10] = {9,8,7,6,5,4,3,2,1,0};
        throw a;
    }
    catch(int* b) { for(int i = 0; i < 10; ++i) cout << *(b+i); }
}

从引号中,throw a将创建一个类型为int*的异常对象,该对象是指向数组第一个元素的指针。但是,当我们退出try块并进入catch子句时,a的数组元素肯定会被破坏,因为我们更改了块范围?我是得到了一个假阳性,还是数组元素在catch子句的持续时间内"保持不变"(未删除)?

取消引用悬挂指针是"未定义行为"。

当程序遇到未定义的行为时,它可以自由地执行任何操作。这包括崩溃和让守护进程飞出你的鼻子,但也包括做你期望的任何事情。

在这种情况下,不存在会覆盖该部分内存的干预操作,并且通常不检查超过堆栈指针的访问(在堆栈指针下方,因为在大多数平台上堆栈会逐渐减少)。

是的,这是一个假阳性。内存不能保证再包含这些值,也不能保证可以访问,但它仍然碰巧包含这些值并可以访问。

还要注意的是,gcc优化由于依赖于程序而不调用未定义的行为而臭名昭著。通常,如果你有一个未定义的行为,未优化的版本似乎可以工作,但一旦你打开优化,它就会开始做一些完全出乎意料的事情。

但是,当我们退出try块并进入catch子句时,肯定会破坏a的数组元素,因为我们更改了块范围?

正确。

我是得到了一个假阳性,还是数组元素在catch子句的持续时间内"保持不变"(未删除)?

他们并不是"一个人呆着"。按照您正确的假设,数组将被销毁。程序正在访问无效内存。这种行为是不明确的。

这个程序是如何产生9876543210(g++5.2.0)的正确输出的?

当行为未定义时,没有正确的输出。程序可以产生它所做的事情,因为当行为未定义时,它可以产生任何输出。

关于UB的推理通常是毫无意义的,但在这种情况下,如果程序尚未覆盖无效内存的部分,则无效内存的内容可能是相同的。

您将在本章后面找到警告:

抛出指针需要指针指向的对象点存在于相应处理程序所在的任何位置。

因此,如果a数组是静态的或全局的,那么您的示例是有效的,否则就是UB。或者(正如Jan Hudec在评论中所写)在try/catch语句的封闭块中。

最新更新