为什么返回对函数本地值的引用而不是编译错误?

Dre*_*kes 36 c++ return-by-reference

以下代码调用未定义的行为.

int& foo()
{
  int bar = 1234;
  return bar;
}
Run Code Online (Sandbox Code Playgroud)

g ++发出警告:

警告:返回对局部变量'bar'的引用[-Wreturn-local-addr]

clang ++也是:

警告:返回与本地变量'bar'关联的堆栈内存[-Wreturn-stack-address]

为什么这不是编译错误(忽略-Werror)?

是否存在将ref返回到本地var的情况?

编辑正如所指出的,规范要求这是可编译的.那么,为什么规范不禁止这样的代码呢?

Rei*_*ica 38

我要说的是,要求这使得程序格式错误(也就是说,使其成为编译错误)会使标准复杂化,效果甚微.在确定此类案例时,您必须准确地说明标准,并且所有编制者都必须实施它们.

如果指定的太少,则不会太有用.并且编译器可能已经检查了这个以发出警告,并且真正的程序员-Wall_you_can_give_me -Werror无论如何都要进行编译.

如果指定太多,编译器很难(或不可能)实现该标准.

考虑这个类(您只有标题和库):

class Foo
{
  int x;

public:
  int& getInteger();
};
Run Code Online (Sandbox Code Playgroud)

这段代码:

int& bar()
{
  Foo f;
  return f.getInteger();
}
Run Code Online (Sandbox Code Playgroud)

现在,是否应该制定标准以使其形成不良?可能不会,如果Foo实现如下:

#include "Foo.h"

int global;

int& Foo::getInteger()
{
  return global;
}
Run Code Online (Sandbox Code Playgroud)

同时,它可以像这样实现:

#include "Foo.h"

int& Foo::getInteger()
{
  return x;
}
Run Code Online (Sandbox Code Playgroud)

这当然会给你一个悬垂的参考.

我的观点是,编译器无法确切地知道返回引用是否正常,除了一些简单的情况(返回对函数范围自动变量或非引用类型的参数的引用).我认为将标准复杂化并不值得.特别是因为大多数编译器已经将此作为实施质量问题提出警告.

  • 这些例子确实有助于解释所涉及的复杂性 谢谢.似乎某些语言(D,Rust)具有直接解决此问题的显式生存期说明符. (2认同)

pax*_*blo 7

出于同样的原因,C允许您返回指向已释放的内存块的指针.

它根据语言规范有效.这是一个可怕的主意(和无处接近到被保证工作),但它仍然是因为它不禁止有效.

如果你问为什么标准允许这个,那可能是因为,当引用引用时,这就是它们的工作方式.标准的每次迭代都有一定的指导方针(例如最大限度地减少"破坏变更"的可能性,那些使现有格式良好的程序无效的标准),标准是用户和实施者之间的协议,毫无疑问比实施者更多的实施者在委员会:-)

可能值得推动这个想法作为一个潜在的变化,并看到ISO说什么,但我怀疑它将被视为那些"突破性变化"之一,因此非常怀疑.

  • @paxdiablo我将OP的问题解释为"为什么这个标准要求不正确?" (3认同)
  • 所以没有技术原因?指针和引用是不同的野兽.指针更灵活,允许大量无效使用.如果你正在处理指针,那就是你玩的游戏.但是,参考文献有一定的保证.我希望编译器拒绝这样的代码.大多数情况下,我希望学习一些我之前没见过的边缘案例,这意味着在某些情况下这可能有用. (2认同)

Bas*_*tch 7

此外,因为您可能想要获取当前堆栈指针(无论您的特定实现是什么意思).

这个功能:

 void* get_stack_pointer (void) { int x; return &x; };
Run Code Online (Sandbox Code Playgroud)

AFAIK,如果不取消引用结果指针,则不是未定义的行为.

比这个更便携:

 void* get_stack_pointer (void) { 
    register void* sp asm ("%esp"); return sp; }
Run Code Online (Sandbox Code Playgroud)

至于为什么你可能想要获得堆栈指针:好吧,有些情况下你有正当理由得到它:例如保守的Boehm垃圾收集器需要扫描堆栈(所以想要堆栈指针和堆栈底部) .

如果你返回一个C++引用,你只能使用&一元运算符获取它的地址,那么获得这样一个地址是IIUC合法的(恕我直言,你可以做的唯一合法操作).

获取堆栈指针的另一个原因是获取NULL任何堆,本地或静态数据的非指针地址(您可以例如散列).但是,您可以使用(void*)1(void*)-1用于此目的.

所以编译器只是警告这个.

我想C++编译器应该接受

int& get_sp_ref(void) { int x; return x; }

void show_sp(void) { 
   std::cout << (&(get_sp_ref())) << std::endl; }
Run Code Online (Sandbox Code Playgroud)

  • 引用很大程度上只是指针的语法糖.相当于"取消引用指针"的参考是"左值到拉力转换",它正在做的是UB (4认同)