Valgrind 入门(无效写入)

Getting started with Valgrind (Invalid Write)

这是我第一次使用 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]='[=10=]';
    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 零字节,这取决于实现:要么是它 returns NULL(然后是 KABOOM 写入它)或者指向你不应该的东西的指针写入 (!) => 几乎没用。
  • 然后你做 buf[l-1]='[=15=]'; 所以你在缓冲区之前写了 1 个字节。

第二部分:

由于 s 已经分配,​​这样做:

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

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

最后解释了 "memory lost" 条消息。

编辑:正如 osgx 评论的那样,这也是无效内存写入的来源:除非文件中字符串的大小为 constant/increasing,returned 缓冲区可能太短。

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

我写了一个快速的就地实现,它避免了 malloc 在你的例程中(修改字符串并 returns 它为 convenience/print)

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;  
}