Valgrind 在尝试将字符串复制到结构成员变量时警告重叠

Posted

技术标签:

【中文标题】Valgrind 在尝试将字符串复制到结构成员变量时警告重叠【英文标题】:Valgrind warns of overlap when trying to copy a string into a struct member variable 【发布时间】:2021-12-29 15:57:38 【问题描述】:

这是结构的外观供参考:

struct thread_data 
    struct ringbuf_t *rb;
    char *file_name;
;

我需要获取命令行参数并将其存储在 threads 数组中每个 thread_data 元素的结构成员变量中,如下所示:

for (int index = optind; index < argc; index++) 
        threads[length].rb = rb;
        memmove(&threads[length].file_name, &argv[index], strlen(argv[index]));
        strcpy(threads[length].file_name, argv[index]);
        ++length;
    

以前使用过memcpy,当我打印变量时它起作用了。然而,Valgrind 给了我这个:

==465645== Source and destination overlap in strcpy(0x1fff000b54, 0x1fff000b54)
==465645==    at 0x4C3C180: strcpy (vg_replace_strmem.c:523)
==465645==    by 0x400F85: main (bytemincer.c:55)

所以我使用了memmove,我仍然得到了相同的 Valgrind 结果。有什么解决办法吗?

【问题讨论】:

您确定要复制到&amp;threads[length].file_name 而不是threads[length].file_namechar *file_name; 成员是一个指针。 ...第二个参数也有同样的问题。你为什么要使用 strcpy 来跟进 memmove? @WeatherVane 我想复制argue[index]的实际值。 memmove 不是在移动字节,所以我需要那个变量所在的地址吗? 要复制argv[index] 的值(指针又名地址),您只需要threads[length].file_name = argv[index];。但是,如果您想将指向的字符从一个缓冲区复制到另一个缓冲区,那么您需要 memmove(threads[length].file_name, argv[index], strlen(argv[index]));strcpy(threads[length].file_name, argv[index]); 简短回答:只需threads[length].file_name = argv[index];(相当于您拥有的memmove)。另外,最好将其设为const char* 【参考方案1】:

这就是你想要的结果:

(我在帖子中使用“fn”而不是“file_name”。)

                                      *(argv[0]) @ 0x2000
                                      +---+---+- -+---+
                     +--------------->|   |   | … | 0 |
argv @ 0x1000        |                +---+---+- -+---+
+---------------+    |            
| 0x2000      -------+                *(argv[1]) @ 0x2100
+---------------+                     +---+---+- -+---+
| 0x2100      -----------+----------->|   |   | … | 0 |
+---------------+        |            +---+---+- -+---+
| 0x2200      -----------)----+
+---------------+        |   |        *(argv[2]) @ 0x2200
| ⋮             |        |   |        +---+---+- -+---+
                         |   +------->|   |   | … | 0 |
rb @ 0x3000              |   |        +---+---+- -+---+
+---------------+        |   |
| 0x4000      -------+   |   |        *rb @ 0x4000
+---------------+    |   |   |        +---------------+
                     +---)---)------->|               |
threads @ 0x5000     |   |   |        +---------------+
+---------------+    |   |   |    
|  +-----------+|    |   |   |    
|rb| 0x4000  --------+   |   |
|  +-----------+|    |   |   |
|fn| 0x2100  --------)---+   |
|  +-----------+|    |       |
+---------------+    |       |
|  +-----------+|    |       |
|rb| 0x4000  --------+       |
|  +-----------+|            |
|fn| 0x2200  ----------------+
|  +-----------+|
+---------------+
| ⋮             |

(假设threads 是一个数组,而不是指向数组的指针。这不会影响帖子的其余部分。)

当然,所有地址都是虚构的。但是您可以看到不止一次变量具有相同的值地址。因为让多个指针指向同一个内存块是非常好的。我们需要做的就是复制指针(地址)。

要复制一个指针,你需要做的就是

dst = src;

所以你只需要

threads[length].rb = rb;
threads[length].fn = argv[index];

虽然

memmove(&threads[length].rb, &rb,          sizeof(threads[length].rb));
memmove(&threads[length].fn, &argv[index], sizeof(threads[length].fn));

memmove(&threads[length].rb, &rb,          sizeof(rb));
memmove(&threads[length].fn, &argv[index], sizeof(argv[index]));

相当于赋值,做这么复杂的事情没有意义:

(注意使用sizeof(argv[index])而不是strlen(argv[index])。这是我们要复制的指针,所以我们需要指针的大小。)

警告来自尝试将缓冲区中 0x2100 处的字符串复制到 0x2100 处的缓冲区中。请记住threads[length].fnargv[index]memmove 之后具有相同的值(地址)。

【讨论】:

以上是关于Valgrind 在尝试将字符串复制到结构成员变量时警告重叠的主要内容,如果未能解决你的问题,请参考以下文章

如何将嵌套结构的成员复制到 CUDA 设备的内存空间?

将字符串复制到 *struct 成员

gcc,c++:静态字符串成员变量导致堆损坏/分段错误

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

如何将sklearn Pipeline结构的结构和数据深度复制到新变量中?

将一个结构的成员复制到另一个 C#