1、指针
1.1 指针与指针变量
1.1.1 指针
前面说过,指针就是地址,指针和地址是同义词。
1.1.2 指针变量
存放指针的变量就是“指针变量”。
1.2 *与&
1.2.1 &
(1)&作为双目运算符使用时,为“位与”运算符
int a = 0x10; int b = 0x04; int c = a & b;
(2)&作为单目运算符使用时,为取地址运算符
1)取变量的地址
int a = 100;
&a代表的是a变量第一个字节的地址,也即是a的指针。
2)取函数的地址
int fun() { }
&fun即函数的指针,也即函数代码所在空间第一个字节的地址,只不过为了方便使用,往往将&省略,也就是说函数名可以直接代表函数的指针。
也就是说fun与&fun等价。
3)取数组的地址
int buf[7] = {0};
&buf为整个数组的地址,注意是整个数组的地址,不是数组第一个元素buf[0]的第一个字节的地址(指针),&buf[0]和buf才是,与数组指针相关的内容,我们留到讲数组时再介绍。
1.2.2 *
*作为双目运算符时是乘号,作为单目运算符时则与指针有关。
(1)用于组建指针类型
*与int/float/struct Student等类型相结合,构成指针类型。
int a = 100; int *p = &a;
此时p和a是两个不同的变量,各自拥有自己独立的变量空间。
a:是整形变量,用于存放整形数100
p:是指针变量,用于存放整形变量a的指针。
由于p中存放了a的指针,所以我们说p也指向了a,但是我们前面说过,本质是p里面放的指针指向a。
什么叫“指向”?
答:指向的意思就是,可以通过这个地址找到对应的空间,并访问它。
(2)解引用时
int a = 100; int *p = &a; *p = 200; //*p:解引用
此时整个*p代表就是p中指针所指向的空间,也就是a的空间,所以*p=200与a=200的效果是一样的。
此时*的作用就是解引用,解引用时*p是一个整体。
这里还要注意的是,使用*解引用时,其实解引用的是p中的指针,而不是p本身,p只是装指针的容器。
总结解引用的目的:找到指针所指向空间,找到后我们就可以访问(读写)它了
读:从空间读数据
int a = 100; int *p = &a; int c = *p; //从*p所代表的空间中读数据
写:修改空间数据
int a = 100; int *p = &a; *p = 200; //往*p所代表的空间中写数据
1.2.3 在c程序中,获取存储空间地址(指针)方式有哪些
(1)直接获取数字形式的地址
int *p = (int *)0x4234f5e4; *p = 200; //向0x4234f5e4所指向的空间,写入200
像这种方式有个前提,那就是程序员需要事先知道这个地址所对应的空间是可用的,可用的意思是?
1)地址对应的空间存在
2)这个空间没有被别人(变量、常量、代码)使用
3)空间的访问权限,能够满足我们的访问要求
在纯应用的c程序中,我们是不知道具体哪个地址是可用的,我们只能采用&a等方式来使用,编译器在编译时,会将它变为具体的地址值,程序员不需要具体的地址值。
疑问:什么时候会直接使用数字形式的地址呢?
答:这个一般在单片机、驱动等偏硬件的c程序中才会用到,因为开发偏底层的程序时,往往会直接通过数字形式的地址来访问存储空间,为什么会这样呢?
其实只要你学习过单片机和驱动,你很容易就能理解我所说的,否则就算我解释了你不太明白,所以目前大家只需要知道存在这种情况即可。
我们目前讲的各种c例子程序,都是属于应用的c程序,与底层硬件无直接关系,所以我们不会用到纯粹数字形式的地址。
疑问:进行单片机、驱动开发时,我怎么知道具体那个数字地址可用?
答:我们需要产看硬件的说明手册。
(2)使用&
int a; int *p = &a; *p = 200;
作为程序员的我们,其实并不知道a的指针具体是多少,所以无法直接使用数字形式的地址,所以只能使用&a方式来代表,编译器会把它变为具体的地址。
这里一定要注意,a不是指针,a只是一个的符号,&a才是a的指针。
(3)malloc返回地址
int *p = malloc(4);
malloc从堆中开辟空间后,程序员也不需要知道这个空间的指针(第一个字节的地址)是多少,也没法知道,因为malloc在运行的过程时才会到堆中开辟空间,到那个时候才知道。
所有只能是在运行的过程中,让malloc将地址(指针)返回并放在p中,然后通过p去间接的使用指针。
(4)某些符号本身就是地址(指针)
1)例子1
char *p = "hello wolrd,it is butefull";
"hello wolrd,it is butefull"是一个字符串常量,它放在了.rodata中,而p中放的是"hello wolrd,it is butefull"所在常量空间的指针。
千万不要以为字符串存放在了p中,p只有4或8个字节,这个字符串存放时需要十几个字节,不可能将字符串放到p中的,所以p中只能放字符串的指针,然后通过这个指针去访问常量空间中的字符换。
对于程序员来说,我们并知道字符串所在常量空间的指针是多少,所以在c语法中,此时整个字符串"hello wolrd,it is butefull"就直接代表那个指针。
疑问:char buf[] = {"hello wolrd"},这种情况也是的吗?
答:这种情况不是的,此时字符串字节放在了数组中,所以这里的"hello wolrd"不代表指针。
2)例子2
int fun() { }
fun这个符号就是函数的指针,即函数代码所在空间的第一个字节的地址。
1.2.4 解引用时,指针的使用方式有哪些
两种,一种是直接使用,另一种是间接使用。
(1)直接使用
直接对指针进行解引用,这就是直接使用。
1)例子1
*((int *)0x43355435) = 100;
2)例子2
int a; *(&a) = 200;
(2)间接使用
先放到指针变量中,然后通过指针变量去使用。
int a = 10; int *p = &a; *p = 200;
这种间接的使用方式,才是我们最常见的方式。
这里一定要注意,指针变量只是一个放指针的“篮子”而已,真正起作用的是里面所放的指针。
1.2.5 *和&互为逆向运算符
int a = 100; int *p = &a; *p = 200;
由于p中放的是&a,而解引用的是p里面的指针,所以*p = 200实际上就等价于*&a = 200;
*&a的意思就是,找到&a这个指针所对应的空间,其实就是a的空间,因此*&a = 200等价于a=200。
其实相当于*和&相互抵消了,因为这两个能相互抵消,所以*和&互为逆向运算符,就好比+-、*/互为逆向运算一样。