2

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

 2 years ago
source link: https://blog.csdn.net/qq_17593855/article/details/121998083
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

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

专栏收录该内容
3 篇文章 1 订阅

在这里插入图片描述

🧑🏻作者简介:一个走在前行路上的人
✨联系方式: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 %d\n", &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
你的支持是我前进最大的动力。


Recommend

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK