结束了C语言操作符的学习,接下来开始学习C语言中的指针。
1、指针是什么?
a、电脑内存
我们都知道电脑有内存,8G,16G乃至32G,那么电脑中这么大的内存该如何管理呢?
实际上,我们将电脑如此庞大的内存划分为一个个小的内存单元,每个内存单元大小为1个字节,这些内存单元在电脑中连续排列。
但为什么每个内存单元大小为1个字节呢?这其实与C语言中各个数据类型的大小密不可分。
我们知道有bit、byte,还有kb、mb、gb等等。太大的不用考虑,主要考虑bit、byte以及kb.如果一个内存单元是一个bit大小,未免有些太小,一个char类型的变量竟要分配八个内存单元进行储存 ,这样显然不好;而如果使用kb的话,储存一个char类型的变量虽然一个内存单元就够用了,但这个内存单元会有很大的冗余,造成无意义的内存空间浪费,所以kb也不行。而byte就刚刚好,这样一个内存单元刚好可存储一个char类型的变量,用于存储的内存单元数少,同时又不存在内存的浪费。因此,最终一个内存单元的大小就定位一个字节。
b、地址
但是,光划分内存单元还并不能很好地管理内存,还得给每个内存单元标号,于是就有了地址。也就是说,地址就是内存单元的编号。
电脑是如何表示地址,即电脑是如何对内存单元进行编号的呢?
对于32位(x86)的机器,相当于有32根地址线,地址线有通电和不通电两种状态,用二进制表示的话就是1和0。也就是说,32根地址线对应二进制中的32位数.不过地址通常都用十六进制显示,32根地址线对应十六进制中的八位数。
对于64位(x64)的机器,与32位基本相同,只不过地址线的数量翻倍,有64根。
这样来看,地址本质上就是一个用十六进制显示的数值。
c、指针和指针变量
那么指针是什么呢?
指针本质上就是地址,即内存单元的编号,但我们口头上讲的指针,其实通常是指指针变量,一种用于存储指针的变量类型。简而言之,指针变量存储指针,指针变量存储地址。
指针变量既然是一种变量类型,那么它占据多少内存呢?其实考虑这个问题,本质上就是考虑指针变量中所存储的地址的大小。
鉴于机器位数的不同,指针变量的大小也有所不同。对于32位的机器,用32个二进制位表示地址,所以指针变量的大小为4个字节;对于64位的机器,用64个二进制位表示地址,所以指针变量的大小为8个字节。
2、指针变量的类型
a、指针变量有哪些类型
指针变量的类型,取决于它所指向的变量的类型。比如说,指针变量指向的是一个整型变量,那么这个指针变量的类型便是int*;如果指向的是一个字符型变量,那便是char*,以此类推。
b、为什么要有不同类型的指针变量
我们可以看到,不同类型的指针变量,它所占据的内存大小是相同的,既然如此,为什么不将指针变量定义为一个统一的类型,而要区分不同类型的指针变量呢?
这个问题可以从以下三个方面来解释:
1、解引用的访问权限
不同类型的指针变量决定了这个指针变量在解引用时能够访问的字节数。
例如,char*类型的指针变量在解引用时能访问1个字节,int*类型的指针变量在解引用时能访问4个字节。
通过调试时的内存窗口,可以清楚地看到这一点:
在内存窗口输入&a,找到a的地址:
在对pa进行解引用操作并赋值100后,可见:
这说明pa的解引用操作访问了四个字节
同理,pc的解引用操作:
这说明pc的解引用操作访问了1个字节
但如果我们做如下调整:
此时,我们将c的地址赋给pa,将a的地址赋给pc。对于整型变量a而言,相应的指针变量解引用时应该访问4个字节,才能正确地对a进行操作;相应地,对于字符型变量c而言,相应指针变量解引用时应该访问1个字节。但现在,该访问1个字节时,访问了4个字节;该访问4个字节时,却只访问了1个字节。具体如下所示:
对于a:
对于c:
这样显然是错误的
2、解引用的数据类型
指针变量在解引用时,找到的变量类型是确定的。
int* 始终默认找到的变量是整型变量,char* 始终默认找到的变量是字符型变量,以此类推。
在这样的情况下,如果将指针与指针变量不正确匹配的话,即便两个不同数据类型所占内存空间相同,也会出现问题:
显然,这个输出结果并不是我们所想要的。
出现这个问题的原因在于将一个整型变量a的地址赋值给了float*类型的指针变量pf。而在对pf进行解引用时,编译器默认通过pf所找到的变量,其类型是float,因而对该变量,也就是a赋值时,采用的float类型的赋值方式,在内存中进行相应操作时,也是讲a看作float类型的变量进行操作的。而浮点型变量与整型变量,二者在内存中的存储方式是截然不同的。因而,就出现了上图所示的奇怪输出。
此点,通过调试时的内存窗口可以有所窥见:
解引用前的a:
解引用后的a:
而正常情况下解引用后应是这样的:
3、指针变量的步长
不同类型的指针变量在进行地址移动,即加减整数操作时,跳过的内存空间是有所不同的,即不同类型的指针变量的步长是不同的。
可以看到,pa+1移动了4个字节,pc+1移动了1个字节。这说明,不同类型的指针变量进行加减整数操作时,跳过的内存空间大小等于该指针变量所指向的数据类型所占内存空间的大小。
综合以上三点,区分不同类型的指针变量是很有必要的。