Using void pointer to simulate a generic linkedlist in C

ぃ、小莉子 提交于 2021-01-28 13:04:09

问题


I'm new to C, and I think there may be an issue with pointers here. Any help would be appreciated!

I have a linkedlist struct that looks like this:

ll.h:

#ifndef LLTEST_LL_H
#define LLTEST_LL_H

#include <stdlib.h>

typedef struct _listNode {
    void *data;
    struct _listNode *next;
} listNode;

typedef struct {
    int logicalLength;
    int elementSize;
    listNode *head;
    listNode *tail;
} linkedlist;

typedef struct table {
    const char* name;
    size_t col_count;
    size_t length;
} table;

typedef struct db {
    const char* name;
    size_t table_count;
    table** tables;
} db;

void list_append(linkedlist *list, void *element);
void create_list(linkedlist *list, int elementSize);
void create_db(const char* db_name, db** db);

#endif //LLTEST_LL_H

main.c

#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/un.h>
#include <sys/socket.h>
#include <unistd.h>
#include <string.h>
#include "ll.h"

linkedlist databases_list;

void create_list(linkedlist *list, int elementSize)
{
    list->logicalLength = 0;
    list->elementSize = elementSize;
    list->head = NULL;
    list->tail = NULL;
}

void list_append(linkedlist *list, void *element)
{
    listNode *node = malloc(sizeof(listNode));
    node->data = malloc(list->elementSize);
    node->next = NULL;
    memcpy(node->data, element, list->elementSize);

    if(list->logicalLength == 0) {
        list->head = list->tail = node;
    } else {
        list->tail->next = node;
        list->tail = node;
    }

    list->logicalLength++;
}

listNode* find_database_node(char *name){

    listNode *node = databases_list.head;
    //bool result = true;
    listNode *found_node = NULL;

    while(node != NULL) {
        db *item = (db *)node->data;

        if (strcmp(item->name, name) == 0){
            found_node = node;
            break;
        }

        node = node->next;
    }

    return found_node;
}

void get_db_pool(char *name, db *value){
    listNode *node = find_database_node(name);

    if(node != NULL){
        value = (db *)node->data;
    }
    else{
        value = NULL;
    }
}

void set_db_pool(db* value){
    list_append(&databases_list, (void *)value);
}

void create_db(const char* db_name, db** db) {
    if (*db == NULL) {
        *db = malloc(sizeof(db));
    }

    (*db)->name = db_name;
    (*db)->table_count = 0;
    (*db)->tables = NULL;
}

int main() {

    create_list(&databases_list, sizeof(db *));
    char* db_name= "mydb";
    db* db1 = NULL;
    create_db(db_name, &db1);
    set_db_pool(db1); //<--this line

    return 0;
}

On the line that I have marked "<--this line", when I check (db)databases_list.head->data's name parameter, I see "\222\017" instead of "mydb" as I would expect (such as when I check db1->name). What am I doing wrong?


回答1:


I've taken the revised code and edited a bit to suit some of my prejudices, so my line numbers are probably slightly different from yours. When I run it under valgrind, I get a complaint:

==55831== Invalid write of size 8
==55831==    at 0x100000EC7: main (ll17.c:78)
==55831==  Address 0x100a7c350 is 8 bytes after a block of size 8 alloc'd
==55831==    at 0x1000066F1: malloc (vg_replace_malloc.c:303)
==55831==    by 0x100000EB9: main (ll17.c:73)
==55831== 
==55831== Invalid write of size 8
==55831==    at 0x100000ECF: main (ll17.c:78)
==55831==  Address 0x100a7c348 is 0 bytes after a block of size 8 alloc'd
==55831==    at 0x1000066F1: malloc (vg_replace_malloc.c:303)
==55831==    by 0x100000EB9: main (ll17.c:73)

Line 73 is as shown:

void create_db(const char* db_name, db** db) {
    if (*db == NULL) {
        *db = malloc(sizeof(db));  // 73
    }

This allocates enough space for a pointer (strictly, a pointer to a pointer), not for a db structure.

  • You should avoid using variables with the same name as their (base) type — it confuses everyone except the compiler.

You really need:

void create_db(const char* db_name, db** db) {
    if (*db == NULL) {
        *db = malloc(sizeof(**db));
    }

With that change in place, the code runs OK under valgrind. According to my build of valgrind, it leaks a lot, but I've recently upgraded from Mac OS X 10.10 Yosemite to 10.11 El Capitan, and I don't trust my suppressions file to give me any useful information. It was built under Yosemite, and I'm also getting 'unknown fcntl calls' tracked by valgrind.




回答2:


I think the line int your test code right here might be flawed.

create_list(&databases_list, sizeof(db *), NULL);

when you do sizeof(db *) you are actually getting the size of the pointer NOT the db struct. You should actually be doing sizeof(db). Since the element size only gets set to the size of a pointer you don't copy enough data over and when you read back you read corrupted data from memory causing your incorrect values.



来源:https://stackoverflow.com/questions/33061335/using-void-pointer-to-simulate-a-generic-linkedlist-in-c

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!