与 C 中的按引用调用混淆



此函数不返回预期结果(交换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 = %dn", *x, *y);
}
void swap_two(int *x, int *y) {
}
int main() {
int a = 5;
int b = 100;
printf("Before a = %d b = %dnn", 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 = %dn", 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 %dn", list->size);
i = 0;
element = list->head;
while (1) {
data = element->data;
fprintf(stdout, "list[%03d] = %03dn", 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:%dn", *data);
return 0;
}

问题是:在swap_one函数中,x=y类似于new_element->next = element->nextelement->next = new_element。为什么new_element->next = element->nextelement->next = new_element工作,但swap_one函数中的x =y不会交换ab

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

谢谢。

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

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

  2. 现在,您调用swap_one(&p, &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只是指向指针值的局部变量。 如果您交换它们,它们不会修改除自身以外的任何内容。 因此,相同的修复程序适用:要更改所指向的数据,您必须遵循指针。

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

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

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

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

变量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将指向调用swap_one()x指向的指针的地址。

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),并在该位置分配值。

现在,来到第二部分代码的这一部分。以elementNULL的情况为例

} 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->next指针指向指针所指向的内容element->next并且这

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

将使element->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;

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

相关内容

  • 没有找到相关文章

最新更新