内存和地址
在谈内存和地址之前,我们先看看生活中的例子:
我们的宿舍楼的每个房间都有一个编号,刚开学时我们并不知道我们是具体住在这栋楼的哪个房间的,试想一下,如果宿管阿姨不给我们具体的房间号,只是口头告诉我们几楼,从左往右或者从右往左的哪个哪个房间,这样是不是既费时又费力,还可能找错。这只是假设,实际上都会给我一把钥匙,钥匙上面就有这个房间的编号,比如:
4–302 就代表4楼302室
我们只需要拿着这个编号找到对应的房间就可以入住了。
看完这个例子,那放在计算机中又是怎样的呢?其实计算机也是类似这样处理的,计算给每个内存单元(一个字节)都进行了编号,32位平台有2^32 种编号,64位平台就有2^64种编号。计算机要访问某个内存单元的时候,拿着这个内存单元的编号就可以快速访问了。生活中我们把宿舍房间号也叫地址,计算机中把内存单元的编号也叫地址,C语言中把地址也叫作指针。因此我们可以这样理解:内存编号 = 地址 = 指针。
取地址操作符 &
我们知道在C语言中,创建变量的过程就向内存申请空间,例如:
在这个代码中,创建了int 类型的变量data,向内存申请了4个字节的空间,每个字节都有一个地址:
- 0x010FFAE8
- 0x010FFAE9
- 0x010FFAEA
- 0x010FFAEB
那我们又该如何拿到这些地址呢?这就需要一个操作符:取地址操作符&
这样就可以获得变量data的地址进行打印了,而且我们还注意到,我们拿到的地址是第一个字节的地址,也就是4个字节中较小的那个地址。
我们取出变量的地址难道只是为了打印吗?当然不是,肯定是为了以后我们使用,那就需要一个变量把这个地址存储起来,这个变量就叫做指针变量,顾名思义,存放指针(地址)的变量。
怎么表示呢?
int * p = &data,我们该如何理解这个表达式呢?可以这样理解,*表明p是指针变量,int 表明 p 指向的对象data是int类型。
作为类比,一个字符变量的地址放到指针变量中就可以这样表示:
char ch = ‘A’;
char *p = &ch;
解引用操作符 *
我们将地址保存起来,未来是要使⽤的,那怎么使⽤呢?在现实⽣活中,我们使⽤地址要找到⼀个房间,在房间⾥可以拿去或者存放物品。C语⾔中其实也是⼀样的,我们只要拿到了地址(指针),就可以通过地址(指针)找到地址(指针)指向的对象,这⾥必须学习⼀个操作符叫解引⽤操作符(*)。
上⾯代码中就使⽤了解引⽤操作符,* p的意思就是通过p中存放的地址,找到指向的空间其实就是data变量了,所以* p = 0,就是使用这个操作符是把data改成了0。这时可能有小伙伴在想,这⾥如果目的就是把data改成0的话,直接写成 data = 0不就完事了,为啥非要使⽤指针呢?其实这⾥是把data的修改交给了p来操作,这样data的修改就多了⼀种的途径,而且在以后得学习中我们也经常要用到指针,比如:当要调用一个函数来修改某个变量的值时,如果直接把变量当作函数的参数(传值调用)是达不到我们想要的目的的,这时就需要使用到指针(地址),也就是把变量的地址当做函数参数(传址调用)进行操作。
指针的大小
指针的大小取决于存放的地址有多大,如果是32位平台,内存单元的编号就是32个0和1组成的二进制序列:
0000 0000 0000 0000 0000 0000 0000 0000
…
1111 1111 1111 1111 1111 1111 1111 1111
地址大小就是32/8 = 4(字节),同理,64位平台就是64/8 = 8(字节),因此32位平台指针的大小就是4个字节,64位平台是8个字节。
注意指针变量的大小和类型是无关的,只要是指针类型的变量,在相同的平台下,大小都是相同的。
指针变量类型的意义
-
指针的解引用
先看一个代码,调试看一下会有什么不一样的地方:
我们可以很明显的看到,指针类型不一样,他们的访问权限是存在差异的,int* 类型的指针解引用一次能访问4个字节(一个整型),而char* 类型的指针解引用一次只能访问一个字节。有细心的小伙伴会发现内存中存放的数据和我们实际看到的会不一样,内存中是倒着放的,这其实是因为当超过一个字节的数据在存储的时候,会有存储循序的问题,如果有兴趣可以了解一下大小端字节序。 -
指针加减整数
继续上代码:
我们可以看到,pc、pi的值是相同的,但是pc+1与pi+1就不同了,pc+1与pc的值差1,而pi+1与pi的值差4,这是因为pc是char* 类型,加1跳过一个字符的大小(一个字节),pi是int* 类型,加1跳过一个整型的大小(4个字节)。
结论:指针的类型决定了指针解引用时的访问权限大小,也决定了指针加减整数时跳过多少个字节。
指针-指针
指针-指针的绝对值=指针与指针之间元素的个数,前提是指针是在同一块空间内,不然没有意义。我们来做一道题目,模拟实现strlen函数求字符串长度。
#include <stdio.h>
int my_strlen(char* str)
{
char* start = str;
while (*str != '\0')
{
str++;
}
return str - start;
}
int main()
{
char arr[] = "Hello world";
int len = my_strlen(arr);
printf("len=%d\n", len);
return 0;
}
这里我们就使用指针-指针的操作。