与 C 语言中的引用调用混淆

confused with call-by-reference in C

提问人:noobee 提问时间:12/16/2021 更新时间:12/16/2021 访问量:82

问:

此函数不返回预期结果(交换和 )。ab

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

void swap_one(int *x, int *y) {
    int *tmp;
    tmp = x;
    x = y;
    y = tmp;
    printf("x = %d y = %d\n", *x, *y);
}

void swap_two(int *x, int *y) {

}

int main() {
    int a = 5;
    int b = 100;
    printf("Before a = %d b = %d\n\n", a, b);
    int *p = (int *) malloc(sizeof(int));
    int *q = (int *) malloc(sizeof(int));
    p = &a;
    q = &b;

    swap_one(p, q);
    printf("After a = %d b = %d\n", a, b);
    return 0;
}

但下面的代码按预期工作。

#include <stdlib.h>
#include <stdio.h>
typedef struct ListElmt_ {
    void *data;
    struct ListElmt_ *next;
} ListElmt;

typedef struct List_ {
    int size;
    int (*match) (const void *key1, const void *key2);
    void (*destroy) (void *data);
    ListElmt *head;
    ListElmt *tail;
} List;

void list_init (List *list) {
    list->size = 0;
    list->match = NULL;
    list->destroy = NULL;
    list->head = NULL;
    list->tail = NULL;
}

int list_ins_next(List *list, ListElmt *element, void *data) {
    ListElmt *new_element;
    /* Alocate storage for the element. */
    if ((new_element = (ListElmt *) malloc(sizeof(ListElmt))) == NULL) return -1;
    /* new_element->data is of type void *. So we use (void *) data */
    new_element->data = (void *)data;
    if (element == NULL) {
        /* Handle insertion at the head of the list */
        if (list->size == 0) list->tail = new_element;
        new_element->next = list->head;
        list->head = new_element;
    } else {
        if (element->next == NULL) list->tail = new_element;
        new_element->next = element->next;
        element->next = new_element;
    }
    list->size++;
    return 0;
}

/* Print the list */
static void print_list(const List *list) {
    ListElmt *element;
    int *data;
    int i;
    /* Display the linked list */
    fprintf(stdout, "List size is %d\n", list->size);
    i = 0;
    element = list->head;
    while (1) {
        data = element->data;
        fprintf(stdout, "list[%03d] = %03d\n", i, *data);
        i++;
        if (element->next == NULL) {
            break;
        } else {
            element  = element->next;
        }
    }
}

int main(int argc, char **argv) {
    List list;
    ListElmt *element;
    int *data;
    int i;
    /* list = (List *) malloc(sizeof(List)); */
    /* Initialize the linked list */
    List *listPtr;
    listPtr = &list;
    list_init(listPtr);
    /* Perform some linked list operations */
    element = listPtr->head;
    for (i = 10; i > 0; i--) {
        if ( (data = (int *) malloc(sizeof(int))) == NULL) return 1;
        *data = i;
        if (list_ins_next(listPtr, NULL, data) != 0) return 1;
    }
    print_list(listPtr);
    fprintf(stdout, "Value in *data is:%d\n", *data);
    return 0;
}

问题是:在函数中,类似于 或 。为什么做和工作,但在swap_one函数不交换和?swap_onex=ynew_element->next = element->nextelement->next = new_elementnew_element->next = element->nextelement->next = new_elementx =yab

对不起,很多代码,但我真的对此感到困惑。

谢谢。

C 按引用传递 值调用

评论


答:

2赞 paddy 12/16/2021 #1

指针按值传递。交换这些变量中保存的指针值不会实现它们所指向的数据的交换。

  1. 想象一下,这是地址0x1000,并且是地址0x1004。&p&q

  2. 现在,您调用 -- 指针的值为 0x1000,指针的值为 0x1004。swap_one(&p, &q);xy

  3. 现在交换指针值。 现在0x1004哪个指向 和 现在0x1000哪个指向 。然而,从未在记忆中移动过。您只更改了指针中存储的值。xqyppq

  4. 当函数返回时,这些指针将超出范围。 并且仍然保留与以前相同的内容,因为您从一开始就没有修改过它们。pq

因此,若要交换它们指向的值,必须取消引用指针以获取实际数据。

int tmp = *x;
*x = *y;
*y = tmp;

将此与链表示例进行对比。在该示例中,您有一个指向结构的指针,并且您正在修改其成员。这实际上修改了内存中的指针,因为这是链表所在的位置以及它存储值的方式。next

但是,如果您将这些指针指向某个假设函数,则会出现同样的问题:-- 并且只是指向指针值的局部变量。如果交换它们,它们除了它们本身之外不会修改任何其他内容。因此,同样的修复方法也适用:要更改所指向的数据,您必须按照指针操作。swap_next(node **x, node **y);xy

2赞 dbush 12/16/2021 #2

函数仅交换传递给函数的指针,而不是指针指向的指针值。因此,您所做的任何更改都不会反映在函数之外。swap_one

