首页 C语言学术正文

指针随笔

欲儿 C语言学术 2022-11-18 257 0

本片文章只是浅浅的从专业的角度好好来分析一下指针,虽然一直用,但是都没有系统性的整理过知识点。


先说定义(个人理解,如有雷同,算我抄袭):计算机存放任何数据,都是要存在一个地方的,这个很好理解,比如你有一个文件放在D盘,你不需要知道是什么文件,但是如果你找到了D盘,你马上就能找到这个文件,自然而然你就知道了这个文件是什么,这个D盘就相当于是指针,指向某个内存数据。所以再说的直白点,指针就是个数据存放的地址罢了。


指针篇章

先来一个经典的代码,大家应该自己也会写,但是还是建议看一下理解一下

#include<stdio.h>


int main()
{
	int a = 10;
	int * b = &a;
	/*
	1.先说 &a  '&'的意思是取地址,所以上面代码意为,取a变量的存储地址
	2.再说 "int * b" 中的 '*' 是干嘛的,简单点可以理解为区分普通变量,因为我如果不加 '*', 那就变成了 "int b" ,这不就是又申明了一个变量而已吗
	    所以这个'*' 有点类似于标识符
	*/

	while (true);
}

利用VS的2013编译器的样子可以大致看一下,指针变量究竟是一个什么样的格式

可以看到

指针变量   b = "地址"  + "地址所存的值" 

所以指针是一个非常神奇的东西它可以存任意类型的数据,因为它想要的其实就是一个数据地址而已

image.png


那么问题来了,难道我的b就没有存放的地址吗,我可以不可以创造一个指针c,指针c所对应的数据是b的内存地址。答案很显然,肯定是可以的

int a = 10;

	int * b = &a;
	/*
	1.先说 &a  '&'的意思是取地址,所以上面代码意为,取a变量的存储地址
	2.再说 "int * b" 中的 '*' 是干嘛的,简单点可以理解为区分普通变量,因为我如果不加 '*', 那就变成了 "int b" ,这不就是又申明了一个变量而已吗
	*/
	
	int *(*c) = &b;

	/*
	1.这里说一下 这次怎么就变成了 "int * (*c) = &b" , 为什么不能写 "int *c = &b"
	2.首先,b的地址所存的是什么——是变量a(10)的地址对吧,但是c存放的是10一样的具体数值的地址吗?
	3.并不是,c存放的是一个内存地址(b)的地址——虽然b已经是一个内存地址了,但是难道内存地址就不需要地址来存储吗?
		总结一下: 
		b :一个具体数值的内存地址
		c :一个内存地址的内存地址
		所以 b  和  c 是两个截然不同的指针类型,是不能采用同一种方式来申明的,就像你不会用 "int a = 9.56"  而会用 "double a = 9.56"
	*/

同样的道理我们也在编译器里面看看,指针c长啥样,可以明显看到  C = “b的内存地址” + "b内存地址所存的东西"

image.png

好,在你认真看完上方的所有内容后,我们再来谈谈,反正就是一个地址,就是一个十六进制的数字罢了,为什么会出现int * 和 char * 等?

我们先来想一个问题,给你一个地址你就一定能知道存的东西是什么吗?就像我告诉你一个D盘,你就一定能找到你想要的?我D盘下面有一张图片,一个文本文件,我只给你一个D盘,你就真的能区分的开,是图片还是文本文件?

显然,并不是这样的。但是我如果告诉你,D盘图片,D盘文本文件呢?

C语言在定义的时候也想到了这个问题,所以在定义指针的时候,做了一件事,我不仅存地址,我还存该地址的值。

所以严格意义上来说,C语言的指针其实并不单是一个内存地址,反而应该是这样一个等式即  指针 = “某个所指地址” + “该地址所存值”

所以,我现有一个变量   "int a = 10;"  ,该变量类型为int  ,所以指针应该为, “指向一个 int 型数值的地址” + “该 int 型数值的值”

上面的图片也解释得很清楚,a就是存了一个10罢了,b = 0x00d5fabc(a所在的内存地址) + 10(a的具体值)

所以 int * b =  &a;  包含的内容相当之多

  1. 其 int 目的于指明地址的终点是一个int型的变量

  2. 其 *    目的于告诉计算机,这不是一个普通变量,是指针,即用来区分的标识符

  3. 其 &a 目的于告诉计算机,这个指针的内存地址是谁的,很明显是告诉计算机,内存地址应该存a的地址

  4. 其 a    目的于告诉计算机,指针变量的名字

