灵魂指针,教给(二)

欢迎来到白刘的领域   Miracle_86.-CSDN博客

系列专栏  C语言知识

先赞后看,已成习惯

   创作不易,多多支持!

目录

一、数组名的理解 

二、使用指针访问数组

三、一维数组传参本质

四、冒泡排序

五、二级指针

六、指针数组

七、指针数组模拟二维数组


一、数组名的理解 

在上一篇博客中,我们在使用指针访问数组时,有这样的代码:

int arr[10] = {1,2,3,4,5,6,7,8,9,10};
int *p = &arr[0];

详见:灵魂指针,教给(一)-CSDN博客

这里我们使用了&arr[0]的方式拿到了数组的首元素地址,但是数组名本身就是地址,而且是数组首元素的地址,我们可以做个测试,来看下面这段代码:

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

第一个printf打印arr[0]的地址,第二个打印数组名代表的地址。

来看运行结果:

我们可以发现,这两个地址一模一样,这也证明了,数组名就是数组首元素(第一个元素)的地址

我们再来看一段代码:

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

我们在前面讲到,指针(地址)的大小是4或者8个字节,而arr是数组名,是数组首元素的地址,那这个应该是打印4或者8,但是这段代码的结果为40。这是为什么呢?

确实,arr作为数组名是数组首元素的地址,但是这里有两个例外:

sizeof(数组名),在sizeof中单独存放数组名,这里的数组代表整个数组,计算的是整个数组的大小,单位为字节

 

&数组名,这里的数组名也表示整个数组,取出的是整个数组的地址(整个数组的地址和数组首元素地址是有区别的)。

除了上述两个例外,其它任何位置出现数组名,都是代表的数组首元素地址。

那我们再来看一段代码:

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

 来看结果:

我们发现,三个地址完全相同,那arr和&arr到底有啥区别呢?别着急,我们继续来做个测试:

#include <stdio.h>
int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };printf("&arr[0] = %p\n", &arr[0]);printf("&arr[0]+1 = %p\n", &arr[0]+1);printf("arr = %p\n", arr);printf("arr+1 = %p\n", arr+1);printf("&arr = %p\n", &arr);printf("&arr+1 = %p\n", &arr+1);return 0;
}

来看运行结果:

这里挑了一组比较明显的结果,我们可以发现&arr[0]和&arr[0]+1之间差了4个字节,arr和arr+1之间也差了4个字节,这是因为它们都是数组首元素的地址,+1的意思是跳过一个元素。

但是&arr和&arr+1差了有40个字节,因为&arr是数组的地址,而arr有10个元素,每个元素的大小是4个字节,所以arr的大小为40个字节,+1是跳过整个数组。

讲到这,想必大家都明白数组名的意义了吧。

二、使用指针访问数组

前面做好铺垫,我们就可以很容易地使用指针访问数组了。

#include <stdio.h>
int main()
{int arr[10] = {0};//输⼊int i = 0;int sz = sizeof(arr)/sizeof(arr[0]);//输⼊int* p = arr;for(i=0; i<sz; i++){scanf("%d", p+i);//scanf("%d", arr+i);//也可以这样写}//输出for(i=0; i<sz; i++){printf("%d ", *(p+i));}return 0;
}

创建了一个int类型的指针p,指向数组arr的首元素的地址,然后p+i代表指向第i个元素,再对其解引用进行输出。

上述代码搞清之后,我们不妨大胆想象一下,,既然arr和p是等价的,那我们既然可以写arr[i],那可不可以写p[i]呢?答案是可以,来看代码:

#include <stdio.h>
int main()
{int arr[10] = {0};//输⼊int i = 0;int sz = sizeof(arr)/sizeof(arr[0]);//输⼊int* p = arr;for(i=0; i<sz; i++){scanf("%d", p+i);//scanf("%d", arr+i);//也可以这样写}//输出for(i=0; i<sz; i++){printf("%d ", p[i]);}return 0;
}

运行结果,毫无疑问是没有问题的:

同理arr[i]也等价于*(arr+i),数字元素的访问在编译器处理的时候,也是转换为首元素地址+偏移量求出元素地址,然后解引用访问。

三、一维数组传参本质

数组我们已经很熟悉了,我们在之前也有所提到过,数组可以传递给函数,如果有忘了的老铁可以进入下方传送门:

