【问题标题】:When returning a reference gcc adds 0x30 to %eax返回参考 gcc 时将 0x30 添加到 %eax
【发布时间】:2025-12-15 18:20:12
【问题描述】:

我有一个函数并调用它:

Class1& Class2::get()
{
   return *m_ptr;
}

Class1& c = m_class2->get();

m_ptr 是一个自定义智能指针,我可以在调试器中看到 m_ptr.m_p 为 0,我也可以在其运算符 T* 中看到它确实返回 0。但是 c (&c) 的地址不是 NULL,它是0x30!我在反汇编中看到的:

13059       return *m_ptr;
eaabbc7e:   mov 0x8(%ebp),%eax
eaabbc81:   add $0xb4,%eax
eaabbc86:   mov %eax,(%esp)
eaabbc89:   call 0xea9ce4c0  <operator T*>
eaabbc8e:   add $0x30,%eax
13060     }

就在添加 $0x30,%eax 行之前,我可以看到 %eax 为 0,即运算符正确返回 NULL。

为什么要加 0x30 的行在这里???

【问题讨论】:

  • 好吧我不知道,我不是 那个 asm 方面的大专家。
  • 我不尊重它。这甚至不是我的代码,但它会检查 (&c != NULL) 和 &c != NULL 是否。
  • 没有看到智能指针的解引用运算符,就无法判断这是否是预期的。无论哪种方式,您都不能取消引用空指针,即使您只是为了初始化引用。
  • 你在使用多重继承吗?

标签: c++ linux pointers gcc disassembly


【解决方案1】:

虽然没有足够的信息来回答,但您的 cmets 暗示 Class2 涉及多重继承,我将冒险猜测模板参数 T 是派生类,而不是 Class2 本身。

所以operator T* 返回一个指向这个派生类的指针。为了将其取消引用以提供Class2&amp;,必须将其转换为Class2*,这可能涉及向指针添加偏移量,具体取决于编译器如何在对象中布置基类子对象。

显然,这仅在指针不为空时才有效;这就是为什么你永远不能取消引用空指针的原因之一,即使你只是使用结果来初始化一个引用。

如果函数返回Class2*,那么你会得到一个如预期的空指针;需要该转换才能将 null 转换为 null。在您的情况下,由于您在这种情况下通过取消引用指针来调用未定义的行为,因此编译器无需在执行转换之前检查 null。

【讨论】:

  • 其实正好相反——模板参数 T 是超类,而 Class2 是它的基类,但这仍然是答案!
  • @queen3:是的,这比我的猜测更有意义。
  • 有趣的是,这段代码(正在移植到 Linux)似乎可以在 Windows 下运行。我想 MS 总是检查 NULL,即使是引用。