室友收了个房租,搞懂了C语言的指针

在这里插入图片描述

??作者简介:一个走在前行路上的人
✨联系方式:2201891280(QQ)
全文大约阅读时间: 80min

☘前言☘

  • 变量的指针、指针变量
  • 指针数组、数组指针
  • 指针函数、函数指针

每个被C语言劝退的伙伴们是否看到这些概念脑壳疼,那种支配感觉扑面而来0.0
这次我就用一个小故事帮大家打通一下指针相关的任督二脉0.0


?1.1 指针究竟是什么

先放上一张神图。大名鼎鼎的冯·诺伊曼体系结构。
在这里插入图片描述
可以发现运算器能直接读取的程序是在存储器内的。那么运算器怎么拿到这个数据呢?
首先我们先明确,因为变量的读取都是从内存中的,所以一定会有一片空间在内存中。我们把它想象成有一栋公寓有一堆房间,我们想要找到我们的房间是不是得有个房间号?请添加图片描述
然后有没有发现102 103 104 105这四个房间我没画隔断,因为很多地方是按照门来编号的,但是一个大房间可能对应多个门呀。

  • 上面的门的编号方式就是编制方式(可以一个门一个号,也可以两个门一个号),地址就每个门的房号,然后数据其实就是门内的内容。
  • 我们根据房号找到家,计算机就是根据地址找到数据

指针其实就是地址。也就是上面的房号。
我们假如忘了门号就看看外面门上的标识牌对吧?计算机内如果变量想要直到自己的地址也是需要看看自己的房号,怎么去看呢?用&,因为是变量自己看所以就是&x就拿到地址啦0.0
举个例子:

#include<cstdio>
int main(){
	int a = 0;
	printf("%d %dn", &a,a);
	return 0;
}

输出结果可能是:2686748 1
其中地址是一个unsigned类型的整数(64位是unsigned longlong),至于为啥是无符号的,你见过谁家房号是负数么0.0

?1.2指针变量

指针变量用来存放地址,然后可以看下图,就是指针变量,比较厉害,手里有把钥匙,并且这个钥匙可以开对应的门。
请添加图片描述
然后由于这是一把钥匙,所以在声明它的时候需要在它前面加个*,表示它是一把钥匙。也就是int *p;,一般来说都是把*放在变量前面。
同时*也表示拿钥匙开门,*p就表示拿到对应的数据,所以图上的就是104号房间对应的元素。
刚才知道&是取地址,就是看看房间号,那么p = &a就可以把p这把钥匙变成a所对应的地址。此时再去*p就是a的值。
看下面的程序:

#include<cstdio>
int main(){
	int a;
	int *p = &a;
	*p = 233;
	printf("%d %d", *p, a);
	return 0;
}

输出结果是233 233,因为开房间的优先级很高,在赋值的时候是先打开门,再把数据写入,所以就把值写入到了a之中。最后输出就是两次a的值。


最后说明一下,不同的指针是不同的,比如下面的两把钥匙是不一样的。因为102-105是连着的,所以第一把钥匙钥匙+1实际地址会加4,而右边的指针对应的只有一个门,+1的时候只会加1,
其实对应的就是int *char *类型的指针,其中一个长度为4,另一个为1。
这个指针的类型叫做基类型
请添加图片描述

?1.3 数组的指针

数组就是一片连续的空间,数组名称也作为数组的首地址使用。
请添加图片描述
根据上面提到的指针加1等于加对应的数据长度,所以a+i&a[i]是完全相同的。
在枚举元素的时候可以这么写:

#include<cstdio>
int main(){
	int a[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
	for(int *p = a; p < a +10;p+)
		printf("%d ",*p);
	return 0;
}

指针的减法

#include<cstdio>
int main(){
	int a[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
	int *p = a;
	int *q = &a[5];
	printf("p = %d ", p);
	printf("q = %d ", q);
	printf("q - p = %d",q - p);
}

输出结果是:p = 2686688 q = 2686708 q - p = 5
会不会感觉震惊?这个q-p竟然不是20???,其实指针的减法是计算两个指针直接差多少个基类型,因为是int所以返回的就是相差多少个int。


?1.4 指针数组

了解完了数组额的指针,那么指针数组是什么呢?就是有一片区域保存着钥匙的区域就是,就是指针数组。同时指针数组的名称也做为这片区域的首地址使用。
在这里插入图片描述
举个栗子?
我声明了一个指针数组int *pnums[4]分别对应上图的四把钥匙,那么pnums的值就是201,假如我想访问102的值就是*(pnums[0])就能够访问啦。
注意:int *pnums[10]int (*punums)[10]是完全不同的。后者代表的含义是这是一个指向长度为10的数组的一个指针。

?1.5 高级指针

人类的本质就是套娃。套娃可以生出万千花花世界0.0
在这里插入图片描述
其实高级指针就是告诉程序这是一个多少级的间接索引。比如二级指针就是需要进行两次查找值才是最终的数据。三级指针就是需要进行三次取值才是最终的数据。
在这里插入图片描述
就如上图,那么为啥一定要区分地址和最终的值呢?因为地址的长度和数据长度一般是不一样长的,比如64位机的寻址空间就是64位,而一个int是32位,为了防止出错就会进行区分。
其实高级指针就是多级索引,只有一层一层剥开才能拿到最终的数据啦。

?1.6 指针函数

所谓的指针函数,其实就是返回类型是指针的函数。
比如我们需要返回一组数据就需要返回数组的首地址。

int *nums(int k,int * returnnums){
	...
}

上面的一个函数就返回了一个int类型的指针。一般我们写程序为了知道返回指针的长度会有一个参数returnnums传进去来获得相应的数组长度。


?1.7 函数指针

其实函数指针也像上面说的那样,是函数保存的地址。之前的都是数据的地址,但是函数本质上也是内存内的一片区域,所以本质上来说对cpu来说没有太大的区别,所以当然可以有地址啦。
举个栗子

int cmp(cont void *a, const void *b){
	return *((int *)a) > *((int *)b) ? 1 : -1;
}
int main(){
	...
	qsort(nums,numssize,sizeof(int),cmp);
	...
}

上面的cmp当做qsort传参的时候就是对应的函数的指针,同样的,函数指针也有不同的基类。qsort要求的基类就是int (*fn) (const void *,const void *),就是一个返回类型为int且传入参数为两个const void*的函数。
不同的基类可能会造成解析错误(类比int 和char 长度不同拿到的数据会出错)。

?1.8 最终总结

其实我们可以发现:

  • 指针放在后面就表示某个东西的地址
  • 指针放在前面就表示本身的属性是指针

有没有觉得好记一些呢?


?写在最后

今天就写到这里了,如果大家觉得对你有帮助的话还希望大家动动手指给个三连0.0
你的支持是我前进最大的动力。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>