(*)据我所知,标准允许实现修改delete
运算符的操作数,但大多数实现都不会这样做。
int* ptr = new int(0);
delete ptr; //delete is allowed to modify ptr, for example set it to 0
std::cout << ptr; // UB?
确认(*),是ptr
(以打印形式)的读物定义明确吗?
如果delete
确实修改了ptr
,是否允许设置陷阱值,这会使读取ptr
UB?
答案 0 :(得分:15)
在C ++ 14中,这是实现定义的行为,[basic.stc.dynamic.deallocation] / 4:
如果在标准库中赋予释放函数的参数是一个不是空指针值的指针,则释放函数将释放指针引用的存储,使得所有指针都无效,引用解除分配的存储的任何部分
通过无效指针值间接并将无效指针值传递给释放函数具有未定义的行为。对无效指针值的任何其他使用都具有实现定义的行为。
有一个脚注:
某些实现可能会定义复制无效指针值会导致系统生成的运行时错误。
自C ++ 11以来,这种情况发生了变化,其中粗体文本表示“未定义的行为”并且没有脚注。
因此,为了回答您的问题,允许delete ptr;
设置一个陷阱值,该值将导致std::cout << ptr
的运行时错误。编译器文档必须指定行为。这是一个比UB更窄的限制,在这种情况下,任何不稳定的行为都是允许的。
答案 1 :(得分:-1)
在此示例中,std::cout << ptr
默认情况下不是未定义的行为,因为ptr
根本没有被取消引用,因此实际设置的值并不重要。
默认情况下,STL没有为operator<<
指针定义int*
。它定义了:
operator<<
(signed|unsignd) char*
,用于打印以空值终止的文字。
operator<<
的通用void*
,它只打印指针所设置的内存地址本身,而不是指向的数据。
由于int*
可以隐式转换为void*
,因此调用std::cin << ptr
实际上是在调用operator<<(std::cin, (void*)ptr)
,因此按ptr
保存的内存地址打印
只有当您的应用为operator<<
定义自己的int*
时,代码才有未定义的行为,然后在删除指针后尝试取消引用。