计算机萌新的成长历程——初识C语言18

大家好,很高兴又和你们见面了,今天咱们要分享的内容是指针的相关内容。

在分享指针前,我们要先了解以下几个知识点:

1.计算机存储数据的方式和关键字register:

计算机要存储数据的话有以下几种途径,按访问速度由快到慢来排列分别是:寄存器>高速缓存>内存>硬盘。它们的存储空间大小是依次增大的,寄存器的存储空间大小最小,硬盘存储空间大小最大。

计算机在进行运算时是先从内存中提取数据然后将数据输送到中央处理器(CPU)进行运算,刚开始的时候内存的读取速度和CPU处理数据的速度是非常匹配的,但是随着时代的发展,CPU的处理速度越来越快,快到内存的访问速度跟不上了,后面为了解决这个问题,就开始出现了有更快访问速度的高速缓存和比高速缓存访问速度更快的寄存器。在这之后CPU在处理数据时会先在寄存器里拿取需要处理的数据,如果没有,再去访问高速缓存,还是没有,再去访问内存,像这样的一个访问流程大大提高了计算机的运行速度,寄存器中的数据又是怎么来的呢?它是先由内存里的数据下载到高速缓存,再由高速缓存下载到寄存器这样一个流程。

这里就需要提到register——寄存器关键字,这个关键字是干嘛用的呢?你可以理解为它就是将数据下载到寄存器里的通道,比如我想定义一个变量int a = 10;在后面的代码中我需要多次使用它,为了更快的读取这个数据,我就可以将它定义为寄存器变量——register int a = 10;但是有一个问题,前面也提到了寄存器是空间最小的,如果我把所有内容都放到寄存器里面,它也装不下呀,那怎么办呢?这里我们就要知道register这个通道它不是一路通畅的,它是会经过筛选的,它会筛选出真正需要的数据,它筛选的途径又是什么呢?它筛选的途径就是咱们使用的编译器,我们在定义寄存器变量后,这些被定义的变量就像是拿到一张入场券一样,它们要先到编译器的面前接受审核,审核通过了,才能进入寄存器。所以我们又可以将register定义的变量称为建议将其定义成寄存器变量。

上面的内容我们只需要了解计算机有寄存器、高数缓存、内存、硬盘这四种存储方式和register整个寄存器关键字的作用就行了,不需要去深究,这里我就不多说了。

2.内存

通过前面的内容我们了解到了,中央处理器(CPU)在读取数据时是从寄存器—>高速缓存—>内存这个顺序来读取的,寄存器中的数据是由内存—>高速缓存—>寄存器这个顺序来存放的。内存是电脑上特别重要的存储器,计算机中所有的程序的运行都是在内存中进行的。

这里我们可以知道,内存它是一个空间,它是一个可以存放和读取数据的空间,就像我们的冰箱一样,里面可以存放各种各样的东西,我们为了更加高效的使用冰箱,我们就将冰箱分成了不同的区块,有冷藏区和冷冻区,冷藏区又会根据自己的喜好不同分成不同的小分区,有存放蔬菜的,有存放水果的,有存放饮料的,有存放零食的等等,为了快速的找到这些分区我们会给他们进行取名,或者是编号,比如存放蔬菜的我把它叫做蔬菜区或者把它叫做1号,存放水果的我把它叫做水果区或者把它叫做2号……我们下次在使用这些分区时只需要找到它们对应的名称或者编号是不是就可以了。在我们的内存中也同样如此,内存被划分成了一个个小的内存单元,每个内存单元都有它相应的编号,这些编号我们称为内存单元的地址。大家再思考一个问题,我们在给冰箱分区的时候是不是每个空间都会有一定的大小啊,那在内存中一个内存单元又有多大呢?这里我们直接记结论——1个字节。

(计算机中的单位划分我就不多介绍了,有兴趣的朋友可以看一下初识C语言3,这里面有介绍到。)

