C++ - 根据 gdb,方法 returns 非空指针,但分配给它的变量为空

C++ - Method returns non-null pointer according to gdb but the variable it's assigned to is null

我在从静态链接库调用方法时遇到问题,而方法 returns 是指向数据结构的指针。根据调试器,返回的值是非空的。但是在方法returns并将值赋值给局部变量后,该变量为null。

下面的屏幕录像演示了这个问题。记录在调用方法之前开始,然后进入方法并退出。正如你所看到的,方法 returns 一个指向地址 0x6920ae10 的指针但是随后存储在本地指针变量中的值是 0x0.

我在这里不知所措......我已经使用 C++ 很多年了,但我以前从未遇到过这样的问题......我在这里错过了什么愚蠢的东西吗?什么可能导致此问题?

我之前直接在执行代码的机器上编译了静态链接库(用于 Impinj RFID 读取器的 LLRP),我也在同一台机器上重新编译了整个程序,所以我认为这不是远程机器上的二进制代码与 IDE.

中的代码不匹配

相同的代码以前确实有效,但现在它是 运行 在不同的平台上(在 Raspberry Pi 而不是 Alix 板上,在 Raspbian 而不是 Ubuntu).

更新: 我今天一直在进一步调查这个问题,我发现问题出现在这里(稍微改变了动画中的代码但问题是一样的):

::LLRP::CReaderEventNotificationData *p_msg_ren_d = ((::LLRP::CREADER_EVENT_NOTIFICATION *) p_msg)->getReaderEventNotificationData();

if (p_msg_ren_d == NULL)
{
    delete p_connection;
    delete p_msg;
    this->_fail("Invalid response from reader (1).");
     return;
}

这是调用方法时的反汇编(我正在用 -O0 编译):(我的评论,我 认为 是继续)

=> 0x001ee394 <+576>:   ldr r0, [r11, #-24] ; 0xffffffe8                    "Load address of p_msg into r0"
   0x001ee398 <+580>:   bl  0x1f0658 <LLRP::CREADER_EVENT_NOTIFICATION::getReaderEventNotificationData()> "call getReaderEventNotificationData"
   0x001ee39c <+584>:   str r0, [r11, #-28] ; 0xffffffe4                    "store r0 on the stack at sp-28"
   0x001ee3a0 <+588>:   ldr r3, [r11, #-28] ; 0xffffffe4                    "load sp-28 into r3"
   0x001ee3a4 <+592>:   cmp r3, #0                                          "check if rd is NULL"

这是被调用的方法的 c++ 代码和反汇编 (p_msg->getReaderEventNotificationData()):

inline CReaderEventNotificationData *
getReaderEventNotificationData (void)
{
    return m_pReaderEventNotificationData;
}
   0x001f0658 <+0>:     push    {r11}       ; (str r11, [sp, #-4]!) "save r11"
   0x001f065c <+4>:     add r11, sp, #0                             "save sp in r11"
   0x001f0660 <+8>:     sub sp, sp, #12                             "decrement sp by 12"
   0x001f0664 <+12>:    str r0, [r11, #-8]                          "store r0 on the stack at sp-8"
=> 0x001f0668 <+16>:    ldr r3, [r11, #-8]                          "load sp-8 into r3"
   0x001f066c <+20>:    ldr r3, [r3, #24]                           "load r3+24 into r3 THIS IS WRONG!"
                                                                    "m_pReaderEventNotificationData is at offset 28 not 24"
   0x001f0670 <+24>:    mov r0, r3                                  "move r3 into r0 as the return value"
   0x001f0674 <+28>:    add sp, r11, #0                             "restore sp"
   0x001f0678 <+32>:    pop {r11}       ; (ldr r11, [sp], #4)       "restore r11"
   0x001f067c <+36>:    bx  lr                                      "return"

如果我看一下地址 p_msg 的内存,它是这样的:

0x69405de8: 0x002bcbf8  0x002b8774  0x00000000  0x69408200
0x69405df8: 0x69408200  0x5c5a5b1a  0x00000000  0x6940ed90
0x69405e08: 0x00000028  0x0000012d  0x694035f0  0x694007c8

所以在偏移 24 处,它实际上是 0x00000000,这就是该方法返回的内容。但是应该返回的正确值实际上位于偏移量 28 (0x6940ed90)

这是编译器问题吗?或者一些 64 位的东西?

顺便说一句,这是编译器版本:gcc version 8.3.0 (Raspbian 8.3.0-6+rpi1)

What could cause this problem?

最可能的原因是您在优化编译代码时感到困惑。程序是继续报告 invalid response from reader,还是实际上继续到第 181 行。

如果是后者,见this answer

如果程序确实执行到第 179 行,那么很可能是您的编译器错误编译了您的程序(您需要反汇编代码才能确定)。

在这种情况下,尝试不同的编译器版本、禁用对特定函数或文件的优化、更改优化级别等可能会让您解决编译器错误。

更新:

The program does report the invalid response from reader, so it is actually called. I spent all afternoon investigating this again and at this point i believe it's a compiler error. In the disassembly i can see that it tries to load the value of m_pReaderEventNotificationData from the object-address+24 (ldr r3, [r3, #24]) but if i view the memory, at this offset is actually 0x000000. The real value that it should return is at offset #28 instead of #24.

这实际上是一个非常普遍的问题,通常 源于 ODR 违规或不完整的重建。

假设你有两个目标文件:foo.obar.o,同时定义

const int NUM_X = 6;
struct Bar {
  int m_x[NUM_X];
  void *m_p;
  void *Fn() { return m_p;}
};

如上所示,Fn() 将 return *(this + 24),并且此偏移量将被编译到两个目标文件中。

现在您将 NUM_X 从 6 更改为 7,并重建 foo.o 不是 bar.oFn里面bar.o仍然return*(this +24),但是应该return *(this + 28)(假设为 32 位二进制)。

如果 struct Barfoo.ccbar.cc 中的定义不同(ODR 违规),可能会发生类似的行为。

更新二:

I deleted all traces of the library from the disk and recompiled the .a file and reinstalled the library and the headers. I also tried to recompile the program when the lib was not present and got a linker error so it's definitely not using another version of the lib that i don't know about... I also deleted the complete build of the program and fully recompiled it... But it's still the same behavior..

您应该确认所涉及的两个文件都看到了 CREADER_EVENT_NOTIFICATION 相同 定义。最好捕获预处理文件并比较那里的定义(这是编译器 实际上 看到的)。请务必使用您用于构建库和应用程序的 exact 编译命令。

如果构建库和应用程序时生效的 #define 不同,ODR 违规可能会偷偷潜入的一种偷偷摸摸的方式。例如,考虑以下代码:

#ifdef NUM_XX
const int NUM_X = NUM_XX;
#else
const int NUM_X = 6;
#endif

struct Bar {
  int m_x[NUM_X];
  void *m_p;
  void *Fn() { return m_p;}
};

现在用 -DNUM_XX=7 编译 foo.cc,不用 bar.cc 编译,你有一个 ODR 违规。