[英]Valgrind is complaining of memory leaks
I have a C program that takes flags and files on the command line. 我有一个C程序,在命令行上接收标志和文件。 In order to process the files I put them in a dynamically allocated array of type
char**
it is when working with this array that Valgrind complains 为了处理文件,我将它们放置在
char**
类型的动态分配数组中,Valgrind抱怨使用该数组时
==14389== Memcheck, a memory error detector
==14389== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==14389== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==14389== Command: ./main
==14389==
==14389== Conditional jump or move depends on uninitialised value(s)
==14389== at 0x4838931: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==14389== by 0x10931B: Options_append_filename (main.c:53)
==14389== by 0x1094B9: parse_cmdline (main.c:85)
==14389== by 0x109544: main (main.c:98)
==14389==
==14389== Invalid write of size 8
==14389== at 0x10934A: Options_append_filename (main.c:54)
==14389== by 0x1094B9: parse_cmdline (main.c:85)
==14389== by 0x109544: main (main.c:98)
==14389== Address 0x4a53090 is 0 bytes inside a block of size 1 alloc'd
==14389== at 0x483998B: realloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==14389== by 0x1092DA: Options_append_filename (main.c:44)
==14389== by 0x1094B9: parse_cmdline (main.c:85)
==14389== by 0x109544: main (main.c:98)
==14389==
==14389== Use of uninitialised value of size 8
==14389== at 0x483A9B8: strcpy (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==14389== by 0x109373: Options_append_filename (main.c:55)
==14389== by 0x1094B9: parse_cmdline (main.c:85)
==14389== by 0x109544: main (main.c:98)
==14389==
==14389== Use of uninitialised value of size 8
==14389== at 0x483A9C7: strcpy (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==14389== by 0x109373: Options_append_filename (main.c:55)
==14389== by 0x1094B9: parse_cmdline (main.c:85)
==14389== by 0x109544: main (main.c:98)
==14389==
==14389== Conditional jump or move depends on uninitialised value(s)
==14389== at 0x483A9D9: strcpy (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==14389== by 0x109373: Options_append_filename (main.c:55)
==14389== by 0x1094B9: parse_cmdline (main.c:85)
==14389== by 0x109544: main (main.c:98)
==14389==
==14389== Conditional jump or move depends on uninitialised value(s)
==14389== at 0x483A9E3: strcpy (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==14389== by 0x109373: Options_append_filename (main.c:55)
==14389== by 0x1094B9: parse_cmdline (main.c:85)
==14389== by 0x109544: main (main.c:98)
==14389==
==14389== Conditional jump or move depends on uninitialised value(s)
==14389== at 0x483A9E5: strcpy (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==14389== by 0x109373: Options_append_filename (main.c:55)
==14389== by 0x1094B9: parse_cmdline (main.c:85)
==14389== by 0x109544: main (main.c:98)
==14389==
==14389== Conditional jump or move depends on uninitialised value(s)
==14389== at 0x483A9F4: strcpy (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==14389== by 0x109373: Options_append_filename (main.c:55)
==14389== by 0x1094B9: parse_cmdline (main.c:85)
==14389== by 0x109544: main (main.c:98)
==14389==
==14389== Invalid write of size 8
==14389== at 0x10938D: Options_append_filename (main.c:56)
==14389== by 0x1094B9: parse_cmdline (main.c:85)
==14389== by 0x109544: main (main.c:98)
==14389== Address 0x4a53098 is 7 bytes after a block of size 1 alloc'd
==14389== at 0x483998B: realloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==14389== by 0x1092DA: Options_append_filename (main.c:44)
==14389== by 0x1094B9: parse_cmdline (main.c:85)
==14389== by 0x109544: main (main.c:98)
==14389==
==14389== Conditional jump or move depends on uninitialised value(s)
==14389== at 0x1095AE: main (main.c:113)
==14389==
==14389== Invalid read of size 8
==14389== at 0x1095A8: main (main.c:113)
==14389== Address 0x4a53098 is 7 bytes after a block of size 1 alloc'd
==14389== at 0x483998B: realloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==14389== by 0x1092DA: Options_append_filename (main.c:44)
==14389== by 0x1094B9: parse_cmdline (main.c:85)
==14389== by 0x109544: main (main.c:98)
==14389==
==14389== Conditional jump or move depends on uninitialised value(s)
==14389== at 0x48C2F73: vfprintf (vfprintf.c:1638)
==14389== by 0x48CB995: printf (printf.c:33)
==14389== by 0x1095E2: main (main.c:117)
==14389==
==14389== Use of uninitialised value of size 8
==14389== at 0x483A852: strlen (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==14389== by 0x48C431D: vfprintf (vfprintf.c:1638)
==14389== by 0x48CB995: printf (printf.c:33)
==14389== by 0x1095E2: main (main.c:117)
==14389==
==14389== Use of uninitialised value of size 8
==14389== at 0x483A864: strlen (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==14389== by 0x48C431D: vfprintf (vfprintf.c:1638)
==14389== by 0x48CB995: printf (printf.c:33)
==14389== by 0x1095E2: main (main.c:117)
==14389==
==14389== Conditional jump or move depends on uninitialised value(s)
==14389== at 0x48F1993: _IO_file_xsputn@@GLIBC_2.2.5 (fileops.c:1224)
==14389== by 0x48C3018: vfprintf (vfprintf.c:1638)
==14389== by 0x48CB995: printf (printf.c:33)
==14389== by 0x1095E2: main (main.c:117)
==14389==
==14389== Use of uninitialised value of size 8
==14389== at 0x48F1995: _IO_file_xsputn@@GLIBC_2.2.5 (fileops.c:1226)
==14389== by 0x48C3018: vfprintf (vfprintf.c:1638)
==14389== by 0x48CB995: printf (printf.c:33)
==14389== by 0x1095E2: main (main.c:117)
==14389==
==14389== Conditional jump or move depends on uninitialised value(s)
==14389== at 0x48F19A6: _IO_file_xsputn@@GLIBC_2.2.5 (fileops.c:1224)
==14389== by 0x48C3018: vfprintf (vfprintf.c:1638)
==14389== by 0x48CB995: printf (printf.c:33)
==14389== by 0x1095E2: main (main.c:117)
==14389==
==14389== Use of uninitialised value of size 8
==14389== at 0x48F19A8: _IO_file_xsputn@@GLIBC_2.2.5 (fileops.c:1226)
==14389== by 0x48C3018: vfprintf (vfprintf.c:1638)
==14389== by 0x48CB995: printf (printf.c:33)
==14389== by 0x1095E2: main (main.c:117)
==14389==
==14389== Conditional jump or move depends on uninitialised value(s)
==14389== at 0x48F19D0: _IO_file_xsputn@@GLIBC_2.2.5 (fileops.c:1224)
==14389== by 0x48C3018: vfprintf (vfprintf.c:1638)
==14389== by 0x48CB995: printf (printf.c:33)
==14389== by 0x1095E2: main (main.c:117)
==14389==
==14389== Use of uninitialised value of size 8
==14389== at 0x48F19C4: _IO_file_xsputn@@GLIBC_2.2.5 (fileops.c:1226)
==14389== by 0x48C3018: vfprintf (vfprintf.c:1638)
==14389== by 0x48CB995: printf (printf.c:33)
==14389== by 0x1095E2: main (main.c:117)
==14389==
==14389== Conditional jump or move depends on uninitialised value(s)
==14389== at 0x483EA3F: mempcpy (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==14389== by 0x48F1951: _IO_file_xsputn@@GLIBC_2.2.5 (fileops.c:1243)
==14389== by 0x48C3018: vfprintf (vfprintf.c:1638)
==14389== by 0x48CB995: printf (printf.c:33)
==14389== by 0x1095E2: main (main.c:117)
==14389==
==14389== Conditional jump or move depends on uninitialised value(s)
==14389== at 0x483EA57: mempcpy (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==14389== by 0x48F1951: _IO_file_xsputn@@GLIBC_2.2.5 (fileops.c:1243)
==14389== by 0x48C3018: vfprintf (vfprintf.c:1638)
==14389== by 0x48CB995: printf (printf.c:33)
==14389== by 0x1095E2: main (main.c:117)
==14389==
==14389== Conditional jump or move depends on uninitialised value(s)
==14389== at 0x483EA5C: mempcpy (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==14389== by 0x48F1951: _IO_file_xsputn@@GLIBC_2.2.5 (fileops.c:1243)
==14389== by 0x48C3018: vfprintf (vfprintf.c:1638)
==14389== by 0x48CB995: printf (printf.c:33)
==14389== by 0x1095E2: main (main.c:117)
==14389==
==14389== Use of uninitialised value of size 8
==14389== at 0x483EA70: mempcpy (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==14389== by 0x48F1951: _IO_file_xsputn@@GLIBC_2.2.5 (fileops.c:1243)
==14389== by 0x48C3018: vfprintf (vfprintf.c:1638)
==14389== by 0x48CB995: printf (printf.c:33)
==14389== by 0x1095E2: main (main.c:117)
==14389==
==14389== Use of uninitialised value of size 8
==14389== at 0x483EA81: mempcpy (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==14389== by 0x48F1951: _IO_file_xsputn@@GLIBC_2.2.5 (fileops.c:1243)
==14389== by 0x48C3018: vfprintf (vfprintf.c:1638)
==14389== by 0x48CB995: printf (printf.c:33)
==14389== by 0x1095E2: main (main.c:117)
==14389==
opts.files[0] is: './main'
==14389==
==14389== HEAP SUMMARY:
==14389== in use at exit: 8 bytes in 2 blocks
==14389== total heap usage: 4 allocs, 2 frees, 1,040 bytes allocated
==14389==
==14389== 1 bytes in 1 blocks are definitely lost in loss record 1 of 2
==14389== at 0x483998B: realloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==14389== by 0x1092DA: Options_append_filename (main.c:44)
==14389== by 0x1094B9: parse_cmdline (main.c:85)
==14389== by 0x109544: main (main.c:98)
==14389==
==14389== 7 bytes in 1 blocks are definitely lost in loss record 2 of 2
==14389== at 0x483774F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==14389== by 0x109349: Options_append_filename (main.c:54)
==14389== by 0x1094B9: parse_cmdline (main.c:85)
==14389== by 0x109544: main (main.c:98)
==14389==
==14389== LEAK SUMMARY:
==14389== definitely lost: 8 bytes in 2 blocks
==14389== indirectly lost: 0 bytes in 0 blocks
==14389== possibly lost: 0 bytes in 0 blocks
==14389== still reachable: 0 bytes in 0 blocks
==14389== suppressed: 0 bytes in 0 blocks
==14389==
==14389== For counts of detected and suppressed errors, rerun with: -v
==14389== Use --track-origins=yes to see where uninitialised values come from
==14389== ERROR SUMMARY: 48 errors from 27 contexts (suppressed: 0 from 0)
I have tried playing around with initializing the array with data etc. 我试着用数据等初始化数组
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
typedef struct {
bool help_flag;
bool version_flag;
char **files;
} Options;
Options Options_new() {
Options opts;
opts.help_flag = false;
opts.version_flag = false;
opts.files = malloc(sizeof(NULL));
opts.files = NULL;
return opts;
}
void Options_cleanup_mem(Options *opts) {
int i = 0;
while (opts->files[i] != NULL)
i++;
for (int j = 0; j < i + 1; j++) {
free(opts->files[j]);
}
}
int Options_append_filename(Options *opts, char *filename) {
int num_files = 0;
char **tmp;
while (opts->files[num_files] != NULL)
num_files++;
tmp = realloc(opts->files, num_files + 1);
/* Handle case where realloc can't allocate memory */
if (tmp == NULL) {
return -1;
}
opts->files = tmp;
free(opts->files[num_files]);
opts->files[num_files] = malloc(strlen(filename) + 1);
strcpy(opts->files[num_files], filename);
opts->files[num_files + 1] = NULL;
return 0;
}
Options parse_cmdline(int argc, char **argv) {
Options opts = Options_new();
for (int a = 0; a < argc; a++) {
if (argv[a][0] == '-') {
char *longopt = argv[a];
if (!strcmp(longopt, "--help")) {
opts.help_flag = true;
} else if (!strcmp(longopt, "--version")) {
opts.version_flag = true;
} else {
fprintf(stderr, "Unkown option\n");
Options_cleanup_mem(&opts);
exit(1);
}
} else {
if (Options_append_filename(&opts, argv[a]) != 0) {
fprintf(stderr, "Error processing arguments\n");
Options_cleanup_mem(&opts);
}
}
}
return opts;
}
int main(int argc, char **argv) {
Options opts = parse_cmdline(argc, argv);
if (opts.help_flag) {
printf("Help message\n");
return 2;
}
if (opts.version_flag) {
printf("Version message\n");
return 2;
}
int i = 0;
while (opts.files[i] != NULL)
i++;
for (int a = 0; a < i; a++)
printf("opts.files[%d] is: '%s'\n", a, opts.files[a]);
return 0;
}
There is at least one memory leak here: 这里至少有一个内存泄漏:
opts.files = malloc(sizeof(NULL));
opts.files = NULL;
You allocate memory for the size of NULL
, which is meaningless as NULL
can be defined as 0
or ((void*)0)
which may have different sizes, and you immediately overwrite this pointer with opts.files = NULL
, making the allocated block unreachable. 您为
NULL
的大小分配内存是没有意义的,因为NULL
可以定义为0
或((void*)0)
,它们的大小可能不同,然后立即用opts.files = NULL
覆盖此指针,从而使分配的块成为可能无法到达。 You should allocate space for one pointer and initialize it to NULL
. 您应该为一个指针分配空间并将其初始化为
NULL
。
There is another leak in Options_cleanup_mem
: you forget to free the array pointed to by opts->files
. Options_cleanup_mem
还有另一个泄漏:您忘记释放opts->files
指向的数组。
The size passed in tmp = realloc(opts->files, num_files + 1);
在
tmp = realloc(opts->files, num_files + 1);
传递的大小tmp = realloc(opts->files, num_files + 1);
is incorrect, you should mutiply the number of elements by the element size sizeof(opts->files[0])
. 是不正确的,您应该根据元素大小
sizeof(opts->files[0])
元素数量。 Furthermore, the new number of elements is num_files + 2
, including the new filename and the null terminator. 此外,新的元素数是
num_files + 2
,包括新的文件名和空终止符。
The line free(opts->files[num_files]);
该行
free(opts->files[num_files]);
is redundant in Options_append_filename
since opts->files[num_files]
is a NULL
pointer. 在
Options_append_filename
是多余的,因为opts->files[num_files]
是NULL
指针。
You should exit the program in all error cases in parse_cmdline()
. 在所有错误情况下,您都应该在
parse_cmdline()
退出程序。
You should free the allocated memory before returning from main()
with Options_cleanup_mem(&opts);
在使用
Options_cleanup_mem(&opts);
从main()
返回之前,应该释放分配的内存Options_cleanup_mem(&opts);
so valgrind does not diagnose unfreed memory. 因此valgrind无法诊断未释放的内存。
Here is a modified version: 这是修改后的版本:
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
typedef struct {
bool help_flag;
bool version_flag;
char **files;
} Options;
Options Options_new(void) {
Options opts;
opts.help_flag = false;
opts.version_flag = false;
opts.files = malloc(sizeof(opts.files[0]));
opts.files[0] = NULL;
return opts;
}
void Options_cleanup_mem(Options *opts) {
int i = 0;
if (opts->files) {
while (opts->files[i] != NULL)
free(opts->files[i++]);
free(opts->files);
}
}
int Options_append_filename(Options *opts, const char *filename) {
int num_files = 0;
char **tmp;
while (opts->files[num_files] != NULL)
num_files++;
tmp = realloc(opts->files, sizeof(opts->files[0]) * (num_files + 2));
/* Handle case where realloc can't allocate memory */
if (tmp == NULL) {
return -1;
}
opts->files = tmp;
opts->files[num_files] = malloc(strlen(filename) + 1);
strcpy(opts->files[num_files], filename);
opts->files[num_files + 1] = NULL;
return 0;
}
Options parse_cmdline(int argc, char **argv) {
Options opts = Options_new();
for (int a = 0; a < argc; a++) {
if (argv[a][0] == '-') {
char *longopt = argv[a];
if (!strcmp(longopt, "--help")) {
opts.help_flag = true;
} else if (!strcmp(longopt, "--version")) {
opts.version_flag = true;
} else {
fprintf(stderr, "Unkown option\n");
Options_cleanup_mem(&opts);
exit(1);
}
} else {
if (Options_append_filename(&opts, argv[a]) != 0) {
fprintf(stderr, "Error processing arguments\n");
Options_cleanup_mem(&opts);
exit(1);
}
}
}
return opts;
}
int main(int argc, char **argv) {
Options opts = parse_cmdline(argc, argv);
if (opts.help_flag) {
printf("Help message\n");
return 2;
}
if (opts.version_flag) {
printf("Version message\n");
return 2;
}
int i = 0;
while (opts.files[i] != NULL)
i++;
for (int a = 0; a < i; a++)
printf("opts.files[%d] is: '%s'\n", a, opts.files[a]);
Options_cleanup_mem(&opts);
return 0;
}
Notes: 笔记:
Options
structure by value. Options
结构时,很难处理内存分配失败。 Options_new
should take a pointer and return an error status. Options_new
应该使用一个指针并返回错误状态。 realloc()
failure, but not for other malloc()
failures. realloc()
失败,但不测试其他malloc()
失败。 Options
structure to avoid recomputing it everywhere. Options
结构中,以避免在各处重新计算。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.