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

目录

🏉前言

🚀 数组名的理解

🚀使用指针访问数组

✈一维数组传参的本质

✈冒泡排序

🏆二级指针

🏆指针数组

🏆指针数组模拟二维数组

🎉结束语


🏉前言

上一章,小赵和各位聊了关于C语言指针的开篇章,我们聊了指针最初的模样,但指针这个东西远远不止上一章那样简单,因为我们开辟的内存远远不止这么简单,毕竟还有数组的开辟,还有函数等等这些,包括我们的指针它也是有内存的,那我们究竟该如何去访问它们的地址,如何使用它们的地址内存,这样的地址内存又究竟对我们编写代码会带来什么样革命性的改变,下面小赵讲讲一一为你解答。

🚀 数组名的理解

首先想和各位说的一点就是,其实我们的数组名就是我们这个数组的地址,那这个数组的地址究竟是整个数组的地址呢?还是说是我们的数组首元素的地址,还是说是我们数组结尾的那个地址。这里我们打印出来看看。

 那我们通过我们对我们的数组打印地址,我们发现我们的首元素地址和我们的数组名代表的地址是一样的,那么我们的数组的地址其实也就明白了。

那如果我们对我们的地址名取地址呢?这里我们也可以打印看看。

这里我们惊讶的发现这两个地址,居然是一样的,那这样是否可以说明我们&arr与arr,是一样的?

这里小赵给大家的解释一下,虽然这两个地址是一样的但含义却是极大的不同,这个就和我们之前聊的单位一样,(char int 等),&arr取得是整个数组的地址,所以如果你对它加一,它的单位实际上就是加了一个数组,那另外一个呢?如果只是arr,它其实代表的就是首元素地址,几乎等价于&arr[0],那它加的就是以每一个数组的元素的大小为单位,那么它的加一,就是加到数组的下一个数组的元素。

🚀使用指针访问数组

好了有了上面的知识作为支撑,我们就可以试着去探讨如何用我们的指针和我们的数组相联系了。

int main()
{int arr[10] = { 0};int i = 0;int sz = sizeof(arr) / sizeof(arr[0]);//数组有多少个元素int* p = arr;//取首元素地址for (int i = 0; i < sz; i++){scanf("%d", p + i);//给数组每个位置赋值}for (i = 0; i < sz; i++){printf("%d", *(p + i));//打印数组每个位置}return 0;
}

这个代码小赵要和大家解释一下,这里我们的数组是定义好的,而且我们的指针指向的是首元素的地址,那么我们其实对它加加就相当于从数组首元素向后走,其实还要给大家补个概念,其实我们的指针指向那个地址,那我们的p其实就相当于这个地址,这也就是我们说的指针=地址。那这里就是地址就不用加&,而下面我们打印的是地址里面的东西所以要解地址操作。 

那这时候有人要问了,能不能超过这个数组进行访问了,其实是不行的,因为这里就是一种越界。

int main()
{int arr[10] = { 0 };int i = 0;int sz = sizeof(arr) / sizeof(arr[0]);//数组有多少个元素int* p = arr;//取首元素地址for (i = 0; i < sz+1; i++){printf("%d", *(p + i));//打印数组每个位置}return 0;
}

那这个空间就是未知打印的东西也是未知的。再看这个 

int main()
{int arr[3] = { 0 };int i = 0;int sz = sizeof(arr) / sizeof(arr[0]);//数组有多少个元素int* p = arr;//取首元素地址for ( i = 0; i < sz+1; i++){scanf("%d", p + i);//给数组每个位置赋值}for (i = 0; i < sz+1; i++){printf("%d", *(p + i));//打印数组每个位置}return 0;
}

在这里我们就使用了未被定义的空间,那么我们的程序是会报错的。所以这种未被定义的空间是不能使用的,我们指针指向它的时候,其实我们的指针也就相当于野指针。 

✈一维数组传参的本质

#include <stdio.h>
void test(int arr[])
{int sz2 = sizeof(arr) / sizeof(arr[0]);printf("sz2 = %d\n", sz2);
}
int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };int sz1 = sizeof(arr) / sizeof(arr[0]);printf("sz1 = %d\n", sz1);test(arr);return 0;

