理解C语言指针

C语言指针理解

本人在初学的时候认为c语言中指针很好理解,但身边好多同学一直在说老师讲的指针太抽象了,看不到,摸不着,非常难理解,甚至学了4年计算机,毕业了,不少同学还说不清楚指针是什么,遇到指针的问题必定出错,这里简单介绍一下。

引言

c语言中有很多抽象的东西,而指针就是其中一个,学好 c 语言就要学会计算机的思维:透过表面看本质。
比如下面一行代码中 两个 ! 运算符

int x = 0;
int a = !!0; //看这里

表面上:第2行的两个 ! 运算符抵消了,相当于没有。但这只是结论。
本质上:对0进行了两次 ! 运算。这才是本质。

很多同学整理错题的时候也是只整理表面上的结论,所以导致同一类型的题目一再二,二再三 出错。


正题

为什么说指针好理解?首先要有抽象或者说本质的概念。

感觉这个世界里非常多相似的东西,上了十几年的学,就算学新知识,也能在已有的知识体系上找到很多的相似的东西,学习指针时,可以拿已有的概念做参照来学。

下面我先给它一个定义,让你能想象的出它大概是什么,干嘛的。

指针像商品的条形码,像汽车的车牌,像寝室的门号,又像人的身份证号…等等。你可以把他理解为一个标志,它并不代表实际内容,只有一个值,但我们可以根据它来找到对应的真实存在的对象。

同样的,指针并不保存所指对象的完整信息,但是我们可以根据一个映射(map)关系,来根据指针中的值,来找到对应真正的对象。看以下丑图:
比如我有个程序:

int main(){
	fun1();
	...
}
void fun1(){
	fun2();
	...
}
void fun2(){
	...
}

那他在内存中的某一时刻(正在执行fun2函数)可能是这样的:
在这里插入图片描述
而你定义的变量或者指针,在内存中的的栈中,具体到某个栈帧里。
在这里插入图片描述
可以看到栈内存的分配非常像一只只集装箱,集装箱里又装了一个个快递…
在这里插入图片描述
其中栈和堆只是个名字,认为划分的两个区域,为了方便程序内存管理。

  • 栈中的内存不需要手动回收
    比如 int a = 1;就不用手动 free(a) 操作
    因为他会随着所在函数的结束,整个函数占用的栈内存会自动释放掉,实际上是编写 c 语言的人帮你释放掉了。

  • 堆内存需要手动回收,比如 xxx* p = malloc(…); 相当于在堆内存中开辟了一块空间,在栈内存中新建了一个变量,把堆内存的地址的值赋值给栈内存中的变量(指针),在使用完毕后需要手动释放掉堆内存的地址,因为不过不主动释放,虽然栈中指针所占内存被自动回收了,但堆内存并没有被标记为释放,而又无法通过栈中的值找到这块堆内存,即发生内存泄漏,这块堆内存在该程序结束前将一直无法使用。

  • 指针和实际指向的对象是两个东西。

废话解释:

像商品的条形码     纯净的商品是不带条形码的,可以根据条形码找到对应的商品
像汽车的车牌		  汽车生产出来也是不带车牌的,但车牌可以方便我们识别一辆车
像寝室的门号        寝室本不需要门号就能使用,门号只是方便我们找到他
像人的身份证号	 人在一诞生是没有身份证号的,是出生后才有

回归正题
比如我有以下代码

int a = 0; 					// 定义一个int类型变量a
int* point_a;				// 定义一个指向 int类型变量 的指针
point_a = &a;				//把 变量a 的内存地址 赋值给 point_a

//以下为比喻-------------

Car aodi = ....;			// 有一辆奥迪车,它的颜色是...长...宽.........
Car* carNum;				// 生产一个车牌
carNum = &aodi;				// 把这个车牌挂到车上

其中,无需纠结比喻部分合不合理,主要看 c 代码即可。

看一下内存中是如何分配的
在这里插入图片描述
可以看到指针的值其实是一个内存地址值,而不是具体值。具体的值保存在变量 a 中。
因此,当你进行以下地址计算时:

