malloc、重铸和释放

j8ag8udp  于 2023-03-29  发布在  其他
关注(0)|答案(3)|浏览(111)

我有一些结构来存储不同类型的列表:int n = int n;typedef char db_string[DB_STRING_LEN];

struct List_db_int {
    struct List_db_int *next;
    struct List_db_int *prev;
    db_int v;
};
struct List_db_string {
    struct List_db_string *next;
    struct List_db_string *prev;
    db_string v;
};
struct List_db_void {
    struct List_db_void *next;
    struct List_db_void *prev;
};

我也有一个union,可以存储任何列表指针:

union Uni_list {
    struct List_db_int *db_type_int;
    struct List_db_string *db_type_string;
    struct List_db_void *db_type_void;
};

我想创建一个函数,它将从列表中删除一些元素,但我希望它是列表类型不可知的,所以我有以下解决方案:

/*data is an array of pointers to begin of lists*/
    union Uni_list *data;
    struct List_db_void *last; 
    for (i = 0; i < ELEMENTS_; i++) {
        last = data[i].db_type_void->next;
        for (j = 0; j < HOW_MANY_ELEMENTS_REMOVE; j++) {
            free_list(last, t->cols[i]/*type of column*/);
            last = last->next;
        }
        data[i].db_type_void->next = last;
    }

下面是free_list函数:空隙

free_list(struct List_db_void *elm, enum db_type type) {
    switch(type) {
        case db_type_int:
            free((struct List_db_int*) elm);
            break;
        case db_type_string:
            free((struct List_db_string*) elm);
            break;
        default:
            /*Should not reach*/
            return ;
            break;
    }
}

但它不能正常工作,当我尝试读取修改后的列表时,我最终得到:

