插入最后一个元素时出现 SkipList 分段错误

SkipList segmentation fault when insert last element

提问人:Trammy 提问时间:11/11/2023 最后编辑:Trammy 更新时间:11/18/2023 访问量:78

问:

我开发了一个带有 void 指针的 SkipList,以及设置其最大级别的概率方法。

这些是我的结构:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

typedef struct _Node{
    Node **next;
    size_t size; //number of pointer in the node
    void *item;
}Node;

typedef struct _SkipList{
    Node **heads;
    size_t max_level; //max height in a single node
    size_t max_height; //max possible height in the skiplist
    int (*compare)(const void *, const void *);
}SkipList;

double random_probability() {
    return (double)rand() / RAND_MAX;
}

size_t random_level(size_t max_height) {
    size_t level = 1;
    while (random_probability() < 0.5 && level < max_height) {
        level++;
    }
    return level;
}

Node *create_node(void *item, size_t level) {
    Node *newNode = (Node *)malloc(sizeof(Node));
    if (!newNode) {
        fprintf(stderr, "Errore nell'allocazione del nodo.\n");
        exit(EXIT_FAILURE);
    }
    newNode->item = item;
    newNode->size = level;
    newNode->next = (Node **)malloc(level * sizeof(Node *));
    if (!newNode->next) {
        fprintf(stderr, "Errore nell'allocazione dei puntatori successivi.\n");
        exit(EXIT_FAILURE);
    }
    for (size_t i = 0; i < level; i++) {
        newNode->next[i] = NULL;
    }
    return newNode;
}

void new_skiplist(SkipList **list, size_t max_height, int (*compare)(const void *, const void *)) {
    *list = (SkipList *)malloc(sizeof(SkipList));
    if (!*list) {
        fprintf(stderr, "Errore nell'allocazione della SkipList.\n");
        exit(EXIT_FAILURE);
    }
    (*list)->heads = (Node **)calloc(max_height, sizeof(Node *));
    if (!(*list)->heads) {
        fprintf(stderr, "Errore nell'allocazione dei puntatori iniziali.\n");
        exit(EXIT_FAILURE);
    }
    (*list)->max_level = 0;
    (*list)->max_height = max_height;
    (*list)->compare = compare;
    srand((unsigned int)time(NULL));
}

这是插入函数

void insert_skiplist(SkipList *list, void *item) {
    Node **update = (Node **)malloc((list->max_height + 1) * sizeof(Node *));
    if (!update) {
        fprintf(stderr, "Errore nell'allocazione dell'array di aggiornamento.\n");
        exit(EXIT_FAILURE);
    }

    Node *x = list->heads;

    // loop invariant: x[i]->item <= item or item < first element of level i in list
    for (size_t i = list->max_level; i > 0; i--) {
        while (x->next[i - 1] != NULL && list->compare(x->next[i - 1]->item, item) <= 0) {
            x = x->next[i - 1];
        }
        update[i] = x;
    }

    // loop end: x[1]->item <= item or item < first element in list
    x = x->next[0];

    if (x != NULL && list->compare(x->item, item) == 0) {
        // Elemento già presente, aggiorna il valore o gestisci il caso a tuo piacimento
        // x->item = newValue;
        free(update);
        return;
    }

    size_t level = random_level(list->max_height);

    if (level > list->max_level) {
        for (size_t i = list->max_level + 1; i <= level; i++) {
            update[i] = list->heads;
        }
        list->max_level = level;
    }

    x = create_node(item, level);

    for (size_t i = 1; i <= level; i++) {
        x->next[i - 1] = update[i]->next[i - 1];
        update[i]->next[i - 1] = x;
    }

    free(update);
}
    const void *search_skiplist(SkipList *list, void *item) {
        Node **x = list->heads;
        for (size_t i = list->max_level; i > 0; i--) {
            while (x[i] != NULL && list->compare(x[i]->item, item) < 0) {
                x = x[i]->next;
            }
        }

        if (x[1] != NULL && list->compare(x[1]->item, item) == 0) {
            return x[1]->item;
        } else {
            return NULL;
        }
    }

现在,当我尝试从 main 上的数组创建跳过列表时:

int compare_int(const void *a, const void *b) {
 int x = (int *)a;
 int y = (int *)b;
 return (x - y);
}
int main() {
 srand((unsigned)time(NULL));

 SkipList *int_list;
 new_skiplist(&int_list, 10, compare_int);

 int int_keys[] = {3, 6, 1, 8, 2, 7, 5, 4, 9};
 for (size_t i = 0; i < sizeof(int_keys) / sizeof(int_keys[0]); i++) {
     insert_skiplist(int_list, int_keys[i]);
 }
const void* found=search_skiplist(int_list,9);
 if(found!=NULL) printf("FOUNDED\n");
 clear_skiplist(int_list);
return 0;

一切正常,直到我尝试插入 8,问题似乎是如果我的插入必须在列表末尾放置一个节点,它就会出现分割错误。

准确地说,当我尝试插入 8 项时,GDB 会出现此错误:

Program received signal SIGSEGV, Segmentation fault. 
Inser into  SkipList int: 9

Program received signal SIGSEGV, Segmentation fault.
0x004017ff in insert_skiplist (list=0xad1800, item=0x3) at src/skiplist.c:104
104         x = x->next[0];
(gdb) bt
#0  0x004017ff in insert_skiplist (list=0xad1800, item=0x3) at src/skiplist.c:104
#1  0x00401af6 in main () at src/main_ex2.c:30
(gdb) print (int)item
$1 = 3
(gdb) print (int*)item
$2 = (int *) 0x3
(gdb) print (int)x->item
$3 = 0
(gdb) print x->item
$4 = (void *) 0x0
(gdb) print x->next[0]
Cannot access memory at address 0x0
(gdb)
(gdb)

所以它无法访问 K-1 节点中的前一项,我不明白为什么,因为它将 6 放在 3 和 1 之后而没有错误。

我被这个错误卡住了,有人可以帮我理解这个错误吗?

非常感谢!

c 算法 segmentation-fault void-pointers skip-lists

评论

2赞 Jabberwocky 11/11/2023
您需要显示创建列表的代码。问题可能出在眼前。我们需要一个最小的可重复的例子。请进行相应的编辑
1赞 Shawn 11/11/2023
使用调试器尝试缩小范围的大道具。我们看到的还不够多。
2赞 Jabberwocky 11/11/2023
@Trammy请展示一个最小的可重现示例(可以复制/粘贴/编译的东西),而不是需要拼接在一起的代码。还要显示输入(如果有)。
0赞 Trammy 11/11/2023
@Jabberwocky所有的代码,没有输入,只是复制 main,并将结构和所有方法放在同一个文件中,请记住将库包含在 main 中
1赞 trincot 11/11/2023
我的IDE抱怨的一些事情: -- 不兼容的转换。 -- 不兼容的转换(第二个参数)。 -- 不兼容的转换(第二个参数)。混合指向 ints 和 ints 的指针似乎是个坏主意。int x = (int *)a;search_skiplist(int_list, 9);insert_skiplist(int_list, int_keys[i]);

答:

0赞 Trammy 11/18/2023 #1

问题出在地址上,这段话是错误的

Node *x = list->heads;

它必须是

Node *x = &list->heads.

现在一切正常