【发布时间】:2022-01-21 23:33:45
【问题描述】:
我的免费使用有问题。
当我在 main.c 中仅释放 l 时,没有使用限制功能,它可以。
但是如果我使用限制功能并释放 l_limit,存在问题:free(): double free 在 tcache 2 中检测到并且 valgrind 不满意。
你能帮我修复免费的错误吗? :)
最小的可重现示例:
#include <stdlib.h>
#include <stdio.h>
typedef void* gpointer;
struct cell_s {
gpointer ptr_value;
struct cell_s *next;
};
typedef struct cell_s cell_t;
typedef cell_t* adr; // address
struct list_s {
cell_t *head;
int size;
};
typedef struct list_s list_t;
typedef void (*list_gfree)(gpointer data);
typedef void (*list_gprint)(gpointer data);
cell_t* create_cell(gpointer v) {
cell_t *c = malloc(sizeof(cell_t));
c->next = NULL;
c->ptr_value = v;
return c;
}
void destroy_int(gpointer data) {
free(data);
}
void print_int(gpointer data) {
int *ptr_value = (int *)data;
printf("%d - ", *ptr_value);
}
list_t* list_create() {
list_t *l = malloc(sizeof(list_t));
l->head = NULL;
l->size = 0;
return l;
}
void list_insert_in_head(list_t *l, gpointer element) {
adr address_c = create_cell(element);
address_c->next = l->head;
l->head = address_c;
++l->size;
}
void list_insert_next(list_t *l, gpointer element, adr address) {
adr address_c = create_cell(element);
if (l->head == NULL) {
list_insert_in_head(l, element);
} else {
address_c->next = address->next;
address->next = address_c;
}
++l->size;
}
void list_remove_in_head(list_t *l, list_gfree ft_destroy) {
if (l->head != NULL) {
adr tmp = l->head->next;
ft_destroy(l->head->ptr_value);
l->head->ptr_value = NULL;
ft_destroy(l->head);
l->head= tmp;
--l->size;
}
}
void list_remove_after(list_t *l, adr address, list_gfree ft_destroy) {
if (l->head->next == NULL) {
printf("Use list_remove_in_head function\n");
} else if (address != NULL) {
adr tmp = address->next->next;
ft_destroy(address->next->ptr_value);
address->next->ptr_value = NULL;
ft_destroy(address->next);
address->next = tmp;
--l->size;
}
}
void list_destroy(list_t *l, list_gfree ft_destroy) {
adr current = l->head;
while(current != NULL) {
adr tmp = current;
current = current->next;
ft_destroy(tmp->ptr_value);
tmp->ptr_value = NULL;
ft_destroy(tmp);
}
free(l);
}
void list_print(list_t *l, list_gprint ft_print) {
adr current = l->head;
while (current != NULL) {
ft_print(current->ptr_value);
current = current->next;
}
printf("\n");
}
list_t* limit(list_t *l, int n) {
list_t *l_limit = list_create();
adr current = l->head;
list_insert_in_head(l_limit, current->ptr_value);
current = current->next;
adr current_addr_l_limit = l_limit->head;
int count = 1;
if (n < l->size) {
while (count < n && current != NULL) {
++count;
list_insert_next(l_limit, current->ptr_value, current_addr_l_limit);
current = current->next;
current_addr_l_limit = current_addr_l_limit->next;
}
} else {
while (current != NULL) {
list_insert_next(l_limit, current->ptr_value, current_addr_l_limit);
current = current->next;
current_addr_l_limit = current_addr_l_limit->next;
}
}
return l_limit;
}
int main(void) {
list_t *l = list_create();
int *ptr_int = (int *)malloc(sizeof(int));
*ptr_int = 4;
list_insert_in_head(l, ptr_int);
list_print(l, print_int);
printf("Size : %d\n", l->size);
int *ptr_int_2 = (int *)malloc(sizeof(int));
*ptr_int_2 = 7;
list_insert_in_head(l, ptr_int_2);
list_print(l, print_int);
printf("Size : %d\n", l->size);
int *ptr_int_3 = (int *)malloc(sizeof(int));
*ptr_int_3 = 100;
list_insert_next(l, ptr_int_3, l->head);
list_print(l, print_int);
printf("Size : %d\n", l->size);
list_t *l_limit = limit(l, 2);
printf("\nLIMIT 2 \n");
list_print(l_limit, print_int);
printf("\n");
list_remove_in_head(l, destroy_int);
list_print(l, print_int);
printf("Size : %d\n", l->size);
list_remove_after(l, l->head, destroy_int);
list_print(l, print_int);
printf("Size : %d\n", l->size);
list_remove_after(l, l->head, destroy_int);
list_print(l, print_int);
printf("Size : %d\n", l->size);
int *ptr_int_4 = (int *)malloc(sizeof(int));
*ptr_int_4 = 447;
list_insert_next(l, ptr_int_4, l->head);
list_print(l, print_int);
printf("Size : %d\n", l->size);
list_destroy(l_limit, destroy_int);
list_destroy(l, destroy_int);
}
输出:
4 -
Size : 1
7 - 4 -
Size : 2
7 - 100 - 4 -
Size : 3
LIMIT 2
7 - 100 -
100 - 4 -
Size : 2
100 -
Size : 1
Use list_remove_in_head function.
100 -
Size : 1
100 - 447 -
Size : 2
free(): double free detected in tcache 2
执行: (-g -fsanitize=address)
=================================================================
==16065==ERROR: AddressSanitizer: attempting double-free on 0x602000000070 in thread T0:
#0 0x7f8b09173517 in __interceptor_free ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:127
#1 0x55ad7141f365 in destroy_int /home/zzz/zzz/main2.c:34
#2 0x55ad7141fa5b in list_destroy /home/zzz/zzz/main2.c:112
#3 0x55ad714203a9 in main /home/zzz/zzz/main2.c:211
#4 0x7f8b08ec4fcf in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58
#5 0x7f8b08ec507c in __libc_start_main_impl ../csu/libc-start.c:409
#6 0x55ad7141f204 in _start (/home/zzz/zzz/main+0x1204)
0x602000000070 is located 0 bytes inside of 4-byte region [0x602000000070,0x602000000074)
freed by thread T0 here:
#0 0x7f8b09173517 in __interceptor_free ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:127
#1 0x55ad7141f365 in destroy_int /home/zzz/zzz/main2.c:34
#2 0x55ad7141f6ea in list_remove_in_head /home/antoine/progc/main2.c:77
#3 0x55ad714200f5 in main /home/zzz/zzz/main2.c:193
#4 0x7f8b08ec4fcf in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58
previously allocated by thread T0 here:
#0 0x7f8b09173867 in __interceptor_malloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:145
#1 0x55ad7141feed in main /home/zzz/zzz/main2.c:176
#2 0x7f8b08ec4fcf in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58
SUMMARY: AddressSanitizer: double-free ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:127 in __interceptor_free
==16065==ABORTING
Valgrind
==16161== Invalid free() / delete / delete[] / realloc()
==16161== at 0x484621F: free (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==16161== by 0x10921F: destroy_int (in /home/zzz/zzz/main2)
==16161== by 0x1094C2: list_destroy (in /home/zzz/zzz/main2)
==16161== by 0x109918: main (in /home/zzz/zzz/main2)
==16161== Address 0x4a97570 is 0 bytes inside a block of size 4 free'd
==16161== at 0x484621F: free (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==16161== by 0x10921F: destroy_int (in /home/zzz/zzz/main2)
==16161== by 0x10939C: list_remove_in_head (in /home/antoine/progc/main2)
==16161== by 0x1097CA: main (in /home/zzz/zzz/main2)
==16161== Block was alloc'd at
==16161== at 0x4843839: malloc (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==16161== by 0x1096B7: main (in /home/zzz/zzz/main2)
==16161==
==16161== Invalid free() / delete / delete[] / realloc()
==16161== at 0x484621F: free (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==16161== by 0x10921F: destroy_int (in /home/zzz/zzz/main2)
==16161== by 0x1094C2: list_destroy (in /home/zzz/zzz/main2)
==16161== by 0x10992E: main (in /home/zzz/zzz/main2)
==16161== Address 0x4a97610 is 0 bytes inside a block of size 4 free'd
==16161== at 0x484621F: free (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==16161== by 0x10921F: destroy_int (in /home/zzz/zzz/main2)
==16161== by 0x1094C2: list_destroy (in /home/zzz/zzz/main2)
==16161== by 0x109918: main (in /home/zzz/zzz/main2)
==16161== Block was alloc'd at
==16161== at 0x4843839: malloc (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==16161== by 0x109715: main (in /home/zzz/zzz/main2)
==16161==
==16161==
==16161== HEAP SUMMARY:
==16161== in use at exit: 0 bytes in 0 blocks
==16161== total heap usage: 13 allocs, 15 frees, 1,168 bytes allocated
==16161==
==16161== All heap blocks were freed -- no leaks are possible
==16161==
==16161== For lists of detected and suppressed errors, rerun with: -s
==16161== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)
【问题讨论】:
-
无关:为什么在调用只需要
void*的free时将void*转换为int*? -
@Ted Lyngmo 明确说明 int * 在函数中被释放。使用 struct student,我会做 destroy_student 并将 void* 转换为 student*
-
我觉得
destroy_int这个名字已经够清楚了。无论如何,如果您首先在destroy_int中添加printf("free %p\n", data); fflush(stdout);,您将看到两次相同的地址。 -
@Ted Lyngmo 是的,你是对的。我用你的 printf 和没有演员表两次看到相同的地址。我删除演员表并编辑我的原始消息。
-
您的示例看起来并不真正最小。您是否复制并粘贴了 Valgrind 的完整消息?也许您可以启用更多详细信息?作为
valgrind的替代方案,您可以使用选项-g -fsanitize=address,undefined使用GCC 编译程序。显然,您在list_remove_in_head /home/main.c:77调用的destroy_int /home/main.c:34中释放了相同的内存,然后又在list_destroy /home/main.c:112调用的destroy_int /home/main.c:34中释放了相同的内存,所以我猜list_remove_in_head有问题。
标签: c linked-list free