C语言发展过程中的小故事,C语言指针的小故事.docx
C语言发展过程中的小故事,C语言指针的小故事 楔子 初学者常闻到 指针乃C的精华。 那么指针终究妙在哪里 怎样去理解指针这个存在有些迫在眉睫 包括学习中都经常发现很多人以为自己真的懂指针 实际上你对指针理解多少呢 发现CSDN的MarkDown排版并不是很好 假如需要看本文的更佳版面 请戳这里 戳这里 开篇 从简单的开场 指针 你是怎样定义这个名词的存在呢 指针是一个变量 什么变量 指向一个内存地址的变量。 什么是内存地址 这是个深奥的概念 假如讲硬是要扯起来 要涉及的东西比拟多。 谭浩强的 ?C语言程序设计?中有这样一个例子(我记得是)。 我们把计算机比作一个旅馆 内存地址就是这个旅馆中的房间的门牌号 而指针那么是一把唯一的能翻开相应房门的钥匙 (这是个有Bug的比喻 请看下面的 戏讲多级指针奇怪的房间问题 解释)。 这个解释的话对于普通的指针解释似乎是行的通的。 int nNum int* pNum nNum; 那么 nNum 就是一个房间了 而 pNum 就是对应的钥匙 我们要翻开房间只需要 简单的通过钥匙就能实现 而在 C 语言中这种实现是 *pNum。 那么我们就可以尝试一下了。 int nNum int* pNum nNum; if (*pNum nNum) printf( True else print( False 房间的大小(指针的区别到底在哪 ) 不得不讲的变量类型 接下来我们开场考虑一个问题 房间的大小问题 我们知道旅馆的房间有大有小 有圆有方(应该没有圆的) 总而言之就是千奇百怪。 我们的变量也是如此 但是事实上并不是这样的哦。 就目前来讲 在 x86 环境下 笔者见过的变量类型本质上只有这么几种。 1 字节 简单的举例: char 2 字节 简单的举例: short int 4 字节 简单的举例: int,long (x86 下所有的指针都是 4 字节) 8 字节 简单的举例: double 有人或者许会讲 C语言不是还有 bool,额,有必要解释一下。 在 C99 中参加了 一个 _Bool 这个是目前在 VS2021 上可以直接使用的 C 语言 布尔型关键字。 还有一点就是 byte 并不是关键字。 指针从哪里来 通常我们是这样声明一个指针变量的 在一个根本的变量类型后面加上一个 * 例如 char* pChar; int* pInteger; float* pFloat; 通常我们是这样取出一个变量的地址的 在变量前面加上取地址符 int nNum; int* pNum nNum; 联想到的事情把钥匙变成房间 由于指针本身是一个 4字节的变量 那么我们是不是能用任意一个4字节变量来保存地址呢 也就是能不能把房间当做钥匙呢 int nNum; int npNum nNum; / 这里把一个 int 类型当做指针用所以变量命名为 npNum 我们写下如上的代码 测试之。发现编译器报出了一个警告 但是照旧可以编译通过。 警告 1 warning C4047: “初始化:“int与“int *的间接级别不同 根据警告的提示 再修改代码如下: int nNum; int npNum (int) nNum; / 这里把一个 int 类型当做指针用所以变量命名为 npNum 警告赫然消失了,我们试图输出一下. printf( 0x%X ,npNum); 我们再试一下这把新的钥匙能不能开门 printf( %d ,*npNum); 编译器给了我们一个脸色呢 错误 1 error C2100: 非法的间接寻址 好吧我们得到了一把没方法开门的“钥匙 接下来我们讨论一下这把“钥匙为什么不能开门。 你知道么 房间是没方法翻开房间的。 由于我们上面已经试验了 房间是打不开房间的 所以我们需要把房间变回钥匙再去开门。 我们来变一下 void Demo_02() int nNum; int npNum (int) nNum; printf( %dn , *(int*)npNum); 结果告诉我们 成功的翻开了一扇新世界的大门 但是因为我们房间里面还没清理 所以一篇乱七八糟。 void Demo_02() int nNum / 整理一下房间 int npNum (int) nNum; printf( %dn , *(int*)npNum); 我们可以看到整理干净之后的房间里面住了 1 先生 1 请按任意键继续 糟糕了 256先生不见了 有一天 旅馆的管理员(我们)不小心把房间变成钥匙的经过搞错了。 void Demo_02() int nNum 256; / 整理一下房间 int npNum (int) nNum; int nSir256 *(char*)npNum; / 钥匙错了 printf( %dn , nSir256); 粗心的管理员把 本来是 int 的房间的钥匙给铸成了 char* 了。 翻开门之后 我们发现 房间干干净净 256 先生好似从来没有来过一样。 我们来看一张 int 房间内部的分布图1 | 厨房 | 卫生间| 厕所| 阳台 | 隔壁房间 . | 0x00 | 0x01 | 0x00 | 0x00 | 老王. 原来如此 我们因为钥匙铸错了 所以房间只剩下了一个厨房了。256 先生正在上厕所呢。 :) 我们设想 假如有一天 我们马虎的把 一个 char 房间的钥匙给铸成了一个 int* 隔壁的老王肯定不会饶了你 给你一个大大的脸色。 新的尝试 我们试着一开场就用上一些奇怪的钥匙。 void Demo_02() int nNum 256; / 整理一下房间 char* cpNum (char*) nNum; int nSirc *ncNum; printf( %dn , nSirc); 果然 结果以及我们猜测的一样。那么指针到底是个什么呢 结论 指针(钥匙)决定了房间的大小。明天去买一个豪宅(大房间)的钥匙吧。:) 注意 假如你的房间很小却配了一把豪宅(大房间)钥匙的话 隔壁老王讲不定会来找你费事。 进阶的修罗场 讲实在的 这个东西不怎么好比喻 接下来就是修罗场了。 混乱的运算符优先级 先热热身 指出下面的指针分别代表什么2 1. int* nNum10; 2. int (*nNum)10; 3. int (*nNum)(int); 4. int (*nNum10)(int); 5. int *nNumA, *nNumB; 6. char str; 7. char* strA, *strB; :) 你能得到多少分? :( 假如有很多做不出来 记得好好理解。 指针数组 (一个元素个数为10的数组 每一个元素都是一个 int* 的指针) 一个指向数组名的指针(实际上数组名本身就是一个指针 这里实际上就是一个 int*) 一个函数指针,指向函数名 ( 成心写的 nNum 来混淆视线) 一个函数指针数组( 函数指针数组 就是讲数组的每一个元素都是 函数指针) 一个 int* 指针 以及一个 二级指针 (int*) 一个 char* 指针 一个 char* 指针 以及一个 二级指针(char*) 到这里 梳理一下指针运算的几个运算符优先级: () * 那么记不住怎么办 没方法 但是假如使用中不记得话 就记得打上括号吧 :) 我一直是这么做的。 int (*Func10)(int); int (*(Func10)(int); 先讲一下这样写的原因, 首先函数指针数组 是一个数组 其次他才是一个 什么类型的数组。 好比讲 int nNum10; int (nNum10); 固然这样臃肿难看 但是假如记不住优先级的话 请一定不要吝啬你的(). 混乱的指针长度 void Demo_04() int* pNum; int nNumArry10; int* pNumC nNumArry; printf( %d-%d-%d , sizeof(pNum), sizeof(nNumArry), sizeof(pNumC); 结果 4 40 - 4 结论: 数组名固然也是一个地址 甚至可以直接给指针赋值。 但是 数组名不等于指针 这是一个陷阱。 strlen 以及 sizeof void Demo_05() / Hello,World 经常被大牛耻笑 固然这种事情完全没有根据 char szA20 No Hello,World! char* szB No Hello,World! printf( A sizeof: %d - strlen: %dn , sizeof(szA), strlen(szA); printf( B sizeof: %d - strlen: %dn , sizeof(szB), strlen(szB); 结果: A sizeof: 20 strlen: 15 B sizeof: 4 strlen: 15 二维数组 int nNum23 1, 2, 3, 4, 5, 6; nNum 是什么? nNum 1 是什么? *nNum 1 是什么? void Demo_06() int nNum23 1, 2, 3, 4, 5, 6 ; printf( %d - %d - %d , *nNum, *(nNum 1), *(*nNum 1); 结果: 1 4 2 结论 nNum 他的类型并不是一个 int*,而是 int3 他每加 1 就会后移 3个元素 *nNum 才是一个 int* nNum 就是一个 int23 的类型了 他每加 1 就会后移 6个元素 备注 假如硬是要讲 数组即指针的话 那二维数组请当做二级指针来理解 但是请不要搞混了指针到底是什么类型的。 戏讲多级指针奇怪的房间问题 怎样这里再沿用初试的那个比喻就会出现奇怪的问题了。 int* ppNum; int nNumArry10; ppNum nNumArry; 于是乎 我们最初的比喻出现了问题了。 假如指针是钥匙 那么二级指针就是放在 钥匙里面的钥匙咯。:) 真的是一个奇怪的讲法。 为了消除掉这个 Bug 我又要开场自圆其讲了 想了很久 最后决定这样解释比拟好一点。 int* 是一个“假房间的钥匙。 假房间就是讲我明明是拿钥匙要翻开房间的 结果翻开了之后发现房间变成了一个钥匙。 (不要关心是怎么翻开了) 反正你翻开了 结果是你没有得到一个房间而是得到一把新的钥匙 int *。 假如再用这把新的钥匙去翻开房间 我们就能得到一个真正的房间 int。 二级指针就是 挂羊头卖狗肉的假房间。 :) 多级指针的意义指针的意义 我觉得我讲的这种意义可能有些以篇概全的味道 可能不是很好。 假如讲 有一天你要去一个地方旅游,海关告诉你 要去旅游可以 但是有一个有趣的游戏规那么。 假如你带了什么过去的话 其实并不会被你真的带过去 有一个叫做系统的家伙会在你要去的地方给你弄个假的东西 以及原来一模一样的。 (求求你们放过我吧 不要讲什么浅拷贝逻辑不对问题了 不要在意那些细节)。 这样就有了一个问题了 有一天我要回来了 我的房间里面的东西都带不回来了 它会把你以前的房间给你。 TAT. 为解析决这个问题 于是我想到了一个好方法,我不带房间了 我带一把钥匙过去。 这个叫系统的坏家伙就把我的钥匙给复制了一份 但是这并不影响我翻开房间。 (不要问房间怎么翻开 这是任意门的钥匙 而任意门遍布世界各地)。(我编不下去了) 于是我回来的时候 我的房间里面的都装回来了 (房间君: 我就没动过好吧 你那是任意门的钥匙( 3 ) ) 固然钥匙换了几遍 但是它不影响我翻开这个门。 接下来是二级指针 有一天 我突发奇想要把我的钥匙带到另一个地方去做个美容。 (钥匙君 我才不要什么美容)。 于是我又想到了新的方法, 把我的钥匙变成一个假房间,然后我带着这个假房间的钥匙过去 过去之后我翻开了这个假房间 于是我得到了之前的那把钥匙。 做完美容之后。我又把这个钥匙变成假房间。 于是系统这个家伙再也没方法对付我的钥匙了。 (钥匙君 我才不要变来变去) 备注: 钥匙 指针 假房间 多级指针 旅游 函数调用 新地方 函数内部 最后讲一下引用是什么 引用就是 我有个叫做 的兄弟在给系统打工 我把房间给他 他就会帮我干上面的那活。 这是一张简单的小端优先的内存图。