使用c_str是否有未定义的异常行为
Is this use of c_str with exception undefined behavior?
我看到了几个类似的代码片段,看起来像这样:
struct MyExcept : std::exception {
explicit MyExcept(const char* m) noexcept : message{m} {}
const char* what() const noexcept override {
return message;
}
const char* message;
};
void foo() {
std::string error;
error += "Some";
error += " Error";
throw MyExcept{error.c_str()};
}
int main() {
try {
foo();
} catch (const MyExcept& e) {
// Is this okay?
std::cout << e.message << std::endl;
}
}
在注释Is this okay?
后面的行中,我们读取了使用std::string
在foo
函数中分配的c样式字符串。由于字符串是通过堆栈展开来销毁的,这是未定义的行为吗?
如果它确实是未定义的行为,那么如果我们用这个函数替换main
函数呢?
int main() {
foo();
}
由于没有catch,编译器不会被迫展开堆栈,而是在控制台中输出what()
的结果并中止程序。那么,它仍然是未定义的行为吗?
void foo() {
std::string error;
error += "Some";
error += " Error";
throw MyExcept{error.c_str()};
} // << error goes out of scope here and so does the pointer returned
// from c_str()
由于没有捕获,编译器不会被迫展开堆栈,但会在控制台中输出
what()
的结果并中止程序。那么,它仍然是未定义的行为吗?
由于默认实现将使用std::terminate
,然后调用std::abort()
,这可能仍然是未定义的行为,因为大多数标准处理程序实现将尝试取消引用what()
。
不过,为了避免这种情况,您可以安装自己的处理程序。
您的第一个代码段具有未定义的行为。[exception.ctor]/1
:
当控件从抛出异常的点传递到处理程序时,析构函数由一个进程调用,该进程在本节中指定,称为堆栈展开。
这里调用析构函数或error
,导致c_str()
成为一个悬空指针。稍后取消引用它(例如,当您使用std::cout
时)是未定义的行为。
你的第二个片段非常好。没有理由认为这是未定义的行为。您从未实际调用过what
,也从未执行过任何可能导致未定义行为的其他操作。标准中唯一没有定义的是堆栈是否展开,[except.terminate]/2
:
在找不到匹配处理程序的情况下,在调用
std::terminate()
之前是否展开堆栈是由实现定义的。
正如其他人所说,由于分配给message
的指针处于悬空状态,因此代码具有未定义的行为。
std::runtime_error
已经为这个问题提供了解决方案。调用以std::string
作为输入的构造函数,并且根本不重写what()
:
struct MyExcept : std::runtime_error {
explicit MyExcept(const std::string & m) noexcept : std::runtime_error(m) {}
};
void foo() {
std::string error;
error += "Some";
error += " Error";
throw MyExcept(error);
}
int main() {
try {
foo();
}
catch (const MyExcept& e) {
std::cout << e.what() << std::endl;
}
}
std::runtime_error
有一个内部std::string
,其数据what()
默认返回,从而避免了悬空问题。
- 编译C++时未定义的引用
- vscode g++链路故障:体系结构x86_64的未定义符号
- 如何修复此错误:未定义对"距离(浮点数,浮点数,浮点数,浮点数,浮点数)"的引用
- 我的项目不会像"undefined reference to `grpc::g_core_codegen_interface'"那样使用未定义的引用错误进行编译
- 不知道某个东西是否被忽略会引入未定义的行为吗
- 对C宏的未定义引用,但在定义它时会出现重新定义错误
- 未定义的引用在哪里
- 编译时的 CImg 库返回对"__imp_SetDIBitsToDevice"的未定义引用
- 对Py_Initialize()的未定义引用
- c++11评估顺序(未定义的行为)
- 使用mysql c++连接器的未定义引用
- 使用c_str是否有未定义的异常行为
- 为什么内置类型的对象上的溢出会导致异常/未定义的行为?
- C++ 未定义对 vtable 异常的引用
- 未定义的行为是否会导致定义为从不引发异常的函数中出现异常
- PCL 和 CMake 的问题:链接时未定义统计异常值删除
- C++的 atoi() 如何保证如果它允许未定义的行为不会引发异常?
- emscripten + sdl = 抛出异常:类型错误:无法设置未定义的属性'widthNative'
- 对`vtable for XXX的未定义引用(其中XXX是结构异常)
- 当异常变量未定义时,通过引用捕获