C++ 我有一种感觉。但我怎么能确定呢?
给定下面的代码,编译器为函数f1()和f2()发出C++ 我有一种感觉。但我怎么能确定呢?,c++,undefined-behavior,C++,Undefined Behavior,给定下面的代码,编译器为函数f1()和f2()发出警告C4172:返回局部变量或临时,但不为f3()。我理解编译器在某些情况下可能无法识别此问题,因为下面的函数f3()就是这种情况。但是,在没有警告信息的情况下,我如何确定这种情况下的正确诊断 const char* const& f1() { return "hello1"; } const char* const& f2() { return static_cast<const char*>("hello2");
警告C4172:返回局部变量或临时
,但不为f3()。我理解编译器在某些情况下可能无法识别此问题,因为下面的函数f3()就是这种情况。但是,在没有警告信息的情况下,我如何确定这种情况下的正确诊断
const char* const& f1() { return "hello1"; }
const char* const& f2() { return static_cast<const char*>("hello2"); }
const char* const& f3() { const char* const& r = "hello3"; return r; }
const char*const&f1(){返回“hello1”;}
const char*const&f2(){return static_cast(“hello2”);}
const char*const&f3(){const char*const&r=“hello3”;返回r;}
是,代码的行为未定义,因为它返回对本地指针的引用,而本地指针在f1
和f2
中被正确检测到
您不能依赖编译器的诊断来捕获这些(或任何其他)未定义行为的情况,它们是在“尽力而为”的基础上提供的。在这个简单的例子中,g++4.8.0不警告(使用-Wall
)显示编译器很容易被愚弄:
int& r() {
int x = 1;
int& y = x;
return y;
}
(仅返回
x
按预期发出警告,并且clang
对所有四个函数发出警告。)是的,代码的行为未定义,因为它返回对本地指针的引用,而本地指针在f1
和f2
中被正确检测到
您不能依赖编译器的诊断来捕获这些(或任何其他)未定义行为的情况,它们是在“尽力而为”的基础上提供的。在这个简单的例子中,g++4.8.0不警告(使用-Wall
)显示编译器很容易被愚弄:
int& r() {
int x = 1;
int& y = x;
return y;
}
(只需按预期返回
x
警告,以及clang
对所有四个函数发出警告。)我确信所有三个函数都有未定义的行为
对于坚持认为f3
不是UB(甚至f1
/f2
)的人:请尝试运行此代码:
#include <iostream>
const char* const& f1() { return "hello1"; }
const char* const& f2() { return static_cast<const char*>("hello2"); }
const char* const& f3() { const char* const& r = "hello3"; return r; }
int main()
{
using namespace std;
//#define F f1
//#define F f2
#define F f3
const char* const& ret = F();
cerr << ret;
cerr << ",";
cerr << ret;
return 0;
}
实际上,字符串literal“hello3”
不是一个常量字符*
,而是一个(静态)常量字符[7]
。在代码中const char*const&r=“hello3”代码>,引用不能直接绑定到此字符数组,因为它的类型不同,因此编译器必须创建一个临时字符指针(在堆栈上创建),该指针通过隐式转换(数组到指针衰减)初始化,引用绑定到该指针()。此临时const char*
的生存期被“扩展”到引用r
的生存期,因此不会在第一个分号处结束,而是在函数返回(和)时结束。因此,f3
返回一个“悬挂引用”。在我的测试输出代码中,任何覆盖堆栈的后续操作都会使UB可见
在jalf的评论之后编辑:我意识到“它在第二次输出上打印垃圾”并不是UB的证据。带有UB的程序可以完全按照预期工作,或者崩溃,或者什么都不做,或者其他任何事情。但是,尽管如此,我不相信一个定义良好的程序(没有UB)会像那样打印垃圾…我确信这三个函数都有未定义的行为
对于坚持认为f3
不是UB(甚至f1
/f2
)的人:请尝试运行此代码:
#include <iostream>
const char* const& f1() { return "hello1"; }
const char* const& f2() { return static_cast<const char*>("hello2"); }
const char* const& f3() { const char* const& r = "hello3"; return r; }
int main()
{
using namespace std;
//#define F f1
//#define F f2
#define F f3
const char* const& ret = F();
cerr << ret;
cerr << ",";
cerr << ret;
return 0;
}
实际上,字符串literal“hello3”
不是一个常量字符*
,而是一个(静态)常量字符[7]
。在代码中const char*const&r=“hello3”代码>,引用不能直接绑定到此字符数组,因为它的类型不同,因此编译器必须创建一个临时字符指针(在堆栈上创建),该指针通过隐式转换(数组到指针衰减)初始化,引用绑定到该指针()。此临时const char*
的生存期被“扩展”到引用r
的生存期,因此不会在第一个分号处结束,而是在函数返回(和)时结束。因此,f3
返回一个“悬挂引用”。在我的测试输出代码中,任何覆盖堆栈的后续操作都会使UB可见
在jalf的评论之后编辑:我意识到“它在第二次输出上打印垃圾”并不是UB的证据。带有UB的程序可以完全按照预期工作,或者崩溃,或者什么都不做,或者其他任何事情。但是,尽管如此,我不相信一个定义良好的程序(没有UB)会像那样打印垃圾…您使用的是哪种编译器(和版本)?我已经在VS2010和GCC中对此进行了测试,(它们一直存在到程序结束)没有一个函数有UB。实现需要为这些情况提供诊断,幸运的是它们提供了诊断,但您不能盲目相信这些诊断。@CaptainObvlious我刚刚意识到这些函数不会返回常量字符*
,而是返回常量字符*常量&
,也就是说,指向静态字符数组的指针不是通过值返回的,而是通过引用const!因此,提出了正确的警告<代码> f3是相同的,但是使用显式命名变量似乎欺骗了编译器在标题中的问题,唯一的方法是确定是否是UB,是通过在ISO C++标准中查找的。如果您可以在那里找到一段描述代码行为的文字,那么它是定义良好的。如果没有这样的段落存在(或者如果你发现有一段明确地说它是未定义的),那么它就是UB。遗憾的是,没有简单的快捷方式(尽管在这里询问可能会提示响应,告诉您在标准中查找的位置)您使用的是哪种编译器(和版本)?我已经在VS2010和GCC中测试过这一点,(它们在程序结束前都是活动的)所有函数都没有UB。实现需要为这些情况提供诊断,幸运的是它们确实提供了诊断,但您不能盲目相信这些诊断。@CaptainObvlious我刚刚意识到这些函数不会返回const char*