如果局部堆栈变量是未定义的行为,为什么编译器会发出关于返回对该变量的引用的警告? C++标准声明返回对本地变量(栈上的引用)是未定义的行为,那么为什么当前编译器中的许多(如果不是全部)只给出警告? struct A{ }; A& foo() { A a; return a; //gcc and VS2008 both give this a warning, but not a compiler error }

如果局部堆栈变量是未定义的行为,为什么编译器会发出关于返回对该变量的引用的警告? C++标准声明返回对本地变量(栈上的引用)是未定义的行为,那么为什么当前编译器中的许多(如果不是全部)只给出警告? struct A{ }; A& foo() { A a; return a; //gcc and VS2008 both give this a warning, but not a compiler error },c++,compiler-construction,standards,C++,Compiler Construction,Standards,如果编译器对此代码给出错误而不是警告,不是更好吗 只需一个警告就可以编译这段代码,这有什么好处吗 请注意,这不是关于代码> const 引用,它可以延长临时性的生命周期到引用本身的生命周期。未定义的行为不是编译错误,它不是一个格式良好的C++程序。并不是每一个格式错误的程序都是不可编译的,它只是不可预测的。我敢打赌,计算机在某种程度上甚至不可能决定给定程序文本是否是一个格式良好的C++程序。 您可以始终将-Werror添加到gcc,以使警告以错误终止编译 要添加另一个最受欢迎的SO主题:是否希望

如果编译器对此代码给出错误而不是警告,不是更好吗

只需一个警告就可以编译这段代码,这有什么好处吗


请注意,这不是关于代码> const 引用,它可以延长临时性的生命周期到引用本身的生命周期。

未定义的行为不是编译错误,它不是一个格式良好的C++程序。并不是每一个格式错误的程序都是不可编译的,它只是不可预测的。我敢打赌,计算机在某种程度上甚至不可能决定给定程序文本是否是一个格式良好的C++程序。 您可以始终将

-Werror
添加到gcc,以使警告以错误终止编译


要添加另一个最受欢迎的SO主题:是否希望
++i++
也导致编译错误?

您还可以返回对静态变量的引用,该引用是有效的代码,因此代码必须能够编译。

因为标准并不限制我们

如果你想射中自己的脚,你可以做到

但是,让我们看看它在哪些方面有用:

int &foo()
{
    int y;
}

bool stack_grows_forward()
{
    int &p=foo();
    int my_p;
    return &my_p < &p;
}
int&foo()
{
int-y;
}
布尔堆栈向前增长()
{
int&p=foo();
int my_p;
返回&my_p<&p;
}

从编译器的角度,几乎不可能验证您是否返回对临时文件的引用。如果标准要求将其诊断为错误,那么编写编译器几乎是不可能的。考虑:

bool not_so_random() { return true; }
int& foo( int x ) {
   static int s = 10;
   int *p = &s;
   if ( !not_so_random() ) {
      p = &x;
   }
   return *p;
}
上述程序正确且运行安全,在我们当前的实现中,保证
foo
将返回对
静态
变量的引用,这是安全的。但从编译器的角度来看(如果单独编译,无法访问
not_so_random()
的实现,编译器就无法知道程序的格式是否正确)

这是一个玩具示例,但您可以想象类似的代码,具有不同的返回路径,其中
p
可能引用所有返回
*p

的路径中的不同长寿命对象,如果您返回指向本地内部函数的指针/引用,只要您不取消对该函数的引用,行为就得到了很好的定义从函数返回的inter/reference

只有当一个人取消引用返回的指针时,它才是未定义的行为。

它是否是未定义的行为取决于调用函数的代码,而不是函数本身

因此,就在编译函数时,编译器无法确定行为是未定义的还是定义良好的。它所能做的最好的事情就是警告您潜在的问题,而它确实做到了

代码示例:

#include <iostream>

struct A
{ 
   int m_i;
   A():m_i(10)
   {

   } 
};  
A& foo() 
{     
    A a;
    a.m_i = 20;     
    return a; 
} 

int main()
{
   foo(); //This is not an Undefined Behavior, return value was never used.

   A ref = foo(); //Still not an Undefined Behavior, return value not yet used.

   std::cout<<ref.m_i; //Undefined Behavior, returned value is used.

   return 0;
}
#包括
结构A
{ 
国际货币基金组织;
A():m_i(10)
{
} 
};  
A&foo()
{     
A A;
a、 m_i=20;
返回a;
} 
int main()
{
foo();//这不是未定义的行为,从未使用过返回值。
A ref=foo();//仍然不是未定义的行为,返回值尚未使用。

std::cout依赖它是非常糟糕的做法,但我确实相信在许多情况下(这从来不是一个好的赌注),如果在时间
foo()之间没有调用函数,那么内存引用仍然有效
返回和调用函数使用其返回值的时间。在这种情况下,堆栈的该区域将没有机会被覆盖


<>在C和C++中,你可以选择访问任意的内存段(当然在进程的内存空间)通过指针算法,那么为什么不允许在选择的任何地方构造引用呢?

编译器不应该拒绝编译程序,除非标准规定允许他们这样做。否则移植程序会困难得多,因为它们可能不会使用不同的编译器编译,即使它们可以编译与标准保持一致

考虑以下功能:

int foobar() {
    int a=1,b=0;
    return a/b;
}
任何合适的编译器都会检测到我被零除,但它不应该拒绝代码,因为我可能真的想要触发一个SIG_FPE信号

正如大卫·罗德里格斯(David Rodríguez)所指出的,有些情况是不可判定的,但也有一些是不可判定的。该标准的一些新版本可能描述了编译器必须/被允许拒绝程序的一些情况。这将要求该标准对要执行的静态分析非常具体


java标准实际上指定了一些检查非空方法总是返回值的规则。不幸的是,我还没有读到足够的C++标准来知道编译器允许做什么。

继续,但是在特定的情况下,OP发布了,编译器可以看到返回的变量是非静态的。询问编译器为什么允许这样的情况。@Gravity:因为标准要求编译器诊断代码,以确定某个代码是否真的是临时的。考虑到别名和单独编译,这可能是一项难以置信的艰巨任务。还要记住:当标准首次编写时,编译器的智能还不及标准的一半它们是今天的。你的函数
实现\u在哪里使用了\u stack()
?:-)@Kerrek:就这一点而言,
在\u无关的指针之间进行比较\u工作()
。如果编译器可以证明你的程序产生了未定义的行为,则允许编译失败。(是的,我希望
++I++
int I;int j=I;
导致编译错误)。@ybungalobill:你确定吗?编译器不应该编译失败,除非标准上说这就是全部