(1)指针的定义
指针指的是一种变量,这个变量是用来保存地址的。
那么对于变量自己本身一定有一个地址,假如定义 int a = 5 ; a 有两重身份,一个是a在内存中对应的地址,另一个就是这个地址里保存的值 5 。
假如我们接着定义 int *p = &a ; 那么同样的,p在被赋值&a 前就有着两重身份,一个是p在内存中对应的地址,另一个就是这个地址里的值,不过现在已经明确了这里的内存里保存的是一个整型变量地址。
(2)变量的传值和传址
在常见的赋值语句,和函数调用中,我们调用一个变量时究竟调用的是他的哪一重身份呢?
先看看赋值
int a;
int b;
b = 3;
a = b;
上面这种情况我们很清楚的知道,赋值语句 a = b ;实现的是将b的内容拷贝一份赋值给a,完了a和b的内存中保存着相同的值。
接着
int *p1;
int *p2;
p1 = &b;
p2 = p1;
p1和p2为指针变量,语句p1 = &b ;我们知道是把b的地址赋给p1,结束后p1对应内存段保存b的地址&b。那么p2 = p1 ;呢?
其实p2 = p1 的赋值做法和前面 a = b一样,都是把右边的内容传到左边的地址对应的内存中去。所以现在我们的p2 中保存的也是&b了。
那么现在 *p2究竟是&b 呢还是 b的内容3呢? 这就要看*p2 是出现在赋值符号‘=’ 的左边还是右边了。如果在左边 *p2 就表示&b,如果在右边则 对 *p2求值后得到的结果为3 。
上面看起来可能有点绕,其实可以直接把*p2看成是p2保存的地址,当它在左边时他是用来接收的,因此保持它原本的形态,当它出现在‘=’右边时,由于赋值语句的特点又对*p2做了一次解引用操作,因而得到的是*p2里面保存的值。细细体会一下,我们发现这和 a = b 的赋值其实是一个道理。
a = (int)(*(&p));
a = (int)(&(*p));
最后如果我们在做以上赋值,得到的 a的值均为(int)&a 。按照*(&a)与&(*a)等效都表示a 的做法,很快我们就能得到(int)的右边实质上都等效为p,那么a = (int)p ,在这里p由于是右值需要再做一次解引用,故最后
a的值均为(int)&a 。
再看看指针做形参的情况
考虑如下代码
int a = 5;
int *p1;
p1 = &a;
int *p2 ;
p2 = p1;
printf("%d,%x,%x,%x\n\n",a ,&a, p1, p2);
同时输出a,&a,p1,p2。p1和p2将输出什么呢?是他们的地址还是他们保存的地址?结果如下:
可见,与所有基本变量一样(算术类型变量)p1,p2做函数参数传递的也是对应内存中的值。不过此时这个值却是一个地址,这种意义上来讲也算是传址调用。
事实上指针做函数形参传递地址只有当形参是数组名时
数组名做形参
char a[] = "test array name";
printf("%c\n%x\n%c\n%x\n%x\n", a[0],&a[0], a, a,&a);
以上代码运行的结果如下:
我们知道 &a 与 &a[0]指的是同一个地址,但是对a[0]输出的则为一个字符,a以字符格式输出就成了乱码。而当我们把a以%x格式
输出,我们得到的正式&a[0]的值。最后我们对数组名做了引用操作,发现&a与a表示的是同一个地址。
显然结论是:数组名做函数参数传递的是数组的首地址 。
参考博文: