【问题标题】:invalid pointer when returning std::string (so says libc)返回 std::string 时指针无效(libc 这么说)
【发布时间】:2015-01-18 08:22:26
【问题描述】:

我在 mmapped 文件消费类中有一个成员函数,如下所示:

std::string Data::GetASCIIZ(OFFSET* offsetp) const
{
  char* str = (char*)_buffer + *offsetp;  // _buffer points to mmap'd file
  *offsetp += strlen(str) + 1;
  return std::string(str);
}

('OFFSET'的类型是unsigned long long)

其存在的理由是 (a) 在 (b) 将 *offsetp 的值推进过去之后,返回假定存在于偏移量 *offsetp 处的空终止 C 字符串的 std::string所述 C 字符串的结尾。

我在很多情况下都调用这个函数,没有问题。但是,我最近添加了一个新的调用,它总是以一种特殊的方式 SIGABRT:

*** glibc detected *** /home/ryan/src/coolapp/out/coolapp: free(): invalid pointer: 0xb7eb165c ***

上面的消息后面跟着一个回溯(最后是 libc.so.6 中的一些代码)和一个内存映射......这两者在调试这个问题时表面上对我很有用。

通过使用 GDB 进行调试,我了解到 SIGABRT 实际上并没有发生在我上面引用的 Data::GetASCIIZ 方法中,而是在赋值右侧调用它的代码中。 (所以,我假设在调用 std::string 的复制构造函数期间):

[编辑:更新为与@WhozCraig 的预期答案相吻合]

struct stuff
{
  char version;
  std::string sigstring;
  // ...
};

stuff* mystuff = (stuff*)malloc(sizeof(stuff));
// ...
mystuff->sigstring = _data->GetASCIIZ(offsetp);  // SIGABRT HAPPENS AT THIS SCOPE

在这种特殊情况下,偏移量 *offsetp 处的 C 字符串恰好是一个空字符串,但我已经通过临时修改 *offsetp 以指向 GDB 中的其他内容来验证这不是必然的。

我的方法被标记为const,因为它不会修改Data 对象的任何内部状态。我正在返回一个存在于堆栈中的对象,但我不是通过引用这样做的,我希望复制构造函数(在调用代码中)在该堆栈项被破坏之前做正确的事情。

我尝试重写 GetASCIIZ 方法以使用显式本地,但这没有帮助。

我错过了什么吗?

如果它有用,这里是这个 SIGABRT 发生的 call-during-assignment 的反汇编。 ('==>' 位于错误点。)

424         sigstring = _data->GetASCIIZ(offsetp);
   0x0807def1 <+183>:   mov    0x8(%ebp),%eax
   0x0807def4 <+186>:   mov    0x4(%eax),%eax
   0x0807def7 <+189>:   lea    0x4(%eax),%ecx
   0x0807defa <+192>:   lea    -0x18(%ebp),%eax
   0x0807defd <+195>:   mov    0x1c(%ebp),%edx
   0x0807df00 <+198>:   mov    %edx,0x8(%esp)
   0x0807df04 <+202>:   mov    %ecx,0x4(%esp)
   0x0807df08 <+206>:   mov    %eax,(%esp)
   0x0807df0b <+209>:   call   0x809e6ee <Data::GetASCIIZ(unsigned long long*) const>
   0x0807df10 <+214>:   sub    $0x4,%esp
   0x0807df13 <+217>:   mov    -0x14(%ebp),%eax
   0x0807df16 <+220>:   lea    0x4(%eax),%edx
   0x0807df19 <+223>:   lea    -0x18(%ebp),%eax
   0x0807df1c <+226>:   mov    %eax,0x4(%esp)
   0x0807df20 <+230>:   mov    %edx,(%esp)
   0x0807df23 <+233>:   call   0x8049560 <_ZNSsaSEOSs@plt>
   0x0807df28 <+238>:   lea    -0x18(%ebp),%eax
   0x0807df2b <+241>:   mov    %eax,(%esp)
=> 0x0807df2e <+244>:   call   0x80497f0 <_ZNSsD1Ev@plt>
   0x0807e026 <+492>:   lea    -0x18(%ebp),%eax
   0x0807e029 <+495>:   mov    %eax,(%esp)
   0x0807e02c <+498>:   call   0x80497f0 <_ZNSsD1Ev@plt>
   0x0807e031 <+503>:   mov    %ebx,%eax
   0x0807e033 <+505>:   jmp    0x807e046 <CoolClass::SpiffyMethod(unsigned long long, unsigned long long, unsigned long long*)+524>
   0x0807e035 <+507>:   mov    %eax,%ebx

【问题讨论】:

  • 如何到达所说的“新电话”?提供的Data 对象是否会无意复制并违反RO3 的情况?我真的很好奇您在 调用您的函数之前对sigstring 做了什么。 IE。 // ... 是什么?
  • 尝试类似 const string sigstring = _data->Get...
  • @WhozCraig 通过“新呼叫”,我假设您的意思是最近添加的对 Data::GetASCIIZ() 的呼叫。不; Data 对象的地址被传递到最终进行此调用的类的构造函数中。该地址被隐藏在_data 成员中,并且只能通过_data 引用。所以,没有副本。另外://... 只是表示 。这是sigstring 遇到的第一件事
  • @RyanV.Bissell "malloc 一个包含 sigstring 的结构" - 到此为止。您在 C++ 中 NOT malloc 非 POD 类型。对于包含sigstring 的结构,您应该使用new 和delete,而不是malloc。
  • 要添加到@WhozCraig 的评论中,使用malloc 会为您提供未初始化的内存,对于 POD 类型,这没关系,但其他类型(包括许多 C++ 标准库类型)需要并期望它们的构造函数具有跑。如果你真的想的话,你可以使用malloc,后跟放置new,但与普通的new相比,这可能没有真正的好处:你所获得的只是结果可以与realloc和@987654351一起使用@,而且这些都不适合非 POD 对象。

标签: c++ linux stdstring g++4.8


【解决方案1】:

您的示例如下。

std::string Data::GetASCIIZ(OFFSET* offsetp) const
{
  char* str = (char*)_buffer + *offsetp;  // _buffer points to mmap'd file
  *offsetp += strlen(str) + 1;
  return std::string(str);
}

return 语句不应该返回 new STL 字符串吗?

std::string Data::GetASCIIZ(OFFSET* offsetp) const
{
  char* str = (char*)_buffer + *offsetp;  // _buffer points to mmap'd file
  *offsetp += strlen(str) + 1;
  return new std::string(str);
}

【讨论】:

  • 不,这会在堆上分配一个新的 std::string,并导致内存泄漏。我的return std::string(str) 行有效地创建了一个未命名的局部变量,仅用于将其(按值)返回给调用例程,调用例程在前者被破坏之前将其值分配给另一个字符串。
  • 由于到目前为止我还没有过多地使用 STL,所以我不确定,感谢您的澄清。如果我明白你在说什么,返回 std::string(str) 意味着 new 运算符。
猜你喜欢
  • 1970-01-01
  • 2017-06-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-12-18
  • 2015-04-26
  • 2012-11-24
  • 1970-01-01
相关资源
最近更新 更多