*** Error in `./ppbase': free(): invalid pointer: 0x00007f84aa47f678 ***                                                       
======= Backtrace: =========                                                                                                   
/usr/lib/libc.so.6(+0x72ecf)[0x7f84aa14cecf]                                                                                   
/usr/lib/libc.so.6(+0x7869e)[0x7f84aa15269e]                                                                                   
/usr/lib/libc.so.6(+0x79377)[0x7f84aa153377]                                                                                   
./ppbase[0x402001]                                                                                                             
./ppbase[0x40217e]                                                                                                             
./ppbase[0x40273d]                                                                                                             
/usr/lib/libc.so.6(__libc_start_main+0xf5)[0x7f84aa0fbbc5]                                                                     
./ppbase[0x4009d9]                                                                                                             
======= Memory map: ========                                                                                                   
00400000-00404000 r-xp 00000000 08:04 10883382                           /home/hafron/dev/ppbase/ppbase                        
00603000-00604000 rw-p 00003000 08:04 10883382                           /home/hafron/dev/ppbase/ppbase                        
01797000-017b8000 rw-p 00000000 00:00 0                                  [heap]                                                
7f84a9ec4000-7f84a9ed9000 r-xp 00000000 08:03 49858                      /usr/lib/libgcc_s.so.1                                
7f84a9ed9000-7f84aa0d9000 ---p 00015000 08:03 49858                      /usr/lib/libgcc_s.so.1                                
7f84aa0d9000-7f84aa0da000 rw-p 00015000 08:03 49858                      /usr/lib/libgcc_s.so.1                                
7f84aa0da000-7f84aa27c000 r-xp 00000000 08:03 9136                       /usr/lib/libc-2.18.so                                 
7f84aa27c000-7f84aa47b000 ---p 001a2000 08:03 9136                       /usr/lib/libc-2.18.so                                 
7f84aa47b000-7f84aa47f000 r--p 001a1000 08:03 9136                       /usr/lib/libc-2.18.so                                 
7f84aa47f000-7f84aa481000 rw-p 001a5000 08:03 9136                       /usr/lib/libc-2.18.so                                 
7f84aa481000-7f84aa485000 rw-p 00000000 00:00 0                                                                                
7f84aa485000-7f84aa4a5000 r-xp 00000000 08:03 7209                       /usr/lib/ld-2.18.so                                   
7f84aa672000-7f84aa675000 rw-p 00000000 00:00 0                                                                                
7f84aa6a1000-7f84aa6a4000 rw-p 00000000 00:00 0                                                                                
7f84aa6a4000-7f84aa6a5000 r--p 0001f000 08:03 7209                       /usr/lib/ld-2.18.so                                   
7f84aa6a5000-7f84aa6a6000 rw-p 00020000 08:03 7209                       /usr/lib/ld-2.18.so                                   
7f84aa6a6000-7f84aa6a7000 rw-p 00000000 00:00 0                                                                                
7fff249c8000-7fff249e9000 rw-p 00000000 00:00 0                          [stack]                                               
7fff249fe000-7fff24a00000 r-xp 00000000 00:00 0                          [vdso]                                                
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]                                            
zsh: abort (core dumped)  ./ppbase < tests/filter

我应该如何正确地在这段代码中使用自由函数?我可以安全地将列表中的任何一个转换为:db_type_void-〉next(第二个例子中的第5行)?

blpfk2vs

blpfk2vs1#

一个问题是下面的一对线

free_list(last, t->cols[i]/*type of column*/);
last = last->next;

这段代码释放了last的内存,然后立即在下一行使用释放的内存。next指针的缓存应该在释放last之前完成。
我不确定这是否是导致你的segfault的原因,但它肯定是应该改变的东西。我怀疑错误最有可能是在分配函数中。你能发布吗?

3duebb1j

3duebb1j2#

free_list(last, t->cols[i]/*type of column*/);
        last = last->next;

free_list释放last,它将在下一行中被访问。
编辑:
关于
我可以将列表中的任何一个安全地转换为:db_type_void-〉next
它不干净。C标准只保证结构体的第一个成员位于偏移量0处。因此,将->next转换为类型化的struct List_db_string *是可以的。但不保证struct List_db_string *struct List_db_void *具有相同的内部表示,因此

struct List_db_void *next;
        next = last->next;  /* was last = last->next in question */

可能会导致问题(理论上;它可能会在每个相关平台上做正确的事情)。
一种更简洁的方法是将last->next转换为正确的类型并访问其next属性。
注意:所有这些都适用于访问next;其他成员(prevv)的行为在没有上述转换的情况下是未定义的。
编辑:
在泛型类型上实现操作,如

struct list_head {
    struct list_head *next;
    struct list_head *prev;
}

允许例如

struct List_db_int {
    struct list_head head;
    int v;
}

struct List_db_string {
    string v;
    struct list_head head;
}

(note属性的不同顺序)。您可以通过使用container_of模式从head计算对象。

odopli94

odopli943#

该功能:

free_list(struct List_db_void *elm, enum db_type type) {
    switch(type) {
        case db_type_int:
            free((struct List_db_int*) elm);
            break;
        case db_type_string:
            free((struct List_db_string*) elm);
            break;
        default:
            /*Should not reach*/
            return ;
            break;
    }
}

完全相同(考虑到我们从未经过default分支,正如您的评论所建议的那样)

free_list(struct List_db_void *elm, enum db_type type) {
    free(elm);
}

指针强制转换是针对类型系统的,没有动态语义。可以说,它们在编译后就消失了。
这就是说,我正在看一看是什么原因导致的segfault。

**编辑:**可能是这个!

for (j = 0; j < HOW_MANY_ELEMENTS_REMOVE; j++) {
    free_list(last, t->cols[i]/*type of column*/);
    last = last->next;
}

您在释放last后正在使用它。请执行以下操作

for (j = 0; j < HOW_MANY_ELEMENTS_REMOVE; j++) {
    struct List_db_void *temp = last->next; 

    free_list(last, t->cols[i]/*type of column*/);
    last = temp;
}

相关问题