指针
指针是一种变量,和int、float、char一样,是一种基本的变量。只不过它比较抽象一些。
我们知道在C语言中,声明一个变量a、b,编译器会为其分配一个物理地址 addr_1 、addr_2,在之后的C语言中,a =1操作,是把数值1存入该 addr_1,就等价于a值变为1。b = b+1 ,是先读取 addr_2的内容,加1操作后,再写会 addr_2内。
可以这样理解: 变量出现在 =右侧,一般是 读 addr_x的内容,变量是左值,一般是 存入 addr_x。 在C语言中变量实质上就是 物理地址的代号。对变量名的操作本质上就是对 物理地址的操作。
所以 a =1 ,b=b+1,都无非是 地址的读写操作。 像float、int、char这类变量,变量名等价于物理地址,a = "address 1" b= "address 2"
访问a就是访问 address1. address1/2里面的内容就是a、b的值,没有其他特别的意义。
int *p 声明了一个指针变量p, 依然存在 p = "address 3" 的映射。但是p里面的内容是一个特别得信息。它是地址 "address n“。
C语言使用 *p 间接的访问 ”address n“ 使用p访问 "address 3",
声明和初始化
(1) int *p ; p = &a (2) float *p = &b ;
方法1,首先声明p是一个 "指向int"的指针变量。 然后 p = &a ,把a的地址,存入 p内。 即 p内存储的值 = address_a
方法2, 声明p是一个 "指向float"的指针变量。 是一个缩写形式。
一定要理解: 变量p自己有一个物理地址addr_p,只不过(addr_p) = &a.
未初始化和非法指针
因为我们经常需要对 指针变量进行间接访问,一定要保证指向的地址是一个安全可靠的。比如指针变量指向了一个未知的地址,后续盲目的
对该地址进行间接读写,很可能出现大问题。所以要保证指针--->安全地址。
非常可惜的是: 编译器对于未明确指定的指针变量不会进行分配。那我们分配到哪?最好有一个 绝对安全的地址供我们使用。
NULL指针: NULL值在stdio.h文件有明确 #define NULL ( (void *) 0 ) 把整型0强制转为指针型,并指向void类型数据。
int *p = NULL ; int *p = (void *) 0 相当于 指针变量p = 0,并指向了 void。
指针变量的间接访问 和直接访问
int *p = &a ; 这时候 p = 10 ;这是非法的,本质原因是: 10是整型变量,p是 指针型变量,不能对等。 可以 p = (int *) 10; 把10也变为
指向整数的指针变量。
&p: 同样对变量p取地址,获得的是p的物理地址。该操作合情合法。
*p : 左值,表示存入 (P)----> address 内。 右值,取(P)----> address内的数据。
* &a = 25 , 即 * (address a) = 25 即存入 a地址内。
指针的指针
既然指针是一个变量,自己拥有自己的地址,那完全可以再用指针指向它,。 这就是指向指针的指针,简称指针的指针
怎么声明? int **pp ;
怎么表明指向的规则? int *p = &a ; int **pp = &p 即可。
那么如何通过指针的指针 访问 整型变量a?
pp = &p
*pp 是对 指针变量p的间接操作。 *p 是完成对 整型a的间接访问。
**pp 才是最终对a的间接访问。
指针表达式实例
Char ch =’a’;
Char *cp = &ch;
表达式形式 |
左值 |
右值 |
&ch |
获得ch变量的地址 |
|
cp |
修改cp的内容,则指向位置将会变化 |
读取cp的内容,即获得某个间接地址 |
&cp |
获得指针变量cp自己的地址 |
|
*cp |
把数据存入 间接地址内 |
获取 间接地址 的内容 |
*cp+1 |
非法 |
获取 间接地址 的内容,并加1操作 |
*(cp+1) |
把数据存入 (间接地址+1)内 |
获取 (间接地址+1) 的内容, |
++cp |
非法 |
Cp值加1,再返回新cp值, 同时 指针的指向位置变化 |
Cp++ |
Cp值加1,返回旧cp值 同时 指针的指向位置变化 |
|
*++cp |
把数据存入(间接地址+1)内, 同时 指针的指向位置变化 |
获取 (间接地址+1) 的内容, 同时 指针的指向位置变化 |
*cp++ |
因为返回的是旧cp值,故而 把数据存入(间接地址)内, 同时指针的指向位置变化 |
因为返回的是旧cp值,故而 获取 间接地址 的内容 同时,指针的指向位置变化 |
++*cp |
获取 间接地址 的内容,+1之后,返回值 指针的指向位置不会变化 |
|
(*cp)++ |
获取 间接地址 的内容,并返回。 指针的指向位置不会变化 |
|
++*cp++ |
获得间接地址 的内容+1,并返回 指针的指向位置变化 |
|
++*++cp |
获得 间接地址+1 的内容,并对其加1,再返回, 指针的指向位置变化 |
这只是一个表格,描述了各种运算的结果。只为加深理解。
指针的运算
指针变量主要用于 间接操作,一般对指针变量本身的操作,并不是很多,主要就是加法运算。
float a[10] ;
float *p = a;
定义了一组浮点型数组,并把数组首地址赋给p。我们知道一个float 型会占用 4 bytes空间。
P = Address_of_a[0]. 如果我们执行p+1,本质上是想 P = Address_of_a[1]. 那么p+1 应该是 p + 1* sizeof(float)
庆幸的是,C编译器为我们进行了这个处理,使得我们可以更加方便的使用指针变量+1.
这也是我们声明指针时,为什么要说明指针指向的是什么数据类型。它其实是为了编译器处理 sizeof(xxx)操作、