printf("%d", &a);				// a 变量的地址, 					0x8004

printf("%d", point_a);			// 变量 point_a 的值 				0x8004

printf("%d", a);				// 变量 a 的值						0

printf("%d", *point_a);			// 变量 point_a 值被按照int解析后的值 0

c 语言中 * &的意思:

首先:内存中的状态只能表示 0 或 1,因此获取内存中变量的值的真正含义,需要根据类型来解析

变量前符号 简介 详情
什么都不加 变量的值 根据变量的类型,解析变量所在内存地址中的值
& 变量地址 获得变量的内存地址
* 寻址并解析 寻找地址等于变量值的内存,根据变量类型解析这块内存中的值
以上是指针知识的大概介绍(是什么)

大概对指针有点概念后,还需要知道他存在的意义(为什么存在)和用法(怎么用

存在意义

主要是为了减轻程序员的负担。

虽然 c 还是不 java,python,js 等写起来爽,几乎不需要关心内存了,但他相比于汇编语言,机器代码,有这种思想,在当时已经非常先进和高级了。

因为如果让程序员直接管理 地址,解析地址中的 0 或 1,那不得累死?所以出现了指针这个东西,极大地方便了程序员来管理内存,在写代码时无需关心底层内存是如何分配的,可以更好的专注于功能的设计。
题外话
当然在现在,c 语言毕竟使用时还需要手动释放堆内存,允许直接访问内存地址,操作不当则很容易使得系统或者其他程序出错(如修改某游戏所占内存中的值,简称外挂),内存自动分配和回收的语言更加受到开发者的喜爱。


如何使用

以上是本质,如果只看现象不看本质,在做题时候稍微有点坑,便反应不过来。

比如一般写代码时候 总是让某一类型的指针指向特定类型的变量,如下

//易读写法
int i = 0;
int* point_i = &i;

char c = ' ';
char* point_c = &c;

//实际中经常会简写为
int i = 0, *pi = &i;
char c = ' ', pc = &c;

其实以上都是非常规范的写法,是指针的最简单的使用,大部分出现在入门阶段。

深入分析

我们通过现象看本质知道。
析地址符 * 可以按照后面变量的类型来进行解析内存等于其后变量值的内容
假设可以以下操作

char c = 'a';

char* point_c = &c;
short* point_s = &c;
int*  point_i = &c;

那我们就知道这三个指针变量 的值相同,类型不同,因类型不同,解析出来的表现也不同。
‘a’ 在内存中存的内容实际是其 ascll 码值,即 61 ,char 类型占用的内存为 1 字节即 8 位,则在内存中形式为 000111101,
那么 *point_c 的流程:

  1. 根据 point_c 的值获得内存地址
  2. 根据 1 得到的内存地址找到对应的内存
  3. 获取找到内存的类型
  4. 检查是否为自身或为(point_c ) 类型的子类,如果不是继续判断能否进行强制类型转换
  5. 如果是,根据所得的地址,按照自身 (point_c) 类型,获取该区域内存的值(char类型,读取1个字节),000111101
  6. 按照 自身 (point_c) 类型解析(转换为 ascll 码所代表的 字符) ,转换成 ‘a’
*point_s 的过程与上类似,我们这里做个假设第 4 步通过了,会发生什么?

直接来到第 5. 步:
5. 按照自身类型(point_s),从该区域读,连续读 2 个字节 000111101 xxxxxxxx
6. 按照 short 类型解析,大部分计算机内存中内存存储会按照高位在前,低位在后存储,因此:
- 其读到的数据用二进制表示为 xxxxxxxx 000111101
- 把它认为成一个 short 类型数据读取

其他情况本质上都是这样的

提示

初学者最重要的就是要理解 指针它所指变量个变量。


本篇仅面向初学者,只介绍了指针的读取,有不理解的地方欢迎留言。

猜你喜欢

转载自blog.csdn.net/qq_35425070/article/details/88790394