valgrind 抱怨 C++ 结构上的未初始化字节

Posted

技术标签:

【中文标题】valgrind 抱怨 C++ 结构上的未初始化字节【英文标题】:valgrind complains about uninitialized bytes on C++ structure 【发布时间】:2019-11-20 16:03:34 【问题描述】:

我通过问题提炼出一个非常简单的示例,其中我有一个正在初始化所有内容的结构构造函数,但 valgrind 抱怨未初始化的字节。罪魁祸首似乎是该类的布尔成员,这导致在 size_t 成员之前插入填充字节。初始化这些填充字节以使 valgrind 不会抱怨的正确方法是什么?

#include <iostream>
#include <cstring>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#define MAX 128

typedef struct foo_struct

  foo_struct(const std::string& name, bool q = true) : q(q)
  
    if (name.size() > MAX)
    
      throw std::runtime_error("too big");
    
    point_name_size = name.size();
    memset(n, 0, sizeof(n));
    memcpy(n, name.c_str(), name.size());
  

  bool q;
  size_t point_name_size;
  char n[MAX];
 foo_t;

int main()

  int fd = open("/tmp/foo", O_WRONLY | O_CREAT, 0666);
  if (-1 == fd)
  
    throw std::runtime_error("Can't create File Descriptor: " + std::string(strerror(errno)));
  

  const foo_t f("hello");
  ssize_t written = write(fd, &f, sizeof(f));
  std::cout << "wrote " << written << std::endl;
  return 0;

编译并运行

g++ try.cpp && valgrind --tool=memcheck --leak-check=yes --show-reachable=yes --num-callers=20  ./a.out

valgrind 错误是

==11790== Syscall param write(buf) points to uninitialised byte(s)
==11790==    at 0x54ED154: write (write.c:27)
==11790==    by 0x1093DE: main (in /home/gri6507/tmp/a.out)
==11790==  Address 0x1fff000251 is on thread 1's stack
==11790==  in frame #1, created by main (???:)

【问题讨论】:

请不要在 c++ 中使用 typedef。 _t 后缀也是保留的。 【参考方案1】:

将数据结构序列化为要写入大容量存储的字节块的正确方法是编写将数据编码为所需格式的序列化程序。依赖标准未指定的内容(例如字节顺序或类型大小)是完全错误的。

有很多可用的序列化库。

【讨论】:

我同意这是正确的方法。但是,在我的实际应用程序中,序列化和反序列化的开销太大了,其中数据实际上并未写入文件,而是通过线程之间的管道进行通信。所以,我的解决方案是将结构更改为union struct unpacked; void * packed 。这样,我可以在联合构造函数期间初始化packed 指针,基于sizeof(unpacked) 将memset 设置为零,而我的序列化程序是packed 指针:-) @PaulGrinberg 当线程共享所有内存时,为什么要在线程之间使用管道进行通信? 我知道线程共享内存。我需要在 libev 事件循环中传递数据并触发事件。最简单的方法之一是使用带有 libev 异步事件的管道在文件句柄上执行非阻塞选择。 @PaulGrinberg 这正是互斥锁和条件变量的用途。【参考方案2】:

初始化这些填充字节以使 valgrind 不会抱怨的正确方法是什么?

我不确定这样做的“正确方法”,但有几种可能性。

    您为填充创建成员并初始化它们
struct bar

  size_t point_name_size;
  char n[MAX];
  bool q;
  bool unused[7] =;
;

这个“通常”的大小与 64 位系统上的结构相同。见here。

    如果你很容易复制,你就用 0 填满自己
struct bar

  bar()
  
    static_assert(std::is_trivially_copyable_v<bar>);
    memset(this, 0, sizeof(bar));      
  

  size_t point_name_size;
  char n[MAX];
  bool q;
;

【讨论】:

以上是关于valgrind 抱怨 C++ 结构上的未初始化字节的主要内容,如果未能解决你的问题,请参考以下文章

使用 new [重复] 实例化的结构上的未初始化内存警告

使用 Valgrind 检测 C++ 内存泄漏

Valgrind C++ 内存泄漏检测

Valgrind C++ 内存泄漏检测

使用 bjam 编译的来自 C++ 的未定义符号

Valgrind报告从stdin的getline后释放的指针上的内存泄漏