一、符号&
1、作为双目运算符使用,是“位与“运算符。
2、作为单目运算符,是取地址运算符。
1)取变量的地址
2)取函数的地址,例如”&函数名“,即函数的指针,也就是函数代码所在空间的第一个字节的地址。我们往往在使用的时候将&省略,也就是说函数名直接代表函数的指针。函数名与&函数名等价。
3)取数组的地址,如 int buf[7]={0}; 其中&buf是整个数组的地址,并不是数组第一个元素buf[0]的第一个字节的地址。&buf[0]和buf才是数组第一个元素的地址。
二、符号*
1、用于组建指针类型
int a=100;
int *p=&a;
此时p和a是两个不同的变量各自拥有自己的独立变量空间:
a:整型变量,存放整型数100。
p:指针变量,用于存放整型变量a的指针。
指向也就是说:可以通过这个地址找到对应的空间,并访问它。
2、解引用
int a = 100;
int *p = &a;
*p = 200; //*p:解引用
此时整个*p代表就是p中指针所指向的空间,也就是a的空间,因此*p=200与a=200的效果一样。在解引用的时候*p是一个整体。
本质:在解引用的时候,其实是解引用p中的指针,而不是p本身,p只是存放指针的容器。也就是指针变量与指针的关系。
三、获取存储空间地址(指针)的方式
1、直接获取数字形式的地址
int *p = (int *)0x4234f5e4; // 这里强制转换为int,如果没转,就是普通的整型数
*p = 200; //向0x4234f5e4所指向的空间,写入200
像这种直接获取的方式其实是有个前提的,就是我们需要事前知道这个地址所对应的空间是可用的:
1)地址对应的空间存在
2)这个空间没被占用
3)空间的访问权限
这种数字形式的地址,也只有我们嵌入式工程师使用了,因为在开发偏底层的c程序中,往往才会直接通过数字形式的地址来访问地址空间。(通过查阅产品手册得到数字地址)
2、使用&
int a;
int *p = &a;
*p = 200;
因为我们并不知道a的指针到底是多少,所以我们无法直接使用数字形式的地址,只能通过&a的方式来代表,编译器会帮助我们把它编程具体的地址。
这里a并不是指针,&a才是a的指针。
3、malloc返回地址
int *p = malloc(4);
malloc从堆中开辟空间,我们也不知道这个空间的指针(第一个字节的地址)是多少,也没办法知道,因为malloc在运行的过程时才会到堆中开辟空间。
所以只能在运行的过程,让malloc将地址返回并放在p中,然后通过p去间接的使用指针。
4、某些符号本身就是地址
char *p = "hello wolrd,it is butefull"
p中存放的是这个字符串所在的常量空间的指针,p指针存放4个或8个字节,这个字符串明显在存放的时候就需要十几个字节,因此p中只能存放字符串的指针,然后通过这个指针去访问常量空间中的字符串。
还有一种特殊的情况,函数名就是函数的指针,也就是函数代码所在空间的第一个字节的地址。
四、解引用时,指针的使用方式有哪些
1、直接使用
2、间接使用
1、直接使用
1)*((int *)0x43355435) = 100;
2)如下
int a;
*(&a) = 200;
2、间接使用
先放在指针变量中,然后通过指针变量去使用。
int a = 10;
int *p = &a;
*p = 200;
一定要区别开指针变量和指针的区别,指针变量只是个容器,指针才是真正起作用的。
五、 * 和 & 互为逆向运算符
int a = 100;
int *p = &a;
*p = 200;
由于p中放的就是&a,解引用是p里面的指针,所以*p=200;实际上就等价于*&a=200;
其实也就相当于这俩符号相互抵消了,这俩符号互为逆向运算符。
六、指针变量空间大小
所有指针变量的大小(宽度)都是固定的,因为存储空间所有字节的指针都是相同”宽度“的。
- 比如,如果地址宽度是
32
位的话,那么每个字节的地址都是32
位的 - 比如,如果地址宽度是
64
位的话,那么每个字节的地址都是64
位的
既然所有字节的指针的宽度都是一样的,那么所有指针类型的宽度也都是一样的。
指针变量是用来存放指针的,既然所有指针的宽度都是一样的,那么用来存放指针的指针变量的宽度也是一样的。
可以测试一下不同系统的地址宽度->
#include <stdio.h>
int main(void)
{
int *p = NULL;
printf("%d\n", sizeof(p)); //或者sizeof(int *))
return 0;
}
七、指针类型
1、指针类型是什么
指针类型由基本类型+*构成
int *
float *
...
2、定义指针变量
定义的时候,在指针类型的*后面加上变量名即可
int *p;
int **p;
3、指针类型中基本类型的应用
Q: 前面不是说了吗,所有指针类型的宽度都是固定的。既然都是固定的,那么我们在种子很类型中区分的那么详细干嘛?
A:意义就在于,正是由于这些基本类型的存在,因此我们在对指针解引用的时候,才能正确的解释指针所指向的空间。
int a = 100;
int *p = &a;
*p = 10000;
a的空间有4个字节,访问a的空间需要知道下面三件事:
- (1)知道
a
第一个字节的地址,即a
指针 - (2)知道访问哪一个字节时结束
- (3)知道空间的存储结构
当*p解引用的时候:
1)p中存放的就是a指针。
2)int*的基本类型就是int,所有访问指针所指向的空间的时候,需要访问的空间大小为四个字节。
3)int*中的int也指明了,在访问指针所指向的空间时,按照整型的存储结构来解析。
总之,在对指针进行解引用的时候,指针类型中基本类型的作用就是决定对所指向空间的解释方式。
指针类型都是固定长度的,指针类型中的基本类型,与指针本身宽度无关,至于指针指向空间的宽度有关。
八、对指针强制类型转换
1、天然的一致
int a = 100;
int *p = &a;
2、通过强制类型转换来保证类型一致
double a = 100.6;
float *p = &a; //隐式强制转换
float *p = (float *)&a; //显式强制转换
3、强制类型转换做了什么
在强制类型转换的时候,&a的值没有变,变的只是指针所指向空间的解释方式。
&a:指针类型为double*
p:float*
两边的类型不一致,因此需要做强制类型转换,然后会被以左边的类型进行解析
struct Student
{
int num;
char name;
float score;
};
struct Teacher
{
char name;
int num;
};
struct Student stu = {123, "zhangsan", 87.6};
struct Teacher *stup = (struct Teacher *)&stu; //&stu为结构体指针,即第一个字节的地址
指针类型被强制转换成struct Teacher *后,通过stup解引用会访问stu空间,这时会以struct Teacher类型进行解析。
九、解释指针
指针就是一个数,至于这个数怎么解释,要看这个数的类型。
int a = 0;
&a:为int*的指针,解引用时按照整型来解释&a所指向的空间。
(int)&a:此时是一个普通的整型数
(void*)&a:为void*指针,这是一个空类型指针。空类型指针仅仅就是一个指针,不能被解引用,如果要解引用,必须被强制转换为其他具体指针类型,才能进行解引用。
(struct student *)&a:结构体指针,解引用的时候会按照 struct student结构进行解释。
在实际编程的时候,一定要慎重使用强制类型转换,避免访问其他非法空间。