今天我正在编写一些东西,在我完成之后,我与valgrind
进行了检查,我得到了一个惊喜。
如果我使用gcc-4.9.2在我的Ubuntu(15.04 64BIT)上编译我的程序,其中包含以下内容:
gcc -Wextra -Werror -Wstrict-prototypes -Wconversion --std=c11 -O2 -g program.c -o program
然后运行valgrind:
valgrind --leak-check=full --track-origins=yes ./program
我得到以下输出:
==5325== Memcheck, a memory error detector ==5325== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al. ==5325== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info ==5325== Command: ./program ==5325== Bye ==5325== ==5325== HEAP SUMMARY: ==5325== in use at exit: 33 bytes in 1 blocks ==5325== total heap usage: 1 allocs, 0 frees, 33 bytes allocated ==5325== ==5325== 33 bytes in 1 blocks are definitely lost in loss record 1 of 1 ==5325== at 0x4C2BBA0: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) ==5325== by 0x4004BD: main (program.c:11) ==5325== ==5325== LEAK SUMMARY: ==5325== definitely lost: 33 bytes in 1 blocks ==5325== indirectly lost: 0 bytes in 0 blocks ==5325== possibly lost: 0 bytes in 0 blocks ==5325== still reachable: 0 bytes in 0 blocks ==5325== suppressed: 0 bytes in 0 blocks ==5325== ==5325== For counts of detected and suppressed errors, rerun with: -v ==5325== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
正如您所看到的那样泄漏,但看看如果我使用gcc-5.2.0编译以下内容会发生什么:
./install/gcc-5.2.0/bin/gcc5.2 -Wextra -Werror -Wstrict-prototypes -Wconversion --std=c11 -O2 -g program.c -o program
现在valgrind说:
==5344== Memcheck, a memory error detector ==5344== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al. ==5344== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info ==5344== Command: ./program ==5344== Bye ==5344== ==5344== HEAP SUMMARY: ==5344== in use at exit: 0 bytes in 0 blocks ==5344== total heap usage: 0 allocs, 0 frees, 0 bytes allocated ==5344== ==5344== All heap blocks were freed -- no leaks are possible ==5344== ==5344== For counts of detected and suppressed errors, rerun with: -v ==5344== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
正如您所看到的,总堆使用量:0个分配,0个释放,0个字节分配
我尝试过的代码如下:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main(void){
int a = 0;
size_t len1 = 0, len2 = 0;
char *string1 = "Hello";
char *string2;
string2 = malloc(33);
strcpy(string2, "Hello");
len1 = strlen(string1);
len2 = strlen(string2);
if(len1 != len2){
a = 5;
}else{
a=4;
}
while (a != -1){
if(a == 2){
break;
}
a--;
}
printf("Bye\n");
/*free(string2);*/
return 0;
}
安装了GCC-5.2.0 using this method。
现在我的问题是:是GCC还是valgrind有错?为什么会发生这种情况,我该如何避免呢?
最后一件事,如果我改变:
printf("Bye\n");
到此:
printf("String2 = %s\n",string2);
发现泄漏:
==5443== Memcheck, a memory error detector ==5443== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al. ==5443== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info ==5443== Command: ./program ==5443== String2 = Hello ==5443== ==5443== HEAP SUMMARY: ==5443== in use at exit: 33 bytes in 1 blocks ==5443== total heap usage: 1 allocs, 0 frees, 33 bytes allocated ==5443== ==5443== 33 bytes in 1 blocks are definitely lost in loss record 1 of 1 ==5443== at 0x4C2BBA0: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) ==5443== by 0x40044D: main (program.c:11) ==5443== ==5443== LEAK SUMMARY: ==5443== definitely lost: 33 bytes in 1 blocks ==5443== indirectly lost: 0 bytes in 0 blocks ==5443== possibly lost: 0 bytes in 0 blocks ==5443== still reachable: 0 bytes in 0 blocks ==5443== suppressed: 0 bytes in 0 blocks ==5443== ==5443== For counts of detected and suppressed errors, rerun with: -v ==5443== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
这让我问自己为什么?不知何故,printf()有助于这个故事。
答案 0 :(得分:2)
似乎GCC 5.2.0能够通过string2
检测到"Hello"
是常数strcpy
。所以它只是优化string2
而不在HEAP中分配新的内存块。我的猜测是string.h
在标题本身中实现了strcpy
和strlen
。
检测内存泄漏的最佳方法是在不进行优化的情况下进行编译。尝试使用-O0
而不是-O2
重新编译它。在这种情况下,编译器将尽可能靠近源代码创建二进制文件。
有了这个:
printf(“String2 =%s \ n”,string2);
发现泄漏:
这似乎编译器检测到对string2
的依赖,因此它不会优化它。 可能是因为printf
的实现在源代码的编译时不可用,或者因为printf
使用了可变参数变量。但这只是我猜...
答案 1 :(得分:2)
继续我们对Will C automatically free memory with no pointers?中的评论的讨论,valgrind
输出的差异是编译器优化-O2
优化代码分配的结果。为什么?我们来看看你的代码:
string2 = malloc(33);
strcpy (string2, "Hello");
...
printf("Bye\n");
虽然您已为string2
分配了内存,并且已将"Hello"
复制到sting2
,但您从未在其余部分中使用 string2
码。由于没有后续操作依赖于string2
指向的内存或其中包含的值,编译器可以完全从最终的可执行文件中删除该代码。
在“optimize”中,编译器会寻找可以使用更少的指令更有效地运行代码的方法,同时仍然提供相同的功能。由于没有任何东西依赖于与string2
相关联的内存或值,因此编译器只是简单地断定代码可以更快地运行并且在更少的指令中如果它只是忽略分配并完全复制。
(这就是为什么当你使用printf
调用string2
时,在另一个答案中建议出现泄漏,编译器不能简单地优化分配并复制掉,因为printf
取决于关于string2
)的记忆和价值
验证发生了什么的关键是查看编译器生成的汇编代码(gcc -S
生成汇编文件,添加选项-masm=intel
来告诉编译器以intel
格式输出汇编而不是ATT
)
让我们从禁用优化-O0
开始。组装的重要部分是:
.cfi_startproc
push rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
mov rbp, rsp
.cfi_def_cfa_register 6
sub rsp, 48
mov DWORD PTR [rbp-4], 0
mov QWORD PTR [rbp-16], 0
mov QWORD PTR [rbp-24], 0
mov QWORD PTR [rbp-32], OFFSET FLAT:.LC0
mov QWORD PTR [rbp-40], 0
mov edi, 33
call malloc ; the call to malloc is retained
mov QWORD PTR [rbp-40], rax
mov rax, QWORD PTR [rbp-40]
mov DWORD PTR [rax], 1819043144
mov WORD PTR [rax+4], 111
mov rax, QWORD PTR [rbp-32]
mov rdi, rax
call strlen
mov QWORD PTR [rbp-16], rax
mov rax, QWORD PTR [rbp-40]
mov rdi, rax
call strlen
mov QWORD PTR [rbp-24], rax
mov rax, QWORD PTR [rbp-16]
cmp rax, QWORD PTR [rbp-24]
je .L2
mov DWORD PTR [rbp-4], 5
jmp .L4
现在,让我们看看优化版本(gcc (GCC) 6.1.1 20160602
使用-Ofast
优化):
.cfi_startproc
sub rsp, 8
.cfi_def_cfa_offset 16
mov edi, OFFSET FLAT:.LC0
call puts
xor eax, eax
add rsp, 8
.cfi_def_cfa_offset 8
ret
.cfi_endproc
根本没有malloc
- 它已被优化掉了。对于优化应该做的事情而言,它的运行时间要少得多。
现在valgrind如何报告它看到的valgrind
版本之间会有所不同,并且OS之间有所不同,但底线是相同的。如果你声明,分配,但从不使用任何值,编译器可以自由地优化该声明/分配,并且找到正在发生的事情的一种方法是查看汇编文件。如果要重现程序集,则使用的完整编译字符串为:
gcc -S -masm=intel -Ofast -o valgrindtest.asm valgrindtest.c
然后看看valgrindtest.asm
。希望这为你增添了另一个难题。