2017-03-08 113 views
-2

我有一个程序,我正在阅读的空间分隔的30,000个数字(每行5个)的文本文件中。当我运行该程序时,有时它可以工作,但有时它不会(seg故障),我相信这意味着我有内存泄漏。Valgrind修复内存泄漏

我有以下几点:

int main(int argc, char const *argv[]) 
{ 

    char input[256]; 
    char buffer[30000]; 
    char **nums = NULL; 
    nums = malloc(sizeof(char) * 30000); 

    char *token; 
    int counter = 0; 
    FILE *fp; 
    fp = fopen("textfile.txt","r"); 

    fgets(input,sizeof(input),stdin); 

    while (fgets(buffer,sizeof(buffer),fp) != NULL) 
    { 
     token = strtok(buffer," "); 
     nums[counter] = malloc(sizeof(char) * 50); 

     while (token != NULL) 
     { 
      if (strlen(token) > 1) 
      { 
       strcpy(nums[counter],token); 
       counter++; 
      } 
      token = strtok(NULL," "); 
     } 
    } 

    for (int x = 0; x < 30000;x++) 
    { 
     free(nums[x]); 
    } 

    free(nums); 
    fclose(fp); 

    return 0; 
} 

我运行的valgrind尝试调试,但我有读取输出的问题。有人能告诉我我要去哪里吗?

==24368== Use of uninitialised value of size 8 
==24368== at 0x4C2588C: strcpy (mc_replace_strmem.c:311) 
==24368== by 0x400820: main (in /home/a.out) 
==24368== 
==24368== Invalid write of size 1 
==24368== at 0x4C2588C: strcpy (mc_replace_strmem.c:311) 
==24368== by 0x400820: main (in /home/a.out) 
==24368== Address 0x0 is not stack'd, malloc'd or (recently) free'd 
==24368== 
+0

如果打开调试符号,将编译后的代码注释为诸如函数名称,文件名和行号之类的东西,则valgrind输出将包含文件和行号。对于大多数编译器来说,它的'-g'。 – Schwern

+0

你甚至没有为'counter'th标记分配内存,除了每行的第一次迭代。另外,因为这些是*数字*,正如您所说,我不确定为什么要将* strings *存储在数组中? –

+0

目前还不清楚您是读30000或6000行(每行5个数字)。为什么需要存储6000(30000?)行,而不是30000?这是一个X-Y问题吗?你真的想要一个二维数组''[6000] [5]'这些数字吗?为什么你扔掉第一行,是列标题? –

回答

1

如果您使用调试符号进行编译,通常使用-g,那么valgrind将在其输出中包含文件和行号,以便更容易理解。


@Jean-FrançoisFabre already found one malloc problem,你的while循环包含更多。

while (fgets(buffer,sizeof(buffer),fp) != NULL) 
{ 
    token = strtok(buffer," "); 
    nums[counter] = malloc(sizeof(char) * 50); 

你总是nums[counter]分配内存,但...

while (token != NULL) 
    { 
     if (strlen(token) > 1) 
     { 
      strcpy(nums[counter],token); 
      counter++; 

你只是偶尔增加counter。你会泄漏很多内存。

你也复制token,这可能是高达29998个字节,到nums[counter]这仅仅是50

 } 
     token = strtok(NULL," "); 
    } 
} 

相反,因为它需要分配nums[counter]和右边的大小。

while (fgets(buffer,sizeof(buffer),fp) != NULL) 
{ 
    token = strtok(buffer," "); 

    while (token != NULL) 
    { 
     if (strlen(token) > 1) 
     { 
      nums[counter] = malloc(sizeof(char) * (strlen(token) + 1)); 
      strcpy(nums[counter],token); 
      counter++; 
     } 
     token = strtok(NULL," "); 
    } 
} 

或者,如果您不介意使用POSIX功能,请使用strdup

nums[counter] = strdup(token); 

而且,正如其他人在评论中指出的那样,为什么要将数字存储为字符串,这是值得商榷的。使用strtolstrtod立即将它们转换为数字并将其存储。它更简单,消耗更少的内存。

long nums[30000]; 

... 

char *end; 
nums[counter] = strtol(token, &end, 10); 
if(end != '\0') { 
    fprintf(stderr, "Couldn't understand %s\n", token); 
} 

end是一个指针,指向点上token其中strtol停止解析。检查它是否为空字节需要token必须只包含数字。您希望与您的转换有多严格取决于您。

4
nums = malloc(sizeof(char) * 30000); 

将坚持不住了数30000个指针。规模过小,应该是:

nums = malloc(sizeof(char*) * 30000); 

一旁,而不是做:

token = strtok(buffer," "); 
nums[counter] = malloc(sizeof(char) * 50); 

你应该做的:

token = strtok(buffer," "); 
nums[counter] = malloc(strlen(token)+1); 

所以你分配的内存适量每个令牌(不要太多,但不能太少),并注意sizeof(char)总是1,所以省略它。