目录
一、 内存和地址
假设你在一栋宿舍楼里,但是房间没有编号,你的一个朋友如果想找到你,就得挨个房字去找,这样效率很低,但是我们给每个房间编上号,如:101,402……,有了房间号,如果你的朋友得到房间号,就可以快速的找到你。
那么在计算机中,我们该如何快速准确的找到数据?
在计算机上CPU(中央处理器)在处理数据的时候,需要的数据是在内存中读取的,处理后的数据也会放回内存中,电脑上内存分为8GB/16GB/32GB等,那这些内存空间如何高效的管理呢? 其实也是把内存划分为一个个的内存单元,每个内存单元的大小取1个字节。
其中,每个内存单元,相当于一个宿舍,一个字节空间里面能放8个比特位,就好比同学们住的8人间,每个人是一个比特位。每个内存单元也都有一个编号(这个编号就相当于宿舍房间的门牌号),有了这个内存单元的编号,CPU就可以快速找到一个内存空间。生活中我们把门牌号也叫地址,在C语言中把内存单元的编号称为指针。
二、 指针变量
指针理解的两个要点
1.指针是内存中一个最小单元的编号,也就是地址
2.平时口头语中说的指针,通常指的是指针变量,是用来存放内存地址的变量
内存单元编号==地址==指针
例如:
a是一个整型变量,整型变量占用4个字节,每个字节都有地址
&a取出的是第一个字节(较小的地址)的地址
1. 指针变量
若
int* pa = a ;
则pa就被称为指针变量
指针变量也是一种变量,用来存放地址,地址唯一标示一个内存单元。
存放在指针变量中的值都会理解为地址。
2. 拆解指针类型
这里pa左边写的是int * , * 是在说明pa是指针变量,int 是在说明pa指向的是整型类型的变量
3. 解引用操作符
使用解引用操作符( * ),我们只要拿到了地址(指针),就可以通过地址(指针)找到地址(指针) 指向的对象
例如:
* pa的意思就是通过pa中存放的地址,找到指向的空间,* pa其实就是a变量了,* pa=0,这个操作符是把a改成了0
再例如:
该代码将n的4个字节全部改为0
该代码仅将n的第一个字节改为0
结论:
指针的类型决定了,对指针解引用的时候有多大的权限(一次能操作几个字节)。
比如char* 的指针解引用就只能访问一个字节,而int* 的指针的解引用就能访问四个字节。
4. 指针变量的大小
32位机器,假设有32根地址总线,每根地址线出来的电信号转换成数字信号后是1或者0,那把32根地址线产生的2进制序列当做一个地址,那么一个地址就是32个bit位,需要4个字节才能存储,指针变量的大小就是4个字节。
64位机器,假设有64根地址线,一个地址就是64个二进制位组成的二进制序列,需要 8个字节的空间来存储,指针变量的大小就是8个字节。
int main()
{
printf("%zd\n", sizeof(char*));
printf("%zd\n", sizeof(short*));
printf("%zd\n", sizeof(int*));
printf("%zd\n", sizeof(double*));
return 0;
}
结论:
• 32位平台下地址是32个bit位,指针变量大小是4个字节
• 64位平台下地址是64个bit位,指针变量大小是8个字节
• 指针变量的大小与类型无关,指针类型的变量,在相同的平台下,大小相同。
三、指针运算
1. 指针+-整数
首先看这样一段代码:
int main()
{
int a = 0x11223344;
int* pa = &a;
char* pc = &a;
printf("%p\n", pa);
printf("%p\n", pa + 1);
printf("%p\n", pc);
printf("%p\n", pc + 1);
return 0;
}
打印结果为:
根据结果得知,pa+1跳过了4个字节,pc+1跳过了1个字节(&a被强制类型转化为char*类型)。此看出,指针+-整数表示访问该元素的前后位置的元素,而指针类型决定指针的步长(指针+1到底跳过几个字节,与指针所指向的类型有关),即指针类型的意义。
2. 指针- 指针
前提: 两个指针必须指向同一块空间
先看代码:
int main()
{
int arr[10] = { 0 };
printf("%d\n", &arr[9] - &arr[0]);
return 0;
}
VS中调试结果为:
由此得出,指针- 指针表示两个指针之间的元素个数
此外,还可以用指针- 指针的方式模拟实现strlen的功能:
int my_strlen(char* arr)
{
char* p = arr;
while (*p != '\0')
p++;
return p - arr;
}
int main()
{
char arr[] = "abcdef";
int len = my_strlen(arr);
printf("%d\n", len);
return 0;
}
3. 指针比较大小
指针之间比较大小与数字之间类似,直接比较数值(前后)
例如:
#define N_VALUE 5
int main()
{
float values[N_VALUE];
float* vp;
//指针+-整数
for (vp = &values[0]; vp < &values[N_VALUE];)
{
*vp++ = 0;
}
return 0;
}
vp++,即访问数组的下一个元素,结果为整个数组都被初始化为0
再例如:
#define N_VALUE 5
int main()
{
float values[N_VALUE];
float* vp;
for (vp = &values[N_VALUE - 1]; vp > &values[0];)
{
*--vp = 0;
}
return 0;
}
* --vp = 0 ;
等价于
vp-- ;
*vp=0 ;
该代码与上面代码相同,只是该代码是从后往前初始化数组
但是注意,该代码的简化版本则会出现问题,即改为:
for (vp = &values[N_VALUE - 1]; vp >= &values[0]; vp--)
{
*vp = 0;
}
修改后的代码与原代码完全相同,但是违反了标准规定:
允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针比较,但是不允许与指向第一个元素之前的那个内存位置的指针进行比较。(可以与数组后面比较,但不能与数组前面比较)。
虽然大部分编译器遇到该代码不会报错,但是该标准还是要注意一下
此篇完,感谢阅读