我们看下面代码:
#include <stdio.h>
typedef struct node {
int r;
} Node, * List;
void AddItemX(int R, List plist);
void AddItemY(int R, List* plist);
int main(void) {
List movies = NULL;
AddItemX(10, movies); ↔movies确实是Node类型指针
printf("outerX:%s\n", movies == NULL ? "NULL" : "NOT NULL");
AddItemY(10, &movies);
printf("outerY:%s\n", movies == NULL ? "NULL" : "NOT NULL");
}
void AddItemX(int R, List plist) {
Node* pnew = (Node*)malloc(sizeof(Node));
pnew->r = R;
plist = pnew;
printf("innerX:%s\n", plist == NULL ? "NULL" : "NOT NULL");
}
void AddItemY(int R, List* plist) {
Node* pnew = (Node*)malloc(sizeof(Node));
pnew->r = R;
*plist = pnew;
printf("innerY:%s\n", plist == NULL ? "NULL" : "NOT NULL");
}
innerX:NOT NULL
outerX:NULL
innerY:NOT NULL
outerY:NOT NULL
List movies;这行说明movies是指向node结构的指针,在AddItemX()函数体中plist即传进的movies指针,plist=pnew,即moives指针指向pnew,但在main()函数中movies为NULL,就向按值传递一样,但这不是指针吗?
我们看一个最简单交换两数值的实现:
#include<stdio.h>
void swap(int* x, int* y);
int main(void) {
int a = 3, b = 4;
printf("a地址=%p,b地址=%p\n", &a, &b);
swap(&a, &b);
printf("a=%d,b=%d\n", a, b);
printf("a地址=%p,b地址=%p\n", &a, &b);
return 0;
}
void swap(int* x, int* y) { // 用int变量来存储进行a和b的地址交换,并且解引用
int tmp;
tmp = *y;
*y = *x;
*x = tmp;
}
//下面代码完成不了交换,对传入的a、b的地址也没有交换
//void swap(int* x, int* y) {
// int* tmp;
// tmp = y;
// y = x;
// x = tmp;
//}
a地址=0x7ffeefbff568,b地址=0x7ffeefbff564
a=4,b=3
a地址=0x7ffeefbff568,b地址=0x7ffeefbff564
swap()函数里面形参(int* a, int* b)只是表示传入的a、b是存储对应int型值的地址。*b = *a;表示将地址a中的值赋值到地址b中。a、b的地址没变,就是值交换了。传进来的是地址,操作的就是外面传入的数据了,否则就是形参的操作,对外面传入的值没任何影响。对于注释部分,没有完成a、b数值的交换,它们的地址也没交换,因为指针地址也就是一个整型值,也就相当于形参的交换,对外面传入的值没有任何影响。
再看一个错误的例子:
#include<stdio.h>
#include<stdlib.h>
void swap(int* x, int* y);
int main(void) {
int* a = (int*)malloc(sizeof(int));
int* b = (int*)malloc(sizeof(int));
*a = 3;
*b = 4;
swap(a, b);
printf("a=%d,b=%d", *a, *b);
return 0;
}
错误的代码:
void swap(int* x, int* y) {
int* tmp = y;
y = x;
x = tmp;
}
正确的代码:
void swap(int* x, int* y) {
int tmp = *y;
*y = *x;
*x = tmp;
}
由于这里a、b都是指针,都存的是地址,所以不用取地址符,其他操作和上一例一致。
所以上面的AddItemX()改成下面即可:
原(错误):
void AddItemX(int R, List plist) {
Node* pnew = (Node*)malloc(sizeof(Node));
pnew->r = R;
plist = pnew;
printf("innerX:%s\n", plist == NULL ? "NULL" : "NOT NULL");
}
现(正确???):
void AddItemX(int R, List plist) {
Node* pnew = (Node*)malloc(sizeof(Node));
pnew->r = R;
*plist = *pnew;
}
plist是个空指针,所以当然不能把内容赋值给它!
前提是plist在main()主函数里面要初始化,否则*plist显然出问题,但如果换成AddItemY()函数,plist为NULL,处理&plist没问题,因为&plist=0x0。
只要从主调函数传入的是地址,就能通过运算符*如期完成任务。特别要注意,形参类型与声明参数类型是不一样的。形参比如(int * x)代表传入的是一个地址,对于声明int * x;代表x是一个指向int型的指针变量。
最后看一个打印地址的最直观的例子,能更加明白函数按值传递:
#include <stdio.h>
#define ArSize 8
int sum_arr(int *arr, int n);
int main(void) {
int cookies[ArSize] = {1, 2, 4, 8, 16, 32, 64, 128};
printf("%p = array address, %d = sizeof cookies\n", cookies, sizeof cookies);
int sum = sum_arr(cookies, ArSize);
printf("Total cookies eaten: %d\n", sum);
sum = sum_arr(cookies, 3);
printf("First three eaters ate %d cookies.\n", sum);
sum = sum_arr(cookies + 4, 4);
printf("Last four eaters ate %d cookies.", sum);
return 0;
}
int sum_arr(int *arr, int n) {
int total = 0;
printf("%p = arr, %d = sizeof arr\n", arr, sizeof arr);
for (int i = 0; i < n; i++) total = total + arr[i];
return total;
}
00CFFCEC = array address, 32 = sizeof cookies
00CFFCEC = arr, 4 = sizeof arr
Total cookies eaten: 255
00CFFCEC = arr, 4 = sizeof arr
First three eaters ate 7 cookies.
00CFFCFC = arr, 4 = sizeof arr
Last four eaters ate 240 cookies.
通过程序运行结果,我们知道传递进函数的的确是地址00CFFCEC,从4 = sizeof arr也正说明了传递的是一个地址而不是整个数组,一般地址是int型就是占4个字节。否则就像第一条打印结果一样size为32了。而后面调用sum_arr(cookies + 4, 4);从结果可以看到它的地址cookies+4和cookies的地址偏移了4*sizeof(int)=16,即00CFFCEC + 16(0x10) = 00CFFCFC。总之,传递的是地址这个值。
总之,函数是按值传递参数的。函数只能通过传递地址(或指针变量)才能更改主调程序传入的变量值。