来自C ++ Primer 18.1.1:
如果[thrown]表达式具有数组或函数类型,则表达式为 转换为相应的指针类型。
该程序如何产生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*
的异常对象,它是指向数组第一个元素的指针。但是当我们退出a
块并输入catch子句时,我们肯定会销毁try
的数组元素,因为我们改变了块范围?在catch子句的持续时间内,我是否得到了误报,或者数组元素是“单独留下”(未删除)?
答案 0 :(得分:11)
取消引用悬空指针是Undefined Behavior。
当程序遇到未定义的行为时,可以自由地执行任何。其中包括撞击和使守护进程从你的鼻子中飞出,但它也包括做你想要的任何事情。
在这种情况下,通常不会检查覆盖该部分内存并通过堆栈指针访问的中间操作(在它下面,因为堆栈在大多数平台上增长)。
所以是的,这是误报。内存不能保证不再包含这些值,并且根本不保证可以访问,但它仍然包含它们并且可以访问。
另请注意,gcc优化对于依赖于不调用未定义行为的程序而言相当臭名昭着。通常,如果您有未定义的行为,未经优化的版本似乎可以正常工作,但是一旦启用优化,它就会开始做一些完全出乎意料的事情。
答案 1 :(得分:8)
但是当我们退出try块并且因为我们改变块范围而输入catch子句时,a的数组元素肯定会被销毁吗?
正确。
在catch子句的持续时间内,我是否得到了误报或数组元素“单独”(未删除)?
他们不是“孤身一人”。正确假设时会破坏阵列。该程序正在访问无效内存。行为未定义。
该程序如何产生9876543210(g ++ 5.2.0)的正确输出?
未定义行为时,没有正确的输出。程序可以产生它所做的事情,因为它可以在行为未定义时产生任何输出。
关于UB的推理通常是没有意义的,但在这种情况下,如果程序中尚未覆盖部分内存,则无效内存的内容可能相同。
答案 2 :(得分:5)
您将在本章后面找到警告:
抛出指针需要指针指向的对象 在相应的处理程序所在的任何地方都存在点。
所以如果a
数组是静态的或全局的,那么你的例子将是有效的,否则它的UB。或者(正如Jan Hudec在评论中写的那样)在try / catch语句的封闭块中。