【问题标题】:confused with call-by-reference in C与 C 中的引用调用混淆
【发布时间】:2022-01-19 05:51:27
【问题描述】:

此函数不返回预期结果(交换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函数中,x=y类似于new_element-&gt;next = element-&gt;nextelement-&gt;next = new_element。为什么new_element-&gt;next = element-&gt;nextelement-&gt;next = new_element 可以工作,但swap_one 函数中的x =y 不能交换ab

很抱歉有很多代码,但我真的对这个感到困惑。

谢谢。

【问题讨论】:

    标签: c pass-by-reference call-by-value


    【解决方案1】:

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

    p = &a;
    q = &b;
    
    p (pointer)        q (pointer)
    ---                ---
    | |---+            | |---+
    ---   |            ---   |
          |                  |
       a ----             b ----
         |  |               |  |
         ----               ----
    

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

    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;
    

    指针x 将指向指针y 所指向的地址,而指针y 将指向指针x 在调用swap_one() 时所指向的地址。

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

    请注意,指针p 和指针q 仍然分别指向变量ab。这就是为什么当swap_one()函数返回时,变量ab的值没有交换。

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

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

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

    现在,进入第二个代码的这一部分。以element 不是NULL 的情况为例

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

    请注意,element 指针的类型为 ListElmt,是的,它也是 list_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_element 指针(转到它所指向的位置)并访问该位置的next 指针。

    所以,这个

    new_element->next = element->next;
    

    将使new_element-&gt;next 指针指向element-&gt;next 指针所指向的内容和this

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

    将使element-&gt;next 指针指向new_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;
    

    因为分配的内存引用,指向指针pq,(使用malloc())将在您将&amp;a&amp;b 分别分配给pq 时丢失。

    【讨论】:

      【解决方案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 函数中,您正在更改指针指向的内容,因此可以在函数外部看到更改。

      【讨论】:

        【解决方案3】:

        指针通过值传递。交换这些变量中保存的指针值并不能实现它们指向的数据的交换。

        1. 假设&amp;p 是地址0x1000,&amp;q 是地址0x1004。

        2. 现在,你调用swap_one(&amp;p, &amp;q);——指针x的值为0x1000,指针y的值为0x1004。

        3. 您现在交换指针值。 x 现在是 0x1004,它指向 qy 现在是 0x1000,它指向 p。但是,pq 从未在内存中移动。您只更改了存储在指针中的值。

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

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

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

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

        但是,如果您将这些指针指向某个假设函数,则会出现同样的问题:swap_next(node **x, node **y); -- xy 只是指向指针值的局部变量。如果您交换它们,它们不会修改除自身之外的任何内容。所以同样的解决方法适用:要更改指向的数据,您必须遵循指针。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2016-04-16
          • 2021-06-20
          • 2020-04-14
          • 2021-04-07
          • 2019-01-08
          • 1970-01-01
          • 2016-08-18
          相关资源
          最近更新 更多