为什么将相同的地址发送到函数中?

Why are same addresses sent into function?

我正在研究双向链表,我遇到了这个问题。我将逐步粘贴部分代码,我会尝试解释发生了什么。

所以我定义了数据类型:

typedef struct node {
    void *data;
    struct node *prev, *next;
} NODE;

这部分 add 函数可能有问题。我已经检查了所有场景,但我不想粘贴不必要的代码。

void add(NODE **phead, NODE **ptail, void *data, int (*cmp)(const void*, const void*)){
    NODE *p, *q, *new = (NODE*)calloc(1, sizeof(NODE));
    new->data = data;
    if(*phead == 0)
        *phead = *ptail = new;

    else if((*cmp)((*phead)->data, data) > 0){
        new->next = *phead;
        (*phead)->prev = new;
        *phead = new;
    }

当我调试代码时,我看到当函数指针被调用时,相同的地址被作为参数发送。

NODE *search(NODE *head, NODE *tail, const void *data, int (*cmp)(const void*, const void*)){
    if(head == 0)
        return 0;
    while ((*cmp)(head->data, data) < 0 && (*cmp)(tail->data, data) > 0)
    {
        head = head->next;
        tail = tail->prev;
    }
    if((*cmp)(head->data, data) == 0)
        return head;
    else if((*cmp)(tail->data, data) == 0)
        return tail;
    else 
        return 0;
}

比较作为addsearch函数参数的函数是:

int cmp_str(const void *a, const void *b){
    return strcmp((const char*)a, (const char*)b);
}

同时调用 searchadd 的主要函数的一部分:

int main(){
    NODE *head = 0, *tail = 0;
    char c, *data = (char*)calloc(20, sizeof(char));
    do
    {
        printf("Add [A], delete [D], write [W], search [S], end [0]: ");
        scanf("\n%c", &c);
        if(c == 'A'){
            get_string(&data);
            NODE *p = search(head, tail, data, &cmp_str);
            if(p){
                p->data = data;
                printf("Data updated!\n");
            }
            else{
                add(&head, &tail, data, &cmp_str);
                printf("Data added.\n");
            }
        }

所以基本上出错的地方是只保存了一个数据。我在这里使用字符串,但数据的参数和变量是 void*。所以当我输入添加两个节点时,只保存最后输入的数据。同样在 main 中,每次除了第一个 p 都会被 search 函数找到,即使它不存在。正如我所说,调试器说 cmp_str 接收到两个相同地址的参数,这可能是找到错误位置的提示。

答案很简单。人们在学习C语言时常有的误区

data 指针始终指向相同的内存位置。

add 函数中,您只需将此引用分配给所有节点。在 C 中 = 不复制指针引用的内存,只分配引用。

您需要在每次交互时分配它(在 main 中)或在 add 函数中复制它。

void add(NODE **phead, NODE **ptail, void *data, size_t data_size, int (*cmp)(const void*, const void*)){
    NODE *p, *q, *new = calloc(1, sizeof(*new));
    //check if calloc did not return NULL
    new -> data = calloc(1, data_size);
    //check if calloc did not return NULL
    memcpy(new->data, data, data_size);
    // ...

一些补充说明:

  1. 使用对象而不是 sizeof 中的类型。
  2. 不要转换 malloc/calloc 的结果。如果编译器给你错误,这表明你使用 C++ 编译器编译 C 代码,这不是一个好主意。

您正在为每个添加操作设置相同的数据指针。看来您分配了一次并且每个周期都使用相同的内存。如果我说错了请指正。

您需要分配新的内存并将数据指针的内容深拷贝到new->data中。

void add(NODE **phead, NODE **ptail, void *data, int (*cmp)(const void*, const void*)){
    NODE *p, *q, *new = (NODE*)calloc(1, sizeof(NODE));
    new->data = data;
    ...