C语言中的工具箱——函数-CSDN博客

那今天我们来认真介绍一下一维数组传参的本质 。

首先,引出一个问题,我们之前计算数组元素个数,都是在函数外部的,那我们在函数内部计算个数可以吗?

#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;
}

我们来看运行结果:

可以看出在函数内部没有得到正确的数组元素个数。

所以我们来学习数组传参的本质,刚刚我们介绍过:数组名是数组首元素的地址;那么在数组传参的时候,传递的是数组名,也就是说数组传参的本质上是传递数组首元素的地址。

所以函数形参理论上应该使用指针变量来接收首元素地址。你们函数内部我们在写sizeof(arr)计算的是一个地址的大小而不是数组的大小(单位:字节)。正是因为函数的参数部分的本质是指针,所以在函数内部是没法求数组元素个数的。

总结:一维数组传参,形参可以写成数组,也可以写成指针。 

四、冒泡排序

排序想必大家都知道是什么意思,但是这个冒泡是什么意思呢?咕嘟咕嘟冒泡泡嘛?事实上,确实可以是这样的。

来看下图:

 

我们在生活中可以看见这种现象:烧开水冒的泡泡或者是我们在水下吐泡泡,从下往上移动,越接近水面泡泡会越来越大。上面这个动图也是如此,我们看5是最大的数字,所以我们要让它慢慢浮到后面(或者前面)。

冒泡排序的核心思想:两两相邻元素进行比较。其实也可以总结为:两层循环,一层判断。

直接上代码:

//⽅法1
#include<stdio.h>
void bubble_sort(int arr[], int sz)//参数接收数组元素个数
{int i = 0;for(i=0; i<sz-1; i++){int j = 0;for(j=0; j<sz-i-1; j++){if(arr[j] > arr[j+1]){int tmp = arr[j];arr[j] = arr[j+1];arr[j+1] = tmp;}}}
}
int main()
{int arr[] = {3,1,7,5,8,9,0,2,4,6};int sz = sizeof(arr)/sizeof(arr[0]);bubble_sort(arr, sz);for(int i=0; i<sz; i++){printf("%d ", arr[i]);}return 0;
}

 我们来看第二种优化后的方法:

//⽅法2 - 优化
#include<stdio.h>
void bubble_sort(int arr[], int sz)//参数接收数组元素个数
{int i = 0;for (i = 0; i < sz - 1; i++){int flag = 1;//假设这⼀趟已经有序了int j = 0;for (j = 0; j < sz - i - 1; j++){if (arr[j] > arr[j + 1]){flag = 0;//发⽣交换就说明,⽆序int tmp = arr[j];arr[j] = arr[j + 1];arr[j + 1] = tmp;}}if (flag == 1)//这⼀趟没交换就说明已经有序,后续⽆序排序了break;}
}
int main()
{int arr[] = { 3,1,7,5,8,9,0,2,4,6 };int sz = sizeof(arr) / sizeof(arr[0]);bubble_sort(arr, sz);for (int i = 0; i < sz; i++){printf("%d ", arr[i]);}return 0;
}

由于第一种可能存在无效遍历,所以我们第二种方法设置了一个flag变量来判断是否有序。

五、二级指针

二级指针,听起来很牛逼的样子,但实际上还是挺好理解的。我们说指针变量,它其实也是个变量,是变量就有地址,那它的地址存放在哪里呢?

这就引出了二级指针。

上图中的ppa就是指向指针pa的二级指针,存放的是pa的地址,它的类型是int * *,

**ppa = 30,等价于*pa = 30,等价于a  = 30。

六、指针数组

指针数组这个名字,很抽象,你说它到底是指针还是数组?答案是数组,其实它是这么理解的:(装着)指针(的)数组。后面我们还会学到一个非常非常容易和它混淆的东西叫:数组指针。我们中国汉语一般是习惯把定于放在中心词的前面,像红苹果、小朋友...这俩玩意也不例外,数组指针和指针数组,谁在后面就是谁。

我们还可以类比一下其它类型的数组,像整型数组和字符数组。

 指针数组是用来存放指针(地址)的。

如下图:

指针数组的每个元素是地址,又可以指向一块区域。

指针数组有什么用呢?我们接着往下学习。

七、指针数组模拟二维数组

既然指针数组可以存放指针,那我每个指针都再指向一个地址,是不是就模拟出二维数组了,因为二维数组的本质是元素为一维数组的数组,我们在之前也有提过:

C语言中的百宝箱——数组(2)-CSDN博客

画图的话就是如下:

我们来代码实现一下:

#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数组中int* parr[3] = { arr1, arr2, arr3 };int i = 0;int j = 0;for (i = 0; i < 3; i++){for (j = 0; j < 5; j++){printf("%d ", parr[i][j]);}printf("\n");}return 0;
}

 运行结果如下:

