在全国各个计算机考研学校中,不少自命题学校会考察“程序设计”这门课程,但是市场上的计算机考研资料却少有涉及C语言和程序设计的。为了弥补这个缺陷,大牛学长将会在接下来的一段时间中不断为大家带来程序设计中的重难点解析。
今天我们先来看一个C语言编程中同学们很熟悉但又不那么清晰的知识点—— 指针。如果同学们首次接触C,遭遇的第一个难题应该就是对于指针的理解,我相信读完这篇文章你就能够完全掌握指针这一概念,以及如何在C中去灵活的运用指针。
那么废话不多说
here we go~
![529524d8c9387b2a9bf52ce295c37abb.gif](https://i-blog.csdnimg.cn/blog_migrate/e474c24e09b345fb860d172f5e8303cc.gif)
数据在计算机当中的存储形式
如果同学们学过了组成原理的知识,应该能够轻松的理解这样的一个概念:我们的计算机当中有这样的一个空间,这个空间被划分为同样大小的单元,我们把每个单元称为 存储单元,如果我们给单元编号,假设有N个单元,它是一个类似顺序表的结构,我们可以编号0~N-1,如图所示,我们就把这个编号称为这个单元的 地址。如图所示。
我们可以对这个存储空间做一系列操作。
例如【向地址2的存储单元中存放a】,可以记作[2]=a,
而【向地址0的存储单元中存放a】记作[0]=b。
而【从地址2的存储单元中取出数值放到x当中】,记作x=[2],此时x=a,同样的,【从地址0的存储单元中取出数值放到y当中】,记作y=[0],此时y=b。
显然对于每一个存储单元,具有两个属性(地址,值)。
C的变量在计算机当中的表示形式
我们知道形如int a = 10,这样的语句表明我们声明了一个int类型的变量,命名为a,赋值10,如果我们打印a,调用printf(a),会打印输出10。那么在实际的计算机当中是如何完成这个过程的呢?
(1)程序维护一张表table记录了我们声明的变量名和系统分配的地址(当然这是示意图,实际情况下会记录更多信息),如图,对于变量a,分配的地址是2,当我们对a进行赋值为10的时候,事实上是执行了[table[a]] = 10的操作,将10写入地址2的存储单元。
(2)当我们获取变量a的值,例如printf(a)的时候,事实上是打印出了[table[a]]的值,也就是获取了地址2的存储单元保存的值。注意,这里其实是对两个表一次做了取值操作。从表2的a键值中找到了地址2,从表一的第二个地址中找到了10。
从(1)和(2)中我们可以看到一个基本结构:table保存(变量名,地址),而地址对应的存储单元保存变量值。
看到这里,大家就已经拥有了学习指针,理解指针的所有知识了。
![d6a2040f4b03b37392d0acdc18a564ed.gif](https://i-blog.csdnimg.cn/blog_migrate/29636da6087eb840e755748004f6e2a4.gif)
![7f65d0a46fd2f371af13129e4e29e8a8.png](https://i-blog.csdnimg.cn/blog_migrate/9c2a4f605f823b072d73bc0792cf5324.png)
![a646cffdd5c1fb3b9a069a970dd82130.gif](https://i-blog.csdnimg.cn/blog_migrate/506a8854c6cb74c9b51822c10599b59e.gif)
![59fe0e44080cbcf8affbcd47afa32531.gif](https://i-blog.csdnimg.cn/blog_migrate/c16c6c64e0a98620d539da02d2aa9714.gif)
![d6a2040f4b03b37392d0acdc18a564ed.gif](https://i-blog.csdnimg.cn/blog_migrate/29636da6087eb840e755748004f6e2a4.gif)
![7f65d0a46fd2f371af13129e4e29e8a8.png](https://i-blog.csdnimg.cn/blog_migrate/9c2a4f605f823b072d73bc0792cf5324.png)
我们看到变量名为b的这个例子,b对应的地址是0,[0]保存的值是2,[2]保存的值是10,我们同样可以通过变量b获取到我们赋值的10,那这个赋值是如何完成的呢?我们发现在b所对应的地址0当中保存的2事实上是变量值10所在的地址。当我们变量来保存地址而不是值的时候,我们就把这个变量称为指针。
所以指针的本质就是地址。
指针我们使用一个特殊的运算符ElemType*,在数据类型后面跟上一个 *,读作xx类型的指针。
而这里的图,对应的是int *b = 10,系统申请一个地址为i的存储单元存放10,然后把这个i存放进地址为j的存储单元,最后在table中保存的记录是(b, j)。
对于图中的b,执行下面两句代码:
printf(b);
输出:2
printf(*b);
输出:10
一定要注意!
* 的作用是表明存储单元保存的是操作数的地址。
在这个例子中*b和a是等价的(当然在实际的计算机中不会把两个变量分配到同一个单元中,这里是为了方便理解)。*b共同作为一个整体代表的是地址为2的这个存储单元。如果我们执行 *b = 20,则会向地址为2的存储单元中写入20。
前面提到了我们通过形如printf(b)这样的语句获取变量时,是根据table读所在地址的存储单元的内容,那如果我们希望获取当前变量名的地址,比如对于(b, 0)这一条记录我们希望能够获取到0这个地址,我们如何实现呢?
这时候,我们引进 & 这个操作符。(&操作符也是同学们经常不能完全理解的操作符)
还是对于上图中的b,执行这句代码:
printf(&b);
printf(&a);
输出:0和2
此时 & 完成的是取地址的操作。
由此我们可以知道如果 & 只有取地址这唯一一种含义是不能够作为语句的左值存在的(因为是取值不能进行赋值),也就是不应该存在int &x = y这样的语句。
但是,事实上有些同学可能看到过这样的语句的存在。因为这是C++引入的新特性(C通过某些设置也可以使用C++语句),此时&对应的不是取地址,而是表示引用。引用类似于给人取别名,对于张三来说,他可以有个别名叫做老三,两个名字不同但是指向的都是同一个人。
上图是执行以下代码之后的结果:
int a = 10;
int b = a;
int &c = a;
我们可以观察到b和c的区别,b虽然也是赋值为10,但是b和a指向了不同的存储单元,只是保存的值的内容相同。但是c是直接指向a的存储单元。引用表示的是使得这个变量和另一个变量共享存储单元。
进一步:
a = 20;
此时存储单元情况是:地址2保存的值为20,地址1保存的值为10。变量a=20,b=10,c=20
如何区别是取地址还是引用?
其实直接看是不是形如(数据类型) & 变量名就可以了。
int &a = b;//引用
printf(&b);//取地址
int c = &a;//取地址
特别注意,当函数的形参是引用类型的时候,实参不需要加上&符号,例如:
void swap(int &a, int &b){
int c = a;
a = b;
b = c;
}//交换两个数
int main(){
int a = 10;
int b = 20;
swap(a, b);
return 0;
}
执行之后,a = 20, b = 10
最后复习一下前面学到的*和&的用法。观察下面的代码:
int a = 10;
int *b = &a;
int c = *&a;
int &d = c;
执行后如图所示,a = 10, *b = 10, c = 10,d = 10,b = 3
今天是2020年8月16日
距离2021年考研还有125天
生活本沉闷
跑起来才有风
![6ab13a2efa2155d2c40137e3a6373793.gif](https://i-blog.csdnimg.cn/blog_migrate/b244a616088f846019a97cc91ae9295a.gif)
![45637beff11b065dc882c8427b9766d2.png](https://i-blog.csdnimg.cn/blog_migrate/287a500948a8dac6682dd9dc61b3feb6.jpeg)