在讲一维数组传参本质前,先给大家看一段代码,这个代码应该就是我们学指针前所用的数组传入函数的方式,但我们其实不知道的是,我们传入的究竟是整个数组的,还是数组的某一个呢?这个代码刚好可以帮我门验证一下这一点。 

在这里我们可以看到我们代码运行后的结果是 10,1这就证明了,我们传入的数组其实只传了一个,那么为什么会这样呢?其实结合小赵这章所聊的内容,我们可以知道我们传入函数的是数组名,那数组名是什么呢?其实就是指针啦,那就证明我们传入函数的其实是个数组首元素指针。那这就好办了,那我们以后数组,不就可以用指针传了吗?而且指针改的是地址,可以让我们在函数中的操作,进入我们的数组中。这里补的一点是,sizeof(arr),计算的是这个所代表的存在里面东西的大小,如果在函数外面,它表示就是整个数组(这一点比较特殊,只有在sizeof这里才代表这个意思。),而我们在函数里面它代表的就是我们传入的首元素的地址,计算的也只有数组首元素的大小。)

void test(int* arr)//参数写成指针形式
{printf("%d\n", sizeof(arr) / sizeof(arr[0]));//计算⼀个指针变量的⼤⼩
}int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };int sz1 = sizeof(arr) / sizeof(arr[0]);printf("sz1 = %d\n", sz1);test(arr);return 0;
}

这里我们用指针试试 ,发现结果一样,与我们的猜想是一致的。

✈冒泡排序

有了上面的知识我们就可以试着用指针来编我们的冒泡排序了。

