这是我第一次使用 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/