浅谈C语言指针
说说自己的一点理解,有不对的地方还希望大佬们多多指正。
CPU是怎么取数据的?
CPU是控制计算机运作的核心部件,但是想让一个计算机工作必须要向他提供指令和数据,指令和数据是存放在存储器中的,也就是我们平时所说的内存。
存储器划分成许多存储单元,每个存储单元都有一个编号,也就是地址,当CPU想要访问数据的时候,它需要先找到这个数据所在的地址,然后再读取数据,同时还需要知道使用什么器件操作。
由此引出CPU进行数据的读写时需要的三个条件:
- 地址信息(通过地址线操作)
- 控制信息(通过控制线操作)
- 数据信息 (通过数据线操作)
CPU通过地址线去到指定的地址取出数据再通过数据线返回。
什么是指针?
在计算机中,所有的数据都存放在内存中,不同的数据存放在不同的内存中,不同类型的数据占用内存的大小也不一样,为了计算机能够正确的访问到内存中的数据,就需要为每块内存编上一个唯一的号码,类似于门牌号,这样计算机就可以通过这个唯一的“门牌号”正确的找到存放在内存中的数据,而指针的本质就是这些所谓的"门牌号"。
取地址符
那么,我们怎么才能看到这些地址("门牌号")呢?
在C语言中有一个运算符叫取地址运算符(&),我们可以尝试通过取地址符的方式来输出一个变量在内存中的地址:
#include <stdio.h>
int main(void){
int a = 1;
int b = 2;
int c = 3;
printf("%x\n",&a);
printf("%x\n",&b);
printf("%x\n",&c);
return 0;
}
输出:
因为在计算机中地址是以十六进制表示的,所以这里的输出采用十六进制的方式。可以看到&a
输出的值是62fe1c
,这个62fe1c
就是a这个变量在内存中的地址,这个a表示的就是1,也就是说,1这个数据存放在内存中地址为62fe1c
的地方,同理,2存放在62fe18
这个地址,3存放在62fe14
这个地址。那么他在内存中是什么样的呢?
如图:
按理来说我们存放的三个变量的存储地址应该是从小到大,而不是现在的从大到小,这是因为采用了小端存储模式,数据依次存放的地址是从大到小的,感兴趣的可以深究一下,这里就不再赘述。从图上还可以看到他们的地址之间相差4,这是因为我们声明的a,b,c都是int类型的,int类型是四个字节的,所以他们之间相差的值是4,如果我再声明一个char d
,因为他是char
类型,占一个字节,所以他的地址就是62fe13
。
指针类型
在C语言中,有许多的数据类型,例如int类型的变量存放的是整型数据,char类型的变量存放的是字符类型,而指针类型的变量,存放的则是地址,换而言之,就是地址一般是存放在指针类型的变量中的。
那么我们怎么定义一个指针类型的变量呢?定义指针类型的变量与定义普通的变量是一样的,只是需要加上指针运算符------星号(*),例如,int *p
,这样声明之后,p就是一个指针类型变量,指向一个整型的变量,p中存放的就是这个整型变量的地址,*p
是int类型(如果看完有点懵,没关系,先往下看)。例如:
由之前的代码我们可以知道,&a取的是变量a的地址,然后我们定义了一个指针类型的变量p用来存放这个地址,所以当输出变量p的时候,也就是输出p的值时,输出的就是变量a的地址。
既然p也是一个变量,也是用来存放的值的,那么,他是不是也应该有一个地址呢?
我们可以尝试用取地址符去取p的地址试试:
通过输出不难发现,p虽然是指针类型,但是他也是一个变量,也是有地址的,特殊的是,这个变量是用来存放地址的。那么他们在内存中是什么样的呢?如图:
现在我们知道了指针类型变量p里面存放的是地址,CPU是通过地址取数据,那么既然我们知道了地址,是不是可以通过地址去取数据呢?答案是可以的。
解引用运算符
以int *p
为例,既然指针变量p存储了数据的地址,我们就有办法取得变量p中存储的地址上的数据------通过解引用运算符(*),他的作用就是取出这个变量指向的值,以*p
为例,就是取出p指向的值,而p中存放的是62fe1c
,所以*p
可以理解为*62fe1c
,也就是取62fe1c
这个地址上的值,而62fe1c
这个地址上存放的是1,那么*p
取出来的就是1,而这个1是int类型,这就解释了上面的*p
是int类型。代码实现:
那么现在就可以简单的理解为使用* 地址
的方式可以取出地址中的数据。
数组指针
我们先来看看数组在内存中是什么样的。
首先,定义一个有五个值的数组,存放1、2、3、4、5五个数字,然后我们通过取地址符看看他们在内存中的地址。
通过输出不难看出,这五个数字存放在五个连续的地址中,我们定义数组的时候给数组定义的变量是arr,用来存放五个数字,那么这个变量是不是也有一个地址呢?我们尝试输出arr的地址看看:
通过输出可以看出,arr的地址和arr[0]的地址是一样的,其实,arr默认就是指向数组中第一个地址的位置,后面每一个位置的数据都可以在首地址的基础上进偏移下标个单位的位置找到,二维数组也是如此,既然我们知道数组中第一个数字的地址,而这个数组中的数据又是连续存放的,那么我们是不是可以通过指针的方式去取数组中的数字呢?将第一个数字的位置赋给一个指针变量,然后通过首地址进行位置偏移是不是也能够找到其他数字呢?如图:
可以发现效果是一样的,我们将数组arr的地址赋值给指针变量p,然后将指针变量偏移1,取到的数值就是arr[1],也就是说p[1]
和arr[1]
的效果是等价的。