首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >C++ -方法根据gdb返回非空指针,但分配给它的变量为空。

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

Stack Overflow用户
提问于 2021-11-15 16:29:32
回答 1查看 243关注 0票数 0

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

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

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

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

同样的代码以前也能工作,但现在它运行在不同的平台上(在Raspberry Pi而不是Alix板上,在Raspbian而不是Ubuntu上)。

更新:--我今天一直在进一步研究这个问题,我发现这个问题发生在这里(在动画中稍微修改了代码,但问题是一样的):

代码语言:javascript
复制
::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编译):(我的评论是我认为正在进行的)

代码语言:javascript
复制
=> 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++代码和反汇编:

代码语言:javascript
复制
inline CReaderEventNotificationData *
getReaderEventNotificationData (void)
{
    return m_pReaderEventNotificationData;
}
代码语言:javascript
复制
   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的记忆,它就是这样的:

代码语言:javascript
复制
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)

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 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.obar.o,并定义

代码语言:javascript
复制
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.oFn it bar.o仍然会返回*(this +24),但是它应该返回*(this + 28) (假设32位二进制)。

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

更新2:

我从磁盘中删除了库的所有跟踪,并重新编译了.a文件,并重新安装了库和标头。我还试图在库不存在时重新编译程序,并得到链接器错误,所以它肯定没有使用我不知道的其他版本的lib .我还删除了程序的完整构建并完全重新编译了它.但还是一样的行为..。

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

ODR违规的一种鬼鬼祟祟的方式是,当构建库和应用程序时,#define是有效的。例如,考虑以下代码:

代码语言:javascript
复制
#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=7bar.cc编译ODR,这样就违反了ODR。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/69977724

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档