将本地数组抛出到 try 块

Throwing an array local to a try block

来自 C++ 入门 18.1.1:

If the [thrown] expression has an array or function type, the expression is converted to its corresponding pointer type.

这个程序如何能够产生 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 子句期间,我得到的是误报还是数组元素 "left alone"(未删除)?

But surely the array elements of a would be destroyed when we exit the try block and enter the catch clause since we change block scope?

正确。

Am I getting a false positive or are the array elements "left alone" (not deleted) during the duration of the catch clause?

他们不是 "left alone"。正如您正确假设的那样,该数组已被销毁。程序正在访问无效内存。行为未定义。

How is it that this program can produce correct output of 9876543210 (g++ 5.2.0)?

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

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

取消引用悬空指针是未定义的行为。

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

在这种情况下,没有任何干预操作会覆盖那部分内存,并且通常不会检查超过堆栈指针(在它下面,因为堆栈在大多数平台上向下增长)的访问。

所以是的,这是误报。内存不再保证包含这些值并且根本不保证可以访问,但它仍然恰好包含它们并且可以访问。

另请注意,gcc 优化因依赖于不调用未定义行为的程序而声名狼藉。通常如果你有一个未定义的行为,未优化的版本似乎可以工作,但一旦你打开优化,它就会开始做一些完全意想不到的事情。

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

Throwing a pointer requires that the object to which the pointer points exist wherever the corresponding handler resides.

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