我已经四处寻找有关使用 getline() 时 valgrind 内存泄漏的帖子。
本质上,我的代码所做的是使用 getline() 在一行中读取。
int main(){
LL A;
LLinit(&A);
size_t len = 5;
char *lineOrigin = (char *)malloc(len);
char *line_ptr = lineOrigin;
getline(&line_ptr,&len,stdin);
int status, val;
while( (status = sscanf(line_ptr,"%d",&val) ) >= 0){
printf("%dn",status);
//if(isBadInput(line_ptr)){...}
if(status == 0){
printf("ENCOUNTERED BAD INPUT. STOPPINGn");
return 1;
}
Node *node_ptr = (Node *)malloc(sizeof(Node));
node_ptr->val = val;
append(&A,node_ptr);
line_ptr = lookPastSpace(line_ptr);
}
printLL(&A);
freeLL(&A);
free(lineOrigin);
return 0;
}
如您所见,我为 char *lineOrigin 分配了一些内存,然后有第二个指针 *line_ptr,我将其指向 *lineOrigin,稍后我在代码中对其进行更改。
这个代码块几乎从stdin读取整数,并将它们按顺序存储到LinkedList中。
每次 sscanf 将 int 读入 &val 时,我都会使用 lookPastSpace() 在字符串中的非空格字符上移动 *line_ptr,直到遇到另一个空格,然后我将指针再移动一个缺口,以便line_ptr现在指向字符串中可以读取新 int 的下一个位置
line_ptr = "123 456 789n"
line_ptr = lookPastSpace(line_ptr)
line_ptr = "456 789n"
...
line_ptr = "" -->sscanf stops.
后记,我沿着链表向下走并释放每个分配的节点,并且由于节点没有分配内存的内容,因此我必须释放LL的内存。
然后,稍后,我释放了 char *lineOrigin,这大概应该有效,因为它是最初分配的缓冲区,但它没有。
tylerjgabb@lectura:~/HW6/medianDir$ make mem
valgrind median
==11827== Memcheck, a memory error detector
==11827== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==11827== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
==11827== Command: median
==11827==
123 456 789
1
1
1
[123, 456, 789]
==11827== Invalid free() / delete / delete[] / realloc()
==11827== at 0x4C2BDEC: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==11827== by 0x400B37: main (in /p3/ht/tylerjgabb/HW6/medianDir/median)
==11827== Address 0x51ff040 is 0 bytes inside a block of size 5 free'd
==11827== at 0x4C2CE8E: realloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==11827== by 0x4EA5F9A: getdelim (iogetdelim.c:106)
==11827== by 0x400A73: main (in /p3/ht/tylerjgabb/HW6/medianDir/median)
==11827==
==11827==
==11827== HEAP SUMMARY:
==11827== in use at exit: 13 bytes in 1 blocks
==11827== total heap usage: 5 allocs, 5 frees, 66 bytes allocated
==11827==
==11827== LEAK SUMMARY:
==11827== definitely lost: 13 bytes in 1 blocks
==11827== indirectly lost: 0 bytes in 0 blocks
==11827== possibly lost: 0 bytes in 0 blocks
==11827== still reachable: 0 bytes in 0 blocks
==11827== suppressed: 0 bytes in 0 blocks
==11827== Rerun with --leak-check=full to see details of leaked memory
==11827==
==11827== For counts of detected and suppressed errors, rerun with: -v
==11827== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
tylerjgabb@lectura:~/HW6/medianDir$
我有点卡住了,很困惑。知道发生了什么吗?
--------------关于 TYPEDEFS 和 LL 函数的说明----------------- LLfuncs.c
#include "LinkedListHeader.h"
#include <stdlib.h>
void LLinit(LL *LL_ptr){
LL_ptr->head_ptr = NULL;
LL_ptr->tail_ptr = NULL;
LL_ptr->length = 0;
}
void freeLL(LL *LL_ptr){
Node *curr_ptr = LL_ptr->head_ptr;
while(curr_ptr != NULL){
Node *next_ptr = curr_ptr->next_ptr;
free(curr_ptr);
curr_ptr = next_ptr;
}
}
以上只是我的LLfuncs.c的一小部分
LinkedListHeader.h
/* This is a header file containing typedefs and macros for linked lists *Tyler J Gabb
*For assignment 6a. Shuffle.
*/
#include <stdio.h>
#define showLocs(LL) printf("head_ptr = %p, tail_ptr = %p length = %dn",LL.head_ptr,LL.tail_ptr,LL.length)
typedef struct LinkedListNode {
int val;
struct LinkedListNode *next_ptr;
} Node;
typedef struct LinkedList {
Node *head_ptr;
Node *tail_ptr;
int length;
} LL;
LLinit() 初始化任何最近声明的 LL 类型,使其头尾在0x0,长度为 0。这有利于其他改变LL的功能
---------------------------一些其他有趣的信息-------------
如果输入字符串的大小小于len的原始值,那么我不会得到内存泄漏(有时)
您基本上具有以下模式:
char *lineOrigin, *line_ptr;
size_t len = 5;
lineOrigin = malloc(len);
line_ptr = lineOrigin;
getline(&line_ptr, &len, stdin);
由于getline()
将在必要时重新分配缓冲区,因此在getline()
调用之后,lineOrigin
可能不再有效。
从本质上讲,您正在lineOrigin
中保留旧指针的副本,该副本可能被getline()
调用释放。而不是释放可能由getline()
、line_ptr
重新分配的当前缓冲区,您错误地(使用)并free()
一些不再有效的旧值:lineOrigin
。
编写模式的正确方法很简单。首先将行指针定义为NULL
,并将其分配的大小定义为零。我将分配的大小称为line_max
,line_len
反映了当前行的长度,但您显然可以使用您认为最易于维护的任何变量命名。
char *line_ptr = NULL;
size_t line_max = 0;
ssize_t line_len;
您使用
line_len = getline(&line_ptr, &line_max, stdin);
if (line_len > 0) {
/* You have a line in line_ptr */
} else
if (ferror(stdin)) {
/* Error reading from standard input (rare!) */
} else
if (feof(stdin)) {
/* No more data to read from standard input. */
}
您可以根据需要多次重复上述操作,但您需要意识到line_ptr
和line_max
的值可能因调用而异。
完成后,释放行缓冲区。因为free(NULL);
始终是安全的,所以即使没有读取实际的行,这样做也是安全的:
free(line_ptr);
line_ptr = NULL;
line_max = 0;
现在,清除变量并不是绝对必要的,但我发现这是一个很好的做法,有时甚至可能有助于调试。
另请注意,即使在getline()
调用之前,也可以像上面那样释放缓冲区并清除变量,因为getline()
不在乎它是否获得大小为零的 NULL 指针,或者之前分配的具有非零大小的缓冲区。