注:上述代码仅是模拟出二维数组的效果,并非完全是二维数组,因为每一行是非连续的。 

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

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

相关文章

黑马点评-好友关注实现

关注和取关 针对用户的操作&#xff0c;可以对用户进行关注和取消关注功能&#xff1a; 需要实现两个接口&#xff1a; 关注和取关接口 判断是否关注的接口 接口&#xff1a; //关注和取关 PutMapping("/{id}/{isFollow}") public Result follow(PathVariable(&…

Jmeter查看结果树之查看响应的13种详解方法

Jmeter查看结果树查看响应有哪几种方法&#xff0c;可通过左侧面板底部的下拉框选择: 01 Text 查看结果树中请求的默认格式为Text&#xff0c;显示取样器结果、请求、响应数据3个部分内容。 取样器结果&#xff1a; 默认Raw展示&#xff0c;可以切换为Parsed视图&#xff0c…

Python爬虫——scrapy-4

免责声明 本文章仅用于学习交流&#xff0c;无任何商业用途 部分图片来自尚硅谷 meta简介 在Scrapy框架中&#xff0c;可以使用meta属性来传递额外的信息。meta属性可以在不同的组件之间传递数据&#xff0c;包括爬虫、中间件和管道等。 在爬虫中&#xff0c;可以使用meta属…

7-4 哲哲打游戏(Python)

哲哲是一位硬核游戏玩家。最近一款名叫《达诺达诺》的新游戏刚刚上市&#xff0c;哲哲自然要快速攻略游戏&#xff0c;守护硬核游戏玩家的一切&#xff01; 为简化模型&#xff0c;我们不妨假设游戏有 N 个剧情点&#xff0c;通过游戏里不同的操作或选择可以从某个剧情点去往另…

rabbitmq4

独占队列&#xff1a;我们的队列只能被当前通道所绑定&#xff0c;不能被其他的连接所绑定&#xff0c;如果有其他的通道或连接再使用此队列的话&#xff0c;会直接报错&#xff0c;一般设置为false&#xff1a; autoDelete&#xff1a;消费者在消费完队列&#xff0c;并且彻底…

20 卷积层里的填充和步幅【李沐动手学深度学习v2课程笔记】

1. 填充和步幅 在上下左右分别填充一些0 2. 代码实现 2.1 填充 我们创建一个高度和宽度为3的二维卷积层&#xff0c;并在所有侧边填充1个像素。给定高度和宽度为8的输入&#xff0c;则输出的高度和宽度也是8。 import torch from torch import nn# 为了方便起见&#xff0c;…

第三百八十九回

文章目录 1. 概念介绍2. 使用方法2.1 获取所有时区2.2 转换时区时间 3. 示例代码4. 内容总结 我们在上一章回中介绍了"分享一些好的Flutter站点"相关的内容&#xff0c;本章回中将介绍timezone包.闲话休提&#xff0c;让我们一起Talk Flutter吧。 1. 概念介绍 我们在…

LeetCode 2482.行和列中一和零的差值

给你一个下标从 0 开始的 m x n 二进制矩阵 grid 。 我们按照如下过程&#xff0c;定义一个下标从 0 开始的 m x n 差值矩阵 diff &#xff1a; 令第 i 行一的数目为 onesRowi 。 令第 j 列一的数目为 onesColj 。 令第 i 行零的数目为 zerosRowi 。 令第 j 列零的数目为 zer…

el-form-item内的el-select如何自适应宽度

最近在使用element-ui做后台管理的时候&#xff0c;有个需求是在弹窗组件里面&#xff0c;添加一个el-select下拉框选项&#xff0c;但是给el-select设置的宽度无法自适应&#xff0c;原因很简单&#xff0c;我们不需要设置固定宽度&#xff0c;设置百分比就行了&#xff0c;让…

