带你学C语言~指针(1)

        Hello,CSDN的各位家人们,你们好啊!今天,小赵要给大家分享的C语言知识是指针,相信不少家人们都或多或少被指针搞得晕头转向,小赵一开始也是,但后来小赵经过不断地努力学习,终于将这里面的知识弄懂了。由于指针的知识点实在太多,小赵决定将它分为几个小部分,让大家更好地学习和吸收。

目录

🎾内存与地址

⚾内存

⚾地址

🎾指针变量与地址

⚾取地址

⚾指针变量

⚾如何认识指针类型

⚾解指针

⚾指针占多少内存

🥉指针变量类型的意义

🎯不同指针类型的影响

🎯void型指针

🎯const修饰指针

🥈指针运算

🏉指针与整数运算(加减)

🏉指针与指针运算

🏉指针的大小比较

🥇野指针

🏓野指针是什么

🏓如何避免野指针

🏐assert断言

🏆指针的使用和地址的调用

⚽模拟strlen函数

⚽传值与传址

🏆结束语


     

🎾内存与地址

⚾内存

在开始指针之前,小赵先和你们聊聊内存,内存是什么,其实我们大多数人心中应该是有自己的一个定义的,因为我们的手机啊,电脑啊,存储一些软件,音乐什么的靠的其实就是我们的内存,可内存究竟是怎么调用的,我们好像并不太清楚也并不太了解。但我们的生活中有一个有意思的例子可以让我们摸清楚就是,我们的房子也是用来存东西的,存我们的书籍,衣服等等,那它是怎么调用和找到的,其实靠的点就两个一个门牌,一个钥匙。

正是这两个东西的存在,让我们所有人都可以井然有序的生活,不会乱放自己的东西在别人家,不会走错门,也让自己的东西不会被别人偷。那我们的内存是不是也是一样呢。其实电脑和内存的关系就是下面这张图展现的。

我们的电脑其实就是通过给每个内存编号,来进行存储访问的,而这个编号其实就是我们下面要讲的地址了。 

⚾地址

如此庞大的内存,究竟是靠着怎么编写的地址来进行有序的访问,相信这是不少小伙伴们的疑惑,其实要解决这个问题还是要借助我们的内存。我们都知道,我们的内存存储是分很多的单位。正如下面这张图。

 在这些茫茫的内存中我们的地址选择了字节,内存把自己分成了无数个细小的字节单位,给每一个字节编写编号,而这个编号就是我们的地址。

🎾指针变量与地址

⚾取地址

我们说既然每个东西它都有自己的内存也都有自己的地址,那我们如何去取出我们的地址呢?又该如何去看到我们的地址。那么小赵就在这里为大家演示一下,首先呢,在我们的C语言中是有一个专门的操作符用来取这个地址的,这操作符就叫取地址操作符,即&。那么如何用呢?

利用我们VS的调试的功能

 在里面输入我们的地址,然后再按一下回车,就可以出来我们的地址了。

 

⚾指针变量

那么既然已经如此完美了,那为什么还要我们指针的存在呢?就像你要去往任何一个房屋总要直到它的地址,而我们的指针干的就是存地址的活,同时它还可以帮助你去打开地址,往那块内存里面去存。这一点就相当于什么呢,就好比这个房子的主人把自己房子的门牌号告诉给了那个人,同时还给了它钥匙,让他能够去拥有这个房子的使用权,但这个房子其实还是归属于这个房主的,在现实中其实也有对应就是租房子给别人的那种感觉。

⚾如何认识指针类型

讲到现在也没把指针长啥样,拉出来给大家看看,那么现在我们就当当当请出我们的指针,来看看这究竟长啥样,能如此强大。

其实看完了这个图相必大家也能推出其他类型的指针,比如字符指针,short类型的指针,long.....等一系列指针,但其实指针也有一些需要注意的或者说难写的,这个我们后面再聊。

⚾解指针

那好了,现在指针我们拿到了地址了,可你不能光给我地址啊,我都租了,你好歹让我在你这个房子里倒腾一下,这个我们C语言也早就准备好了,我们C语言的解指针用的就是*,对没错这个是不是超级像你那个钥匙插进去的那个门缝,所以这个可好记了,下面小赵就要给大家演示一下我们该怎么用这个指针去倒腾这个房子了。

怎么样是不是很神奇,其实这里小赵还有一个比较有意思的说法,a=10,其实就相当于我们家本来的样子,*p=150,就相当于我们打开家,把家倒腾成的样子,然后我们再输出家的样子。怎么样还是有意思的吧。

⚾指针占多少内存

那么我们的指针究竟占多少的内存呢?

这个其实这要分情况的,首先就是我们x64(即64位)的版本下面,我们可以打出来看看。

接着就是我们的x86(即32位)

🥉指针变量类型的意义

相信这个时候就有人问了,指针变量所占的内存的大小是一样的吗?那当然是一样的。那为什么还要划分为各种指针类型呢?既然大家内存都一样,直接用就好了,分这么多类干嘛?那么接下来小赵就来帮助你去解决这个问题。

🎯不同指针类型的影响

其实我们都知道,不同的类型所占的内存大小是不一样的,而到了指针这里,就完全可以看作是它所控制的内存大小,比如你租房子只租给一个人卧室,那么对这个人来说他所能改变的其实就只能是这个卧室以内的范围,外面的厨房其实他没办法去改变。那么对指针来讲也是一样的。比如你一个指向char类型的指针去指向人家int的类型的指针那你所能改变的也只有人家的四分之一部分咯。

在上面的这些案例中,我们不难发现不同类型的指针变量所拥有的权利是不一样的,它们的访问权完全由自身所指向的类型的所占的内存大小决定。

🎯void型指针

但一个队伍总会有这么一个特殊的存在,也可以说是几乎全能的存在,没错这个在指针中可以访问一切类型的指针类型是存在的就是我们的void型指针,这个指针类型可以接收所有的指针类型,但全能未必是好事,它的遗憾就是它无法打开门读取(即无法解指针),也无法对内存做出任何修改。如果要进入其中修改,就必须强制类型转化成其他类型,这个我们后边会多次使用这个指针去接收一些其他指针,在这里小赵就不多讲了,就给大家举个简单的例子吧。

🎯const修饰指针

首先我们先再一次认识一下我们const.

const 限定符,它把一个对象转换成一个常量。

既然const是把我们修饰的变量转变为常量,那你说这个变量还能变吗?当然是不能啦。比如我们的常量1,还能有一天忽然变了不成,当然不可能变啦。下面可以看一下小赵举得一个例子更加深刻地体会一下。 

那放到我们的指针上又是怎么样呢?

先看第一种

那么这里为什么会报错呢,其实这里就相当于是修饰我们指针变量所指的地址的那部分内存,就是我把你这部分内存给定下来了,既然定下来了,那你说这个内存我还能变吗?

但是我们p这个租房子的是可以走的,你定了房子的样子,但不代表我这个租房子的人不能走啊。

我们的p只需要重新去租个房子就好了。

下面看第二种

其实这里也相当于我们的第一种,当然小赵在这里有个比较有意思的说法啊就是把这个*p(人和钥匙绑一起就是那个房间)当成我们租的那块地盘,你把我租的地盘定下了,那你说这个地盘还能动吗?而单独的一个P则相当于一个自由自在的人,那它不就可以改变了吗?

接下来看第三种。

第三种其实你再用小赵上面的理解就好理解了,就是我把你这个租客定下来,也就是这个指针变量所指的地址定下来,那你说他还自由吗,他不久不能换地址了,但这时候地址那块空间没定下来,那不就可以随便改吗?

总结:

结论:const修饰指针变量的时候
• const如果放在*的左边,修饰的是指针指向的内容,保证指针指向的内容不能通过指针来改变。
但是指针变量本身的内容可变。
• const如果放在*的右边,修饰的是指针变量本身,保证了指针变量的内容不能修改,但是指针指 向的内容,可以通过指针改变。

🥈指针运算

那么接下来就有人要问了,指针可以运算吗,运算的意义又是什么呢?

下面小赵来为你解答。

🏉指针与整数运算(加减)

指针于整数的与运算,上面我们提到了,我们地址在内存中是以一个一个的单元存储的,每个单元是1个字节,那我们的指针与整数的加减运算,其实就是跳过了多少个这样的单元,而跳过多少个单元的问题,那究竟跳过多少个呢?其实就是(整数)*(指向的类型所占的字节大小)

这里小赵为了方便大家理解,用我们的数组做个示范。因为数组里面存储的数据是连续的。

🏉指针与指针运算

那么指针与指针的运算,这个就比较有意思了,它的结果其实就是这两个地址之间的元素多少,当然这个其实我们一般也是数组用的多,因为其他的数据都不一定是连续的,只有数组我们目前确定是连续的。

🏉指针的大小比较

那么两个指针的大小比较呢,其实这个就跟我们的地址高低有关了,其实也是后面小赵要与各位聊的,但可以透露的一点是数组下面的下标越高,它的地址一般越高,毕竟也是后创建的。

当然还有一点要提的是,我们的数组必须是在一个数组中,两个数组是不连续的。 

🥇野指针

🏓野指针是什么

野指针往往其实指的就是一块没有被开发的内存,就是你这个指针啊,没有指向任何地址,这个地址就是随机的,同时也是不允许被使用的。就像你随便找了一个房子没被人授权,这能允许吗?

野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)指针变量在定义时如果未初始化,其值是随机的,指针变量的值是别的变量的地址,意味着指针指向了一个地址是不确定的变量,此时去解引用就是去访问了一个不确定的地址,所以结果是不可知的。 

像我们在敲代码时候常遇到的情况有 三种

第一种,是由于我们没给它地址。

这时候我们的编译器是会报错的;

第二种越界访问。

这里我们跳过了a这块区域,然后就是未知区域,所以出来的也是随机值。 

第三种使用的空间被回收或释放

这里出现的情况就是我们的函数在运行结束后将自己开辟的那块空间收回了,所以再找也就是一块野指针了。(这里函数栈帧会讲)

🏓如何避免野指针

那么如何避免这个野指针呢?

方案一

我们把我们的指针上来就指向一个明确的地址。

方案二

给它一个空指针,让它什么都没有无法使用。

它既然是一块空区域,那你也就没办法塞东西给里面了。

🏐assert断言

assert是用来干嘛呢?我们可以先用小赵上次说的网站查查。

我们注意到assert是看你括号里面的东西是否是真的,如果错了它就会终止程序的运行。

那么这个就刚好可以判断我们的指针是否是空指针,防止我们的错误使用。

🏆指针的使用和地址的调用

好了,我们了解上面的一些关于指针的相关知识下面小赵带你去实际应用我们的指针吧。

⚽模拟strlen函数

 其实这个函数也是小赵多次提到的一个函数,它的主要作用,就是去算一个字符串在\0之前的字母数,同时还要注意的一点是它的是要头文件的即#include<string.h>

 那我们究竟该如何实现呢?在这里小赵就不卖关子了。

int my_strlen(char* a)
{
	int count = 0;
	while (*a != '\0')//*a不等于'\0',继续运行
	{
		a++;//看下一个字母
		count++;//统计字母数量
	}
	return count;//返回字母数量
}
int main()
{
	char a[10] = "abcdef";
	int m = my_strlen(a);
	printf("%d", m);

}

 

⚽传值与传址

其实之前学完函数后就有不少人有这样的困惑了,就是要给两个变量在函数里面换值的无法成功,这其实是因为函数里面创建的变量都是临时变量,当函数运行完了,这些代码的空间什么的都会回收,我们的值就像我们输入进去,给它们里面的两个在里面换,但人间不会影响到你外面的人

但如果这里你用我们的地址就不一样了,因为我们如果用地址,那么它就可以开打开我们这边的房间对我们进行换值了。

void Swap(int* px, int* py)//传入的地址给你
{
	int tmp = 0;
	tmp = *px;//这部分和之前的交换一样
	*px = *py;
	*py = tmp;
}
int main()
{
	int a = 0;
	int b = 0;
	scanf("%d %d", &a, &b);
	printf("交换前:a=%d b=%d\n", a, b);
	Swap(&a, &b);
	printf("交换后:a=%d b=%d\n", a, b);
	return 0;
}

🏆结束语

好了小赵今天的分享就到这里了,如果大家有什么不明白的地方可以在小赵的下方留言哦,同时如果小赵有什么地方说得不对也希望得到大家的指点,谢谢各位家人们的支持。你们的支持是小赵创作的动力,加油。

如果觉得文章对你有帮助的话,还请点赞,关注,收藏支持小赵,如有不足还请指点,小赵及时改正,感谢大家支持!!!

相关推荐

  1. C语言完全理解指针(三)函数指针数组

    2023-12-16 05:14:05       13 阅读

最近更新

  1. 写一个字符设备的驱动步骤

    2023-12-16 05:14:05       0 阅读
  2. Transformer和Bert的原理是什么

    2023-12-16 05:14:05       0 阅读
  3. 使用tkinter 制作工作流ui

    2023-12-16 05:14:05       1 阅读
  4. postman工具介绍

    2023-12-16 05:14:05       1 阅读
  5. vue-路由自动化

    2023-12-16 05:14:05       1 阅读
  6. el-date-picker 扩展

    2023-12-16 05:14:05       1 阅读
  7. Go语言入门之变量、常量、指针以及数据类型

    2023-12-16 05:14:05       1 阅读
  8. Kotlin 处理livedata数据倒灌

    2023-12-16 05:14:05       1 阅读

热门阅读

  1. facebook广告怎么降低预算优化效果

    2023-12-16 05:14:05       41 阅读
  2. C# TcpIpSocketSever

    2023-12-16 05:14:05       30 阅读
  3. uniapp腾讯地图路线规划

    2023-12-16 05:14:05       41 阅读
  4. 什么是Aop?

    2023-12-16 05:14:05       37 阅读
  5. 前端开发环境配置

    2023-12-16 05:14:05       45 阅读