#include <stdio.h>
void BOBBLE(int* a)//优化版冒泡排序
{for (int m = 0; m < 4; m++){int flag = 1;//判断是否排序完成for (int j = 0; j < 4 - m; j++){if (a[j] >a[j + 1])//如果a[j+i]都大于arr[j],则证明排序已经完成了{flag = 0;//如果排序未完成,那么将falg置为零int t = a[j + 1];a[j + 1] = a[j];a[j] = t;}}if (flag == 1)//如果完成,结束循环break;}}
int main()
{int a[5] = { 0 };int i;for (i = 0; i < 5; i++){scanf("%d", &a[i]);}BOBBLE(a);for (int m = 0; m < 5; m++){printf("%d", a[m]);}return 0;
}

我们会发现有了指针的加入,让我们原本冗杂的长的代码变得更容易看懂,可以将我们的冒泡排序加入到我们的函数中去了,而且由于我们的指针改的其实是数组地址里面的内容,那这就让我们的数组可以真正的改变了(这个有点类似(1)的交换 )。

同时我们对我们的之前的冒泡排序进行了优化,让我们的代码可以不用执行的很复杂,大大加快代码了运行速度。

🏆二级指针

那好了数组解决了吧,那我们一级指针的地址咋说,谁能来管管呢?就这样我们的二级指针诞生了。

当然,我们得先看看我们指针是否有地址,看看它的地址。

我们看到我们指针指向的地址,与我们指针本身的地址是不一样的,这也就间接证明了,我们的指针其实是有自己独立的地址。

那我们的二级指针是什么样的,其实也蛮简单的,我们可以用上一章的思想解决,打开一个空间要一个钥匙,那我们现在打开两个呢?那不就是两个吗。

int main()
{int a = 0;int* p = &a;int** b = &p;printf("%p\n", &a);printf("%p\n", p);printf("%p\n", &p);printf("%p", b);
}

那我怎么才能访问到里面的a能其实也就是开两次锁。

🏆指针数组

那紧接着下来就是,我们有木有这样一个数组里面存的是指针呢?那其实也是有的。我们之前聊到,数组前面的那个就是它里面的元素类型,那我们只要将前面写成int*,char*等就可以了。

int main()
{int a = 0;int* p = &a;int* b[1] = { p };//指针数组printf("%d", *(b[0]));
}

🏆指针数组模拟二维数组

那一维数组聊完了,那二维数组呢?这里就不得不提小赵前面的概念了,二维数组里面每个元素其实就是一维数组,那既然是一维数组,我们是不是可以试着用一维数组的首元素地址代替呢?然后再对这个元素地址进行解指针操作,加一加二等,这样是不是就让指针数组模拟二维数组的操作完成了。 

#include <stdio.h>
int main()
{int arr1[] = { 1,2,3,4,5 };int arr2[] = { 2,3,4,5,6 };int arr3[] = { 3,4,5,6,7 };int* parr[3] = { arr1, arr2, arr3 };//数组名是数组⾸元素的地址,类型是int*的,就可以存放在parr数组中int i = 0;int j = 0;for (i = 0; i < 3; i++){for (j = 0; j < 5; j++){printf("%d ", parr[i][j]);//这里要解释一下,其实我们a[1]=*(a+1)的操作,那我们这里的arr[1][2]其实也就等于*(arr[1]+2)的操作。}printf("\n");}return 0;
}

这里要和家人们 解释的其实就是我批注的那一块,我们的arr[]这个操作其实就是在对我们的arr进行解指针的操作,而括号里面的数就是我们arr+数字,地址的移动,然后进行解指针。 

🎉结束语

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/220445.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

element组件库的日期选择器如何限制?

本次项目中涉及到根据日期查找出来的数据进行调整,所以修改的数据必须是查找范围内的数据.需要对调整数据的日期进行限制,效果如下: 首先我们使用了element 组件库的日期选择器,其中灌完介绍, picker-options中函数disabledDate可以设置禁用状态,代码如下: <el-date-pickerv…

【SpringBoot】参数校验及异常处理

实现注册功能时经常遇到参数校验的问题。 参数校验 引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId> </dependency>参数前添加注解&#xff0c;并指定校验规…

【数据分析之Numpy】Numpy中复制函数numpy.repeat()与numpy.tile()的使用方法及区别

一、简介 numpy.repeat()与numpy.tile()都是Numpy库中的复制函数&#xff0c;用于将数组中的元素重复指定的次数。 numpy.repeat()函数接受三个参数&#xff1a;要重复的数组、重复的次数和重复的轴。 numpy.tile()函数接受两个参数&#xff1a;要重复的数组和重复的次数。 二…

Github 2023-12-18 开源项目周报 Top14

根据Github Trendings的统计&#xff0c;本周(2023-12-18统计)共有14个项目上榜。根据开发语言中项目的数量&#xff0c;汇总情况如下&#xff1a; 开发语言项目数量TypeScript项目4Python项目4Jupyter Notebook项目3非开发语言项目1JavaScript项目1Rust项目1Go项目1 基于项目…

自然语言处理阅读第二弹

HuggingFace 镜像网站模型库 NLP中的自回归模型和自编码模型 自回归&#xff1a;根据上文内容预测下一个可能的单词&#xff0c;或者根据下文预测上一个可能的单词。只能利用上文或者下文的信息&#xff0c;不能同时利用上文和下文的信息。自编码&#xff1a;对输入的句子随…

MyBatis的查询方法!!!

准备工作&#xff1a;1.创建一个maven工程&#xff0c;然后将pojo类导入到项目中去。 2.导入依赖到pom.xml文件中 3.在resources中创建log4j.properites和mybatis-config.xml 4.创建UserMapper接口和UserMapper.xml文件 5.创建测试类MyBatisTest 1.创建Maven工程&#xff0c;还…

Windows 安装RocketMQ

1.rocketmq下载 https://archive.apache.org/dist/rocketmq/5.1.4/ 2.环境准备 64位JDK 1.8; Maven 3.2.x; 64位操作系统系统&#xff0c;本文档在Windows上安装 3.解压到一个无中文无空格的目录下&#xff0c;解压后目录如下&#xff1a; 配置环境变量 4.更改配置 java的…

2018年第七届数学建模国际赛小美赛B题世界杯足球赛的赛制安排解题全过程文档及程序

2018年第七届数学建模国际赛小美赛 B题 世界杯足球赛的赛制安排 原题再现&#xff1a; 有32支球队参加国际足联世界杯决赛阶段的比赛。但从2026年开始&#xff0c;球队的数量将增加到48支。由于时间有限&#xff0c;一支球队不能打太多比赛。因此&#xff0c;国际足联提议改变…

SpringBoot Whitelabel Error Page 报错--【已解决】

springboot 报错信息如下 这个报错页面就是个404 &#xff0c;代表你访问的url 没有对应的的requestmapping 其实没啥影响的一个问题&#xff0c;但是看到Error 就是不爽&#xff0c;改了他丫的 解决方法如下 一、调整application.properties配置【治标不治本】 server.err…

HIVE窗口函数

什么是窗口函数 hive中开窗函数通过over关键字声明&#xff1b;窗口函数&#xff0c;准确地说&#xff0c;函数在窗口中的应用&#xff1b;比如sum函数不仅可在group by后聚合&#xff0c;在可在窗口中应用&#xff1b; hive中groupby算子和开窗over&#xff0c;shuffle的逻辑…

Cockpit upload文件上传漏洞(CVE-2023-1313)

0x01 产品简介 Cockpit 是一个自托管、灵活且用户友好的无头内容平台,用于创建自定义数字体验。 0x02 漏洞概述 Cockpit assetsmanager/upload接口处存在文件上传漏洞,攻击者可通过该漏洞在服务器端任意上传代码,写入后门,获取服务器权限,进而控制整个web服务器。 0x0…

深入理解 hash 和 history:网页导航的基础(下)

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…

Linux 特殊符号

目录 1. # 注释 2. &#xff1b;命令分隔符 3. .. 上级目录 4. . 当前目录 5. " " 换行&#xff0c;解析变量 6. 换行&#xff0c;不解析变量 7. \ 和 / 8. &#xff01;历史命令调用&#xff0c;取反 9. * 通配符 10. $ 调用变量 11. | 管道 12. || …

亿发零售云引领新零售时代:智能收银系统助力连锁门店多业态发展

近来&#xff0c;越来越多零售从业者认识到&#xff0c;线上和线下全渠道整合将成为国内消费市场的主要趋势。在这个趋势中&#xff0c;线下门店仍然被视为品牌发展的重要价值来源。 在连锁门店中&#xff0c;收银系统被认为是运营管理的关键工具&#xff0c;能够为品牌门店提供…

MySQL数据库 DDL

目录 一、DDL 二、操作数据库 三、操作表 四、数据类型 五、表操作案例 六、修改表 七、删除表 一、DDL Data Definition Language&#xff0c;数据定义语言&#xff0c;用来定义数据库对象(数据库&#xff0c;表&#xff0c;字段) 。 二、操作数据库 &#xff08;1&am…

ControlNet Adding Conditional Control to Text-to-Image Diffusion Models

ControlNet: Adding Conditional Control to Text-to-Image Diffusion Models TL; DR&#xff1a;ControlNet 使得我们能通过输入额外的条件图&#xff08;如 Canny 边缘、人体姿态、深度图等&#xff09;&#xff0c;对 SD 生成结果的空间位置有更准确的控制。它拷贝 SD 部分…

基于Springboot的旅游网站设计与实现(论文+调试+源码)

项目描述 临近学期结束&#xff0c;还是毕业设计&#xff0c;你还在做java程序网络编程&#xff0c;期末作业&#xff0c;老师的作业要求觉得大了吗?不知道毕业设计该怎么办?网页功能的数量是否太多?没有合适的类型或系统?等等。这里根据疫情当下&#xff0c;你想解决的问…

Golang清晰代码指南

发挥易读和易维护软件的好处 - 第一部分 嗨&#xff0c;开发者们&#xff0c;清晰的代码是指编写易于阅读、理解和维护的软件代码。它是遵循一组原则和实践&#xff0c;优先考虑清晰性、简单性和一致性的代码。清晰的代码旨在使代码库更易管理&#xff0c;减少引入错误的可能性…

【老牌期刊】IF:6+,2天预审,3-5个月录用!

期刊简介 1区计算机智能类SCI 【期刊概况】IF&#xff1a;6.0-7.0&#xff0c;JCR1区&#xff0c;中科院2区&#xff1b; 【终审周期】走期刊部系统&#xff0c;3个月左右录用&#xff1b; 【检索情况】SCI检索&#xff1b; 【WOS收录年份】2018年&#xff1b; 【自引率】…

记录一下github深度学习的错误

1.[visdom]无法正常启动服务问题解决 在Anaconda命令窗口中&#xff1a; 使用python -m visdom.server启动visdom服务时&#xff0c;卡在&#xff1a; Checking for scripts. Downloading scripts, this may take a little while 无法下载和启动服务。 ERROR&#xff1a;由…