通过上面的这个例子,大家应该对内存单元的地址有了更进一步的理解了吧,下面我们来开始探讨如何使用这个地址。这里我们要介绍两个关键字,一个是我们比较熟悉的&——取地址关键字,一个是我们之前没有提到的*——解引用操作符。我们还要介绍一种打印方式%p——打印地址。接下来我们通过代码来理解如何使用这个地址:

代码语言:javascript
复制
//存放地址
#define _CRT_SECURE_NO_WARNINGS 1

#include <stdio.h>
#include <string.h>

int main()
{
int a = 10;
int* p = &a;//&——取地址,这里是取出a的地址并赋值给p;
//int*——地址的数据类型
//p——指针变量——用来存放地址的变量
printf("%p\n", &a);//%p——以十六进制的形式打印地址
printf("%p\n", p);
return 0;
}

接下来我们运行一下看看结果如何:

通过这个结果,我们可以得到以下结论:

1.我们在定义变量的时候就是在内存中申请了一个空间来存放数据;

2.我们可以通过&将申请的空间地址给取出来,并且能够存放进指针变量中;

3.我们存放地址的格式是:

代码语言:javascript
复制
变量类型* 指针变量名 = &变量名;//变量类型+*——这里就变成了指针变量的数据类型;
//&+变量名——把需要存放地址的变量通过取地址操作符&给取出来;
//指针变量名——和我们前面学的内容一样,都是可以根据自己的喜好来编写的,没有固定的搭配;

4.通过打印我们可以得知这个地址不是固定的,它是会随时变换的,通过%p打印出来的地址的展示形式是十六进制。

我们在存放这个地址后如何来使用呢?代码如下:

代码语言:javascript
复制
#define _CRT_SECURE_NO_WARNINGS 1

#include <stdio.h>
#include <string.h>

int main()
{
int a = 10;
int* p = &a;//&——取地址,这里是取出a的地址并赋值给p;
//int*——地址的数据类型
//p——指针变量——用来存放地址的变量
p = 20;//——解引用操作符,取出本存放的地址,并将20存放在该地址中
//*p等价于变量a
printf("a=%d\n", a);
return 0;
}

运行结果如下:

通过这里我们可以通过解引用操作符,我们成功过将地址取出来并将20存放进去了,这里就是地址的使用,指针变量在通过解引用操作符会转变成指针变量里储存的地址的变量,也即是我们这里的整型变量a。

我们继续探讨一下这个指针变量的大小是多少呢?这里我们通过sizeof来计算一下指针变量的大小:

这里我们可以看到,在64位操作系统下,指针变量的大小是8个字节,在32位操作系统下,指针变量的大小是4个字节。那不同类型的指针变量大小是不是一致的呢?我们来探讨一下:

代码语言:javascript
复制
#define _CRT_SECURE_NO_WARNINGS 1

#include <stdio.h>
#include <string.h>

int main()
{
printf("%d\n", sizeof(char*));
printf("%d\n", sizeof(int*));
printf("%d\n", sizeof(short*));
printf("%d\n", sizeof(long*));
printf("%d\n", sizeof(long long*));
printf("%d\n", sizeof(float*));
printf("%d\n", sizeof(double*));
return 0;
}

打印结果如下:

这里我们可以看到,不同的指针类型在64位操作系统下大小都是8,在32位操作系统下的大小都是4。这里我们先把结论记住,随着后续学习的深入,我会继续分享我对这一块知识点的理解。

下面我们来对今天的内容做个总结:

1.内存空间中被划分成了一个个小的单元,这些单元叫做内存单元,它们每一个内存单元都有相对应的编号,这个编号叫做内存单元的地址;

2.我们可以通过&——取地址符号将操作对象的地址取出来并存储在指针变量中;

3.我们可以通过*——解引用操作符来将指针变量中存储的地址给取出来并正常使用;

4.在32位操作系统中,指针变量的大小是4个字节也就是32个比特位;在64位操作系统中,指针变量的大小是8个字节也就是64个比特位。

今天咱们要分享的内容到这里就结束了,希望能帮助各位更好的理解指针的知识点,后面随着学习的深入,我会继续分享我在学习过程中的感受,感谢各位的翻阅,咱们下一篇再见。