释放getline()缓冲区后,丢失了大约c:bytes个字节

bytes lost after freeing getline() buffer

我到处寻找有关使用getline()时valgrind内存泄漏的帖子。

本质上,我的代码所做的是使用getline()在一行中读取的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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("%d
"
,status);
    //if(isBadInput(line_ptr)){...}
    if(status == 0){
        printf("ENCOUNTERED BAD INPUT. STOPPING
"
);
        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的下一个位置

1
2
3
4
5
6
7
line_ptr ="123 456 789
"

line_ptr = lookPastSpace(line_ptr)
line_ptr ="456 789
"

...
line_ptr ="" -->sscanf stops.

事后,我走下链表,释放了每个分配的节点,并且由于节点没有分配内存的内容,所以我不得不释放LL的内存。

然后,稍后,我释放char * lineOrigin,这大概应该可以工作,因为它是最初分配的缓冲区,但是没有。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
    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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
    #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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/* 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 = %d
",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的原始值,那么我不会出现内存泄漏(有时)


您基本上具有以下模式:

1
2
3
4
5
6
char *lineOrigin, *line_ptr;
size_t len = 5;

lineOrigin = malloc(len);
line_ptr = lineOrigin;
getline(&line_ptr, &len, stdin);

因为getline()会在必要时重新分配缓冲区,所以在getline()调用之后,lineOrigin可能不再有效。

本质上,您将在lineOrigin中保留旧指针的副本,该副本可能由getline()调用释放。您没有释放(使用)和free()一些不再有效的旧值:lineOrigin,而不是释放可能由getline()line_ptr重新分配的当前缓冲区。

编写模式的正确方法很简单。首先将行指针定义为NULL,并将其分配的大小设置为零。我将分配的大小称为line_max,其中line_len反映了当前行的长度,但是显然您可以使用您认为最可维护的任何变量命名。

1
2
3
char   *line_ptr = NULL;
size_t  line_max = 0;
ssize_t line_len;

您使用读取了一行或多行

1
2
3
4
5
6
7
8
9
10
11
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_ptrline_max的值可能在一个调用之间有所不同。

完成后,释放行缓冲区。因为free(NULL);始终是安全的,所以即使没有读取任何实际行也可以这样做:

1
2
3
free(line_ptr);
line_ptr = NULL;
line_max = 0;

现在,清除变量并不是绝对必要的,但是我发现这是一个好习惯,有时甚至可以帮助调试。

另请注意,即使在getline()调用之前,也完全可以释放缓冲区并按上述方式清除变量,因为getline()不在乎它是否获得零大小的NULL指针,还是先前分配的带有零的缓冲区。非零大小。