您需要取消引用这些指针才能读取/写入它们指向的内容。

void swap_one(int *x, int *y) {
    int tmp;
    tmp = *x;
    *x = *y;
    *y = tmp;
    printf("x = %d y = %d\n", *x, *y);
}

在函数中,您正在更改指针指向的内容,因此可以在函数外部看到更改。list_ins_next

0赞 H.S. 12/16/2021 #3

变量 和 是 的局部变量。 这是调用函数时发生的情况:xyswap_one()swap_one()

p = &a;
q = &b;

p (pointer)        q (pointer)
---                ---
| |---+            | |---+
---   |            ---   |
      |                  |
   a ----             b ----
     |  |               |  |
     ----               ----

从函数调用函数:swap_one(p, q)main()

p (pointer)        q (pointer)
---                ---
| |---+            | |---+
---   |            ---   |
      |                  |
   a ----             b ----
     |  |               |  |
     ----               ----
      |                  |
      |                  |
---   |            ---   |
| |---+            | |---+
---                ---   
x (pointer)        y (pointer)
pointing to a      pointing to b

(x and y are parameters of swap_one()) 

执行以下函数语句后:swap_one()

tmp = x;
x = y;
y = tmp;

指针将指向指针指向的地址,指针将指向指针在调用时指向的地址。xyyxswap_one()

 p (pointer)        q (pointer)
---                ---
| |---+            | |---+
---   |            ---   |
      |                  |
   a ----             b ----
     |  | -----+        |  |
     ----      |        ----
               |         | 
      +--------|---------+
---   |        |   ---   
| |---+        +---| |
---                ---   
x (pointer)        y (pointer)
pointing to b      pointing to a 

请注意,指针和指针仍分别指向变量和。这就是为什么当函数返回时,变量和的值不会被交换。pqabswap_one()ab

如果要交换变量的值,其地址作为参数传递给函数,而不是取消引用指针参数并替换位置处的值,即您应该在函数中执行以下操作:swap_one()swap_one()

    tmp = *x;
    *x = *y; 
    *y = tmp;

通过这些更改,其地址传递给函数的变量的值将被交换,因为现在,您正在取消引用指针(即)和指针(即)并在该位置分配值。swap_one()x*xy*y

现在,来到第二段代码的这一部分。当不是elementNULL

    } else {
    if (element->next == NULL) list->tail = new_element;
    new_element->next = element->next;
    element->next = new_element;

请注意,指针是类型的,是的,它也是函数的局部指针变量。但是,在这里,我们利用这个局部指针变量来修改它所指向的结构成员的值。elementListElmtlist_ins_next()

    new_element (pointer of type ListElmt)
      ----
      |  |-----+
      ----     |
               |
           -----------
           |    |    |
           -----------
             ^      ^
             |      |
          data     next
          pointer  pointer


    element (pointer of type ListElmt, which is pointing to an existing node of list
      ----   passed to list_ins_next() function)
      |  |-----+
      ----     |
               |
           -----------
           |    |    |
           -----------
             ^      ^
             |      |
          data     next
          pointer  pointer

请注意,这

new_element->next 

(*new_element).next

这意味着,取消引用指针(转到它指向的位置)并在该位置访问指针。new_elementnext

所以,这个

new_element->next = element->next;

将使指针指向指向哪个指针,而这个new_element->nextelement->next

element->next = new_element; // same as (*element).next = new_element;

将使指针指向指针所指向的内容。执行这些语句后,element->nextnew_element

        new_element (pointer of type ListElmt)
      ----
      |  |-----+
      ----     |
               |
           -----------
      +--- |    |    |-------> (pointing to what element->next was pointing at)
      |    -----------
      |      ^      ^
      |      |      |
      |   data     next
      |   pointer  pointer
      --------------------+
                          |
    element               |
      |  |-----+          |
      ----     |          |
               |          |
           -----------    |
           |    |    |----+
           -----------
             ^      ^
             |      |
          data     next
          pointer  pointer

因此,如果要对传递给函数的指针进行更改,则必须在调用函数中取消引用它。


如果您仍然感到困惑,请仅检查以下内容:

在第二个代码中,如果执行与第一个代码相同的操作:

example = <some pointer of type ListElmt>;

即将一些相同类型的其他指针分配给 ,这将发生以下情况:example

     element 
      ----                 -----------
      |  |---------------> |    |    |
      ----                 -----------

         (The original ListElmt type element
          whose address passed to list_ins_next() function)
           -----------
           |    |    |
           -----------
             ^      ^
             |      |
          data     next
          pointer  pointer

现在,当函数返回时,其地址传递给该函数的原始元素将保持不变。list_ins_next()


附加:

在这里,您的程序正在泄漏内存:

    int *p = (int *) malloc(sizeof(int));
    int *q = (int *) malloc(sizeof(int));
    p = &a;
    q = &b;

因为分配的内存引用 to 和 , (using ) 在分别分配 和 to 和 时将丢失。pqmalloc()&a&bpq