能,Memcheck 通过拦截并替换 glibc 的 memcpy/strcpy/strcat 等函数,在检测到 src 与 dst 内存重叠时直接报错终止操作,典型提示为“Source and destination overlap”,但对自实现函数或 memmove 不触发该检查。

Memcheck 能否检测 strcpy/strcat/memcpy 的 src/dst 重叠?
能,但仅限于标准库实现被 Valgrind 替换过的函数(即通过 vg_replace_memcpy 等拦截的版本)。glibc 的 memcpy、strcpy、strcat 等在启用 Memcheck 后会被动态替换,一旦检测到 src 和 dst 地址范围重叠,就会报错。
典型错误信息形如:
==12345== Source and destination overlap in memcpy(0x1FFEFF00A0, 0x1FFEFF00A0, 8)
注意:这不是 warning,而是直接终止当前操作并标记为错误。该检查对 memmove 不触发——因为 memmove 本就允许重叠。
- 只对 libc 提供的符号生效;自己手写的 memcpy 循环不会被拦截
- 必须用
-g编译,否则堆栈里看不到调用点文件行号 -
strncpy若n过大导致越界写入缓冲区,会触发Invalid write,而非重叠警告
为什么 strncpy + memset 组合常被 Memcheck 报“uninitialised value”?
常见模式是:先 strncpy(dst, src, n),再 memset(dst + len, 0, rest),但 len 来自未初始化或截断的 strlen(src),导致 dst 中间部分未被写满,后续又用它做条件判断或传给系统调用。
Memcheck 不关心你“想不想初始化”,只看执行路径中是否读了未写过的字节。例如:
char buf[256];<br>strncpy(buf, input, sizeof(buf)-1);<br>// buf[sizeof(buf)-1] 未被 strncpy 写入(除非 input 长度刚好 >= sizeof(buf)-1)<br>if (buf[255] == '\0') { ... } // ← Conditional jump depends on uninitialised value
- 永远用
memset(buf, 0, sizeof(buf))初始化缓冲区,再strncpy - 避免依赖
strncpy的“自动补零”行为——它只在源字符串短于n时才补零,且最多补一个 -
--track-origins=yes可定位未初始化值源头,但会让运行慢 2–3 倍
strstr/strchr 返回 NULL 后直接解引用,Memcheck 会报什么?
不会直接报“NULL dereference”,因为 Memcheck 不拦截指针解引用本身;但它大概率会紧跟着报 Invalid read of size X——因为你用 NULL 当作有效地址去读了。
例如:
char *p = strstr(s, "key");<br>if (*p == 'k') { ... } // p 为 NULL 时触发
输出类似:
==67890== Invalid read of size 1<br>==67890== at 0x4005AB: main (test.c:12)<br>==67890== Address 0x0 is not stack'd, malloc'd or free'd
- 这种错误本质是逻辑缺陷,不是内存管理问题,但 Memcheck 仍能暴露
- 所有字符串查找函数返回值都必须判空,不能假设一定非 NULL
- Clang 的
-fanalyzer或 GCC 的-Wnonnull可在编译期捕获部分此类问题
用 strdup 或 strndup 后忘记 free,Memcheck 怎么分类泄漏?
这类泄漏会被归为 definitely lost:指针离开作用域后无任何副本指向该内存块。
例如:
void f() {<br> char *s = strdup("hello");<br> // 没有 free(s)<br>} // s 是栈变量,出作用域即销毁,堆内存彻底丢失
关键点:
-
strdup和strndup内部调用malloc,必须配对free,不能用delete - 若把返回指针存进全局链表或传给回调函数,可能变成
indirectly lost,需顺藤摸瓜查引用链 -
--leak-check=full和--show-leak-kinds=all才能看到全部分类,缺省只报definitely和indirectly
字符串操作相关的内存错误往往藏在“看起来安全”的组合调用里,比如 strncpy + strlen + memcmp,或者 strtok 后反复用原指针。Memcheck 不会告诉你“这里该用 strnlen”,但它会坚定指出哪一行读了不该读的字节——盯住 Invalid read 和 Conditional jump on uninitialised value 这两类输出,比看泄漏报告更能提前掐灭 bug。











