在调用静态链接库中的方法时,遇到了一个问题,该方法返回指向数据结构的指针。根据调试器,返回的值是非空的。但是,在方法返回并将值赋值给局部变量之后,变量为null。
下面的屏幕记录演示了这个问题。记录在调用方法之前开始,然后进入该方法并退出。如您所见,该方法返回一个指向地址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++ )的方法的c++代码和反汇编:
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点的东西?
这是编译器版本btw:gcc version 8.3.0 (Raspbian 8.3.0-6+rpi1)
发布于 2021-11-16 15:29:02
是什么导致了这个问题?
最有可能的原因是您已经用优化编译了代码,并且变得很困惑。程序是继续报告invalid response from reader,还是实际上继续到第181行。
如果是后者,请参见this answer。
如果程序确实执行了第179行,那么很可能您的编译器错误地编译了您的程序(您需要拆解代码才能确定)。
在这种情况下,尝试不同的编译器版本、禁用对特定函数或文件的优化、更改优化级别等都可能让您绕过编译器错误。
更新:
程序确实报告了来自读取器的无效响应,因此它实际上被调用。我花了一下午的时间再次研究这个问题,在这一点上,我认为这是一个编译器错误。在反汇编中,我可以看到它试图从对象地址+24 (
ldr r3, [r3, #24])加载m_pReaderEventNotificationData的值,但是如果我查看内存,这个偏移量实际上是0x000000。它应该返回的实际值是偏移量#28,而不是#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()将返回*(this + 24),此偏移量将被编译到两个对象文件中。
现在您将NUM_X从6更改为7,并重新构建foo.o而不是bar.o。Fn it bar.o仍然会返回*(this +24),但是它应该返回*(this + 28) (假设32位二进制)。
如果struct Bar在foo.cc和bar.cc (ODR违反)中的定义不同,也可能发生类似的行为。
更新2:
我从磁盘中删除了库的所有跟踪,并重新编译了.a文件,并重新安装了库和标头。我还试图在库不存在时重新编译程序,并得到链接器错误,所以它肯定没有使用我不知道的其他版本的lib .我还删除了程序的完整构建并完全重新编译了它.但还是一样的行为..。
您应该验证所涉及的两个文件是否都看到了CREADER_EVENT_NOTIFICATION的相同定义。最好捕获预处理文件并比较那里的定义(这是编译器实际看到的)。确保使用用于构建库和应用程序的确切编译命令。
ODR违规的一种鬼鬼祟祟的方式是,当构建库和应用程序时,#define是有效的。例如,考虑以下代码:
#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;}
};现在使用没有foo.cc的-DNUM_XX=7和bar.cc编译ODR,这样就违反了ODR。
https://stackoverflow.com/questions/69977724
复制相似问题