【框架设计】MVC、MVP、MVVM对比图

1. MVC&#xff08;Model-View-Controller&#xff09; 2. MVP&#xff08;Model-View-Presenter&#xff09; 3. MVVM&#xff08;Model-View-ViewModel&#xff09;

Golang基于Redis bitmap实现布隆过滤器(完结版)

Golang基于Redis bitmap实现布隆过滤器&#xff08;完结版&#xff09; 为了防止黑客恶意刷接口&#xff08;请求压根不存在的数据&#xff09;&#xff0c;目前通常有以下几种做法&#xff1a; 限制IP&#xff08;限流&#xff09;Redis缓存不存在的key布隆过滤器挡在Redis前 …

对simplex算法的时间复杂度进行分析

对于simplex算法,如果每进行一次pivot变换,目标函数所得到的结果都会有可能出现增加的情况,所以得到的结论中,可以肯定它的值是一定不会出现减少的情况的,每次从目标函数中找到一个系数大于0的变量,然后再在约束条件中选取能够让它的增值最少的那个来继续进行pivot变换。…

linux kernel物理内存概述(五)

目录 概述 一、快速路径分配 1、get_page_from_freelist 2、rmqueue()函数 二、慢速路径分配 1、分配流程 三、direct_compact 概述 物理内存分配步骤 1、初始化,参数初始化 2、内存充足&#xff0c;快速分配 get_page_from_freelist 3、内存压力大&#xff0c;慢速…

类和对象-C++运算符重载

#include <iostream> #include <string> using namespace std;class Person { public:Person(int age){m_Agenew int (age);}~Person(){if(m_Age!NULL){delete m_Age;m_AgeNULL;}}//重载 赋值运算符Person& operator (Person &p){//编译器提供深拷贝//m_Ag…

嵌入式软件开发工程师如何提高C语言编码技能?

嵌入式软件开发工程师如何提高C语言编码技能&#xff1f; 在开始前我分享下我的经历&#xff0c;我刚入行时遇到一个好公司和师父&#xff0c;给了我机会&#xff0c;一年时间从3k薪资涨到18k的&#xff0c; 我师父给了一些 电气工程师学习方法和资料&#xff0c;让我不断提升…

vSphere 8考试认证题库 2024最新(VCP 8.0版本)

VMware VCP-DCV&#xff08;2V0-21.23&#xff09;认证考试题库&#xff0c;已全部更新&#xff0c;答案已经完成校对&#xff0c;完整题库请扫描上方二维码访问。正常考可以考到450分以上&#xff08;满分500分&#xff0c;300分通过&#xff09; An administrator is tasked …

echarts 模拟时间轴播放效果

使用echarts柱状图模拟时间轴播放控制。开始/暂停/快进/慢进/点选 代码可直接放echart官方示例执行 let start new Date(2024-01-01); let end new Date(2024-01-10); let diff end - start; let dotLen 200;let data [start, end]; option {color: [#3398DB],tooltip: …

论文解读:Rectifying the Shortcut Learning of Background for Few-Shot Learning

文章汇总 问题&动机&解决方法 图像背景是一种有害知识的来源&#xff0c;这是少数镜头学习模型容易吸收的(问题) 通过在训练和评估中提取图像中的前景目标而不需要任何额外的监督来解决这个问题(动机) 在训练和评估时将模型的注意力吸引到图像前景上(方法) 摘要 …

Day25:安全开发-PHP应用文件管理模块包含上传遍历写入删除下载安全

目录 PHP文件操作安全 文件包含 文件删除 文件编辑 文件下载 云产品OSS存储对象去存储文件(泄漏安全) 思维导图 PHP知识点 功能&#xff1a;新闻列表&#xff0c;会员中心&#xff0c;资源下载&#xff0c;留言版&#xff0c;后台模块&#xff0c;模版引用&#xff0c;框…

关于出国留学和考研比较----以本人双非跨考计算机为例

文章目录 中心论点国内就业现状勿让旧认知害了自己那出国留学真的一无是处了吗?1. 藤校仍旧是具有极高价值2. 时间成本低3. 研究生一定比单纯的本科找工作强!4. 很多人说出国读博好,可以无脑入,真是这样吗? 中心论点 如果在选择出国留学还是国内考研的最终核心诉求都是有更好…