所以现在应该不难理解,为什么c申明的时候就要用,int *(*c) = &b;

  1. 其 int     一样目的于指明多重内存地址之后是一个int型的变量

  2. 其 *(*c)  一样目的于告诉计算机,这不仅是一个指针变量,还是一个非b一样的指针变量(因为有两个*嘛)

  3. 其 &b 目的于告诉计算机,这个指针的内存地址是谁的,很明显是告诉计算机,内存地址应该存b的地址

  4. 其 b    目的于告诉计算机,指针变量的名字



于是又有新的问题,我可不可以再创一个变量用来存c的内存地址,和c的值。显然,可以,并且我告诉你这可以无上限的进行下去

#include<stdio.h>


int main()
{
	int a = 10;
	int * b = &a;

	int *(*c) = &b;

	int *(*(*d)) = &c;

	while (true);
}

还是一样,我们来看看d指针又长一个什么样子

image.png


那么,我到底应该加几个星号,几个括号呢,绕过来绕过去脑子都要疯掉了!哈哈哈别慌,请听我娓娓道来。

首先,当你认真看完上方的内容你肯定会发现,b c d 虽然同为指针,但是应该是三个不同类型的指针 

先说 a = 10; 就是一个普通变量,为了演说方便,我现定义

a的存储地址为 0x00aaaaaa

b的存储地址为 0x00bbbbbb

c的存储地址为  0x00cccccc

再说 b =  "地址(a的内存地址即0x00aaaaaa"        +         “值(a的值即10)”

再说 c =   "地址(b的内存地址即0x00bbbbbb)"    +           "值(b的值)"     注意:b的值是多少——0x00aaaaaa {10}

再说 d =   "地址(c的内存地址即0x00cccccc)        +           "值(c的值)"     注意:c的值是多少——"b的内存地址(0x00bbbbbb)" + "b的值({0x00aaaaaa {10}})"

所以d 应该长的样子是  d = "c的地址" + "c的值"

0x00cccccc  + {0x00bbbbbb + {0x00aaaaaa {10}} }

我们再反过来理解一下,通过b如何找到a,首先读取b的类型,读为指针,于是先去指针所存的地址,找到该地址,再读该指针所存变量类型为int ,于是再读数值为10

那我们再思考如何通过c找到b,通过b再找到a,首先读取c的类型,读为指针,于是先去指针所存地址,找到该地址,再读取该指针所存变量类型为一个指针即b,于是我们再读取b的类型,读为指针,于是上方通过b的方式再循环一次。为此我画了一个流程图,可以借鉴看看!

image.png


上面一切的一切看完以后得出来的结论是,不管是什么样的指针,怎么样的指针,归根结底都是要指向一个具体的值,上面的不管是b c d当你一步一步往下走的时候,你就会发现他们都不约而同的指向了a 的值。


我们姑且称b为一级指针,因为它第一次直接就指向了a,再称呼c为二级指针,因为它要两次寻址才能指向a,同样的,d为三级指针,因为它要通过三次寻址才能找到a,于是乎上面的代码就可以很明确了,几个* 其实是等于指针的级数,一级的话就是一个*,二级就是两个*。应该解释清楚了叭!哈哈


当然了既然指针是可以指向地址的,那在运行过程中不用调试能不能看到地址呢,答案是显然的!



#include<stdio.h>


int main()
{
	int a = 10;
	int * b = &a;


	printf("a的10进制地址:%d\n", b); //注意看这里,直接打印b的话就是b所包含的内存地址即a的地址
	printf("a的16进制地址:%x\n", b); //这里和上面只是区分了一下输出的格式
	printf("a的数值:%d", *b);        //如果你要输出数值,那就是取地址的值,这里注意下,在申明指针的时候*是标识符,在读取指针所包含的值的时候*就变成了取该地址的值的意思
	while (true);
}

image.png


同样的相同的指针类型,也可以互相赋值,比如说这样子

int a = 10;
	int * b = &a;
	int * c = b;

	printf("a的10进制地址:%d\n", b);
	printf("a的10进制地址:%d\n", c);
	printf("a的数值:%d", *b);

可以看到的是,c和b其实 级数相同,并且所存数值类型同为int ,说白了b 和c 除了名字不一样完全等价,所以b可以直接赋值给c

版权声明

本文仅代表作者观点,不代表本站立场。
本文系作者授权发表,未经许可,不得转载。

评论