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.o
和bar.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.o
。 Fn
里面bar.o
会仍然return*(this +24)
,但是应该return *(this + 28)
(假设为 32 位二进制)。
如果 struct Bar
在 foo.cc
和 bar.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 违规。
我在从静态链接库调用方法时遇到问题,而方法 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.o
和bar.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.o
。 Fn
里面bar.o
会仍然return*(this +24)
,但是应该return *(this + 28)
(假设为 32 位二进制)。
如果 struct Bar
在 foo.cc
和 bar.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 违规。