Valgrind入门(无效写入)

时间:2017-03-10 20:14:16

标签: 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 个答案:

答案 0 :(得分:1)

写入字节错误:

你的反向弦太短了。由于空字符串终止符,您必须在reverse中为已分配的缓冲区添加1。

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

  • 首先你malloc零字节,这是依赖于实现的:它返回NULL(写入时是KABOOM)或指向不应该写入的东西(!)=&gt ;几乎没用。
  • 然后你做buf[l-1]='\0';所以你在缓冲区之前写了1个字节。

第二部分:

由于已分配s,因此执行此操作:

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

给出了内存泄漏,因为你在reverse中分配了一个新字符串并且你返回它,覆盖了之前分配的s指针(你丢失了引用,但你没有释放它)< / p>

最终解释了“记忆丢失”的消息。

编辑:正如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;  
}