提问人:noobee 提问时间:12/16/2021 更新时间:12/16/2021 访问量:82
与 C 语言中的引用调用混淆
confused with call-by-reference in C
问:
此函数不返回预期结果(交换和 )。a
b
#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_one
x=y
new_element->next = element->next
element->next = new_element
new_element->next = element->next
element->next = new_element
x =y
a
b
对不起,很多代码,但我真的对此感到困惑。
谢谢。
答:
指针按值传递。交换这些变量中保存的指针值不会实现它们所指向的数据的交换。
想象一下,这是地址0x1000,并且是地址0x1004。
&p
&q
现在,您调用 -- 指针的值为 0x1000,指针的值为 0x1004。
swap_one(&p, &q);
x
y
现在交换指针值。 现在0x1004哪个指向 和 现在0x1000哪个指向 。然而,从未在记忆中移动过。您只更改了指针中存储的值。
x
q
y
p
p
q
当函数返回时,这些指针将超出范围。 并且仍然保留与以前相同的内容,因为您从一开始就没有修改过它们。
p
q
因此,若要交换它们指向的值,必须取消引用指针以获取实际数据。
int tmp = *x;
*x = *y;
*y = tmp;
将此与链表示例进行对比。在该示例中,您有一个指向结构的指针,并且您正在修改其成员。这实际上修改了内存中的指针,因为这是链表所在的位置以及它存储值的方式。next
但是,如果您将这些指针指向某个假设函数,则会出现同样的问题:-- 并且只是指向指针值的局部变量。如果交换它们,它们除了它们本身之外不会修改任何其他内容。因此,同样的修复方法也适用:要更改所指向的数据,您必须按照指针操作。swap_next(node **x, node **y);
x
y
函数仅交换传递给函数的指针值,而不是指针指向的指针值。因此,您所做的任何更改都不会反映在函数之外。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
变量 和 是 的局部变量。
这是调用函数时发生的情况:x
y
swap_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;
指针将指向指针指向的地址,指针将指向指针在调用时指向的地址。x
y
y
x
swap_one()
p (pointer) q (pointer)
--- ---
| |---+ | |---+
--- | --- |
| |
a ---- b ----
| | -----+ | |
---- | ----
| |
+--------|---------+
--- | | ---
| |---+ +---| |
--- ---
x (pointer) y (pointer)
pointing to b pointing to a
请注意,指针和指针仍分别指向变量和。这就是为什么当函数返回时,变量和的值不会被交换。p
q
a
b
swap_one()
a
b
如果要交换变量的值,其地址作为参数传递给函数,而不是取消引用指针参数并替换位置处的值,即您应该在函数中执行以下操作: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->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;
因为分配的内存引用 to 和 , (using ) 在分别分配 和 to 和 时将丢失。p
q
malloc()
&a
&b
p
q
上一个:交换两个结构的元素
评论