一. 指针是什么?
1.指针是内存单元中一个最小单元的编号,也就是地址。
2.平时口语中的指针,其实就是指针变量,用来存放内存地址的变量。
如:创建一个int类型的变量,就需要在内存中开辟四个单元并且编号是从小到大。
1.地址在内存中是怎么编排的?
对于32位的机器,假设有32根地址总线,每根线在寻址的时候会产生高电平(高电压)和低电平(低电压)就是1或者0。
32根线产生的地址就是:
如图:
一共有2^32个地址,每个地址标识一个1字节(Byte),那一共就是2^32Byte = (2^32Byte/1024)KB = (2^32Byte/1024\1024)MB = (2^32Byte/1024/1024/1024)GB = 4GB
所以在32位的机器上地址是由32个0或1组成的二进制序列,一个序列由4个字节来存储,即一个地址\指针在内存中占4个单元(4字节)。
根据以上分析,在64位的机器上,一个二进制序列要8字节来存储,所以指针\地址大小就为 8字节
二. 指针和指针类型
指针\地址 大小为 4\8 字节,不管创建什么类型的指针他们的大小都相等。
1.指针类型的意义
<1>
为什么两个十六进制等于一字节?
二进制的数越大表达越复杂,这时就引入了十六进制,更大的二进制数用十六进制更好表达。
->测试
-> 会报警告(两边类型不兼容)但是可以运行。
-> 将a强制类型转换成char*之后编译器没有报错,因为指针\地址的大小和int型的大小一致
-> 对指针变量b解引用之后,通过调试我们可以看到a的地址位图中所示(图中所示为十六进制)
-> 我们接着调试,可以发现我们通过b将a的地址改写了,但是只改写了一个内存单元
结论:指针类型只决定了指针在解引用的时候访问几个字节
如:
int*在解引用时访问4个字节
char*在解引用时访问1个字节
………………
<2>
->通过下图可以看到pc,pb的地址是一样的
->但我们给pc,pb分别加1可以看到
->pc加1移动了4个字节,pb加1移动了1个字节
结论:指针的类型决定了进行加1减1操作时跳过了多少字节,决定了指针的步长。
2.野指针
野指针:指针指向的位置是不可知的(随机的,不正确的,没有明确限制的)。
例1:
报错有两个原因
1.p没有初始化,就意味p没有明确的指向,p就是野指针。
2.对野指针解引用,一个局部变量不初始化的话,放的是随机值(如:0xAAAAAAAA),这个随机值的内存单元并不是野指针p所属的,这就导致了p非法访问空间 。
例2:越界访问成野指针
数组越界,p指向的地址不属于p(非法访问空间)。
例3:
int* test()
{
int a = 10;
return &a;
}
int main()
{
int* p;
p = test();
}
这种情况p属于野指针
因为a开辟的空间是临时的,当子函数test将&a传给p的时候就被系统给回收了,这时p就是一个野指针。(a的地址没有消失只是不属于p了属于系统,p还是指向的这个地址但是这个地址已经不认p了)
那我们怎么预防野指针的出现了?
1.创建指针时应该赋什么值赋什么值
2.不知道赋什么值就赋空指针(NULL)
例4:
三.指针运算
例1:
不是所以的指针都可以相减,指向同一块空间的指针才能相减,不在同一块空间相减没意义,(指针相加(0xAABBCCDD+0xAABBCCDE)是没意义的,如:我们可以用时间减时间这个例子来解释,2024年-2015年=9年,可以用来相减但是加起来就会很奇怪,2024年+2015年=4039年,这个时间算出来就没什么意义)。
例2:不在同一块空间相减
用法:
<1>求字符串长度
int My_strlen(char* str1)
{
char New_str2 = str1;
while((str1++) != '\0')
{
;
}
return (str1 - New_str2);
}
<2>给数组初始化
//第一种
#define value 5
int arr[value];
for(pr = &arr[value]; pr < &a[0]; (*--p) = 0) //需要--p因为数组下标是从5开始的,别越界访问了
{
;
}
//第二种
for(pr = &arr[value-1]; pr <= &a[0]; (*p--) = 0)
{
;
}
标准规定:允许指向数组元素的指针与指向最后一个数组元素后面那个内存位置的指针作比较,但是不允许与指向第一个元素前面那个内存位置的指针作比较
四. 一级指针和二级指针
1.一级指针
一级指针:它用于储存变量的地址。
2.一级指针的定义
整型指针为例
声明一个一级指针时,需要指定指针所指向的数据类型例如:int* p;表示p是指向整型数据的一个指针
3.一级指针的赋值
例:int a, *p = &a, 这里&a是a的地址,将a的地址赋给指针变量p,这样指针p就指向了变量a,将p解引用(*),得到的就是取地址a中的数据
对指针变量p解引用:*p = 3;a的值也会被更改,因为他们所在的是同一个地方,反之a的值改变*p也会改变。
4.二级指针
例1:
int a = 10;
int* p = &a; //p是一个一级指针变量
&p //对p取地址
int** pr = &p; //ps是一个二级指针
一级指针对a进行改值:*p = 20;
二级变量对a进行改值:**p = 20; 解引用地址的地址
例2:int* p的类型是什么了?
总结:
一级指针是用来存放变量地址的
二级指针变量是用来存放一级指针地址的(不要理解为存放地址的地址)