c - Valgrind 入门(无效写入)

标签 c memory-leaks valgrind

这是我第一次使用 Valgrind,我在弄清楚用 C 编写的简单代码中有什么问题时遇到了一些问题。它所做的只是通过一个文件获取一些字符串,反转它们,然后将它们打印到另一个文件中(或者附加它们,如果我将字符“a”作为第三个参数传递给程序)。这是代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

char* reverse(char*,int);

int main(int argc,char* argv[]) {
    FILE *input, *output; char* s;
    s=malloc(1024*sizeof(char));
    if (argv[3]!=NULL && strcmp(argv[3],"a") == 0) output=fopen(argv[2],"a");
    else output=fopen(argv[2],"w");
    if((input=fopen(argv[1],"r")) == NULL) {
        perror("File inesistente");
        return -1;
    }
    while(fgets(s,1024,input)!=NULL) {
            s = reverse(s,strlen(s));
            fprintf(output,"%s\n",s);
    }
    free(s);
    fclose(input);
    fclose(output);
    return 0;
}

char* reverse(char* c,int l) {
    char* buf; int i; buf=malloc((l)*sizeof(char));
    for(i=0; i<l-1; i++) {
        buf[i]=c[l-2-i];
    }
    buf[l-1]='\0';
    return buf;
}

这就是 Valgrind 告诉我的:

==2768== Memcheck, a memory error detector
==2768== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==2768== Using Valgrind-3.10.0 and LibVEX; rerun with -h for copyright info
==2768== Command: ./tokfile input.txt output.txt
==2768== 
==2768== Invalid write of size 2
==2768==    at 0x4C2FF2B: __GI_memcpy (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==2768==    by 0x4EA6FA3: _IO_getline_info (iogetline.c:105)
==2768==    by 0x4EA5E35: fgets (iofgets.c:56)
==2768==    by 0x40083D: main (tokfile.c:17)
==2768==  Address 0x51fc9d2 is 2 bytes inside a block of size 3 alloc'd
==2768==    at 0x4C2ABA0: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==2768==    by 0x40087D: reverse (tokfile.c:27)
==2768==    by 0x40080A: main (tokfile.c:18)
==2768== 
==2768== Invalid write of size 1
==2768==    at 0x4C2FF63: __GI_memcpy (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==2768==    by 0x4EA6FA3: _IO_getline_info (iogetline.c:105)
==2768==    by 0x4EA5E35: fgets (iofgets.c:56)
==2768==    by 0x40083D: main (tokfile.c:17)
==2768==  Address 0x51fc9d4 is 1 bytes after a block of size 3 alloc'd
==2768==    at 0x4C2ABA0: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==2768==    by 0x40087D: reverse (tokfile.c:27)
==2768==    by 0x40080A: main (tokfile.c:18)
==2768== 
==2768== Invalid write of size 1
==2768==    at 0x4EA5EA2: fgets (iofgets.c:64)
==2768==    by 0x40083D: main (tokfile.c:17)
==2768==  Address 0x51fc9d5 is 2 bytes after a block of size 3 alloc'd
==2768==    at 0x4C2ABA0: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==2768==    by 0x40087D: reverse (tokfile.c:27)
==2768==    by 0x40080A: main (tokfile.c:18)
==2768== 
==2768== Invalid read of size 1
==2768==    at 0x4C2E134: strlen (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==2768==    by 0x4007FA: main (tokfile.c:18)
==2768==  Address 0x51fc9d3 is 0 bytes after a block of size 3 alloc'd
==2768==    at 0x4C2ABA0: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==2768==    by 0x40087D: reverse (tokfile.c:27)
==2768==    by 0x40080A: main (tokfile.c:18)
==2768== 
==2768== Invalid read of size 1
==2768==    at 0x4008AB: reverse (tokfile.c:29)
==2768==    by 0x40080A: main (tokfile.c:18)
==2768==  Address 0x51fc9d3 is 0 bytes after a block of size 3 alloc'd
==2768==    at 0x4C2ABA0: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==2768==    by 0x40087D: reverse (tokfile.c:27)
==2768==    by 0x40080A: main (tokfile.c:18)
==2768== 
==2768== 
==2768== HEAP SUMMARY:
==2768==     in use at exit: 1,044 bytes in 5 blocks
==2768==   total heap usage: 7 allocs, 2 frees, 2,180 bytes allocated
==2768== 
==2768== 20 bytes in 4 blocks are definitely lost in loss record 1 of 2
==2768==    at 0x4C2ABA0: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==2768==    by 0x40087D: reverse (tokfile.c:27)
==2768==    by 0x40080A: main (tokfile.c:18)
==2768== 
==2768== 1,024 bytes in 1 blocks are definitely lost in loss record 2 of 2
==2768==    at 0x4C2ABA0: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==2768==    by 0x40074E: main (tokfile.c:10)
==2768== 
==2768== LEAK SUMMARY:
==2768==    definitely lost: 1,044 bytes in 5 blocks
==2768==    indirectly lost: 0 bytes in 0 blocks
==2768==      possibly lost: 0 bytes in 0 blocks
==2768==    still reachable: 0 bytes in 0 blocks
==2768==         suppressed: 0 bytes in 0 blocks
==2768== 
==2768== For counts of detected and suppressed errors, rerun with: -v
==2768== ERROR SUMMARY: 17 errors from 7 contexts (suppressed: 0 from 0)

我想我以错误的方式分配了内存? fgets有什么问题吗? 谢谢。

最佳答案

对于写字节错误:

您的反转字符串太短。由于空字符串终止符,您必须将 1 添加到 reverse 中分配的缓冲区。

这不是真正的 valgrind 问题(除了结果是错误的),但由于您不这样做,在某些时候您的索引是错误的。例如,如果您尝试反转一个空字符串,

  • 首先你 malloc 零字节,这取决于实现:要么它返回 NULL(然后在写入它时返回 KABOOM)或者指向你不应该写的东西的指针进入 (!) => 几乎没用。
  • 然后你执行 buf[l-1]='\0'; 所以你在缓冲区之前写入 1 个字节。

第二部分:

因为 s 已经被分配,这样做:

s=reverse(s,strlen(s));

会导致内存泄漏,因为您在 reverse 中分配了一个新字符串并返回它,覆盖了之前分配的 s 指针(您丢失了引用,但您没有释放它)

这最终解释了“内存丢失”的消息。

编辑:正如 osgx 评论的那样,这也是无效内存写入的来源:除非文件中字符串的大小不变/增加,否则返回的缓冲区可能太短。

我建议您使用就地反向算法,这将节省您在 reverse 例程中的分配。另外,不要传递 strlen(s)。您可以从例程中计算长度。

我写了一个快速的就地实现,它避免了在你的例程中使用 malloc(为了方便/打印修改字符串并返回它)

const char *reverse(char *s)
{
   char temp;
   int i;
   int len=strlen(s);

   for (i=0;i<len/2;i++)
   {
      temp = s[i];
      s[i] = s[len-i-1];
      s[len-i-1] = temp;
   }
   return s;  
}

关于c - Valgrind 入门(无效写入),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42726968/

相关文章:

c++ - 从程序集调用 C/C++ 函数 (OSX Mavericks x64)

c - 更安全的重新分配方式

使用 C 和指向指针的指针创建动态字符数组

尽管免费,但 C 内存泄漏

c++ - 如何为我的程序修复这个小的内存泄漏?

c++ - 排序算法面试

c - 如何防止一个进程产生更多的 child

c - 正在存储截断的值

c# - WPF WF4.5 Rehosted Designer 内存问题

java - 为什么这会在旋转时泄漏?