C语言——深入理解指针3

news2024/11/24 5:24:19

目录

  • 1. 数组名的理解
    • 1. 数组名
    • 1.2 数组名理解的特例
  • 2. 使用指针访问数组
  • 3. 一维数组传参的本质
  • 4. 冒泡排序
    • 4.1 冒泡排序的概念
    • 4.2 冒泡排序的优化
  • 5. 二级指针
    • 5.1 二级指针的概念
    • 5.2 二级指针的运算
  • 6. 指针数组
  • 7. 指针数组模拟二维数组

1. 数组名的理解

1. 数组名

  • 在上⼀个博客我们在使用指针访问数组的内容时,有这样的代码
  • 上一个博客的链接在这里——深入理解指针2
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
int *p = &arr[0];
  • 这⾥我们使用 &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;
}
  • 运行结果为:

在这里插入图片描述

  • 我们发现数组名和数组⾸元素的地址打印出的结果⼀模⼀样,数组名就是数组首元素(第⼀个元素)的地址。

1.2 数组名理解的特例

  • 这时候有同学会有疑问?数组名如果是数组首元素的地址,那下面的代码怎么理解呢?
#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;
}

在这里插入图片描述

  • 输出的结果是:40,如果arr是数组首元素的地址,那输出应该的应该是4/8才对
  • 其实数组名就是数组首元素(第⼀个元素)的地址是对的,但是有两个例外:
  1. sizeof(数组名),sizeof中单独放数组名,这里的数组名表示整个数组,计算的是整个数组的大小,单位是字节
  2. &数组名,这里的数组名表示整个数组,取出的是整个数组的地(整个数组的地址和数组首元素的地址是有区别的)
  • 除此之外,任何地方使用数组名,数组名都表示首元素的地址。
  • 这时有好奇的同学,再试⼀下这个代码:
#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个字节,是因为&arr[0]和arr都是首元素的地址,+1就是跳过⼀个元素
  • 但是&arr和&arr+1相差40个字节,这就是因为&arr是数组的地址,+1操作是跳过整个数组的。到这里大家应该搞清楚数组名的意义了吧

2. 使用指针访问数组

  • 有了前面知识的支撑,再结合数组的特点,我们就可以很方便的使用指针访问数组了
#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是数组首元素的地址,可以赋值给p,其实数组名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;
}
  • 我们可以看到,如果将* (p+i) 换成 p[i] 也是能够正常打印的,所以本质上 p[i] 是等价于 * (p+i)
  • 同理 arr[i] 应该等价于* (arr+i),数组元素的访问在编译器处理的时候,也是转换成首元素的地址+偏移量求出元素的地址,然后解引用来访问的
  • 即在这段代码里这四个都是等价的(如下图)

在这里插入图片描述

  • 这里我们把 [ ] 的作用概括成将两个操作数相加再解引用
  • 这里再拓展一下,既然 arr+i 是相加的关系,又因为相加满足交换律
  • 所以也可以写成 i+arr,即arr[i] 又等价于 i[arr] ,p[i] 等价于 i[p]
  • 但是这样写的很别扭,所以一般就不使用这种写法

3. 一维数组传参的本质

  • 数组我们学过了,之前也说过了,数组是可以传递给函数的,这个地方我们讨论⼀下数组传参的本质
  • 首先从⼀个问题开始,我们之前都是在函数外部计算数组的元素个数,那我们可以把函数传给⼀个函数后,函数内部求数组的元素个数吗?
#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;
}
  • 代码在×86环境的结果为:

在这里插入图片描述

  • 我们发现在函数内部是没有正确获得数组的元素个数
  • 这就要学习数组传参的本质了,上个小节我们学习了:数组名是数组⾸元素的地址;
  • 那么在数组传参的时候,传递的是数组名,也就是说本质上数组传参本质上传递的是数组⾸元素的地址
  • 所以函数形参的部分理论上应该使用指针变量来接收首元素的地址。那么在函数内部我们写sizeof(arr) 计算的是⼀个地址的大小(单位字节),而这取决于编译环境,所以并不是数组的大小(单位字节)。
  • 正是因为函数的参数部分是本质是指针,所以在函数内部是没办法求的数组元素个数的
void test(int arr[])//参数写成数组形式,本质上还是指针
{
	printf("%d\n", sizeof(arr));
}

void test(int* arr)//参数写成指针形式
{
	printf("%d\n", sizeof(arr));//计算⼀个指针变量的⼤⼩
}

int main()
{
	int arr[10] = {1,2,3,4,5,6,7,8,9,10};
	test(arr);
	return 0;
}
  • 总结:⼀维数组传参,形参的部分可以写成数组的形式,也可以写成指针的形式

4. 冒泡排序

4.1 冒泡排序的概念

  • 冒泡排序的核心思想就是:两两相邻的元素进行比较。
  • 比如我们先让所有的元素都比较一次,如果前一个小就把两数交换,
#include <stdio.h>
int main()
{
	int arr[10] = {10,9,8,7,6,5,4,3,2,1};
	int sz = sizeof(arr) / sizeof(arr[0]);
	
	for (int j=0; j<sz-1; j++)
	{
		if (arr[j] > arr[j+1])
		{
			int tmp = arr[j];
			arr[j] = arr[j+1];
			arr[j+1] = tmp;
		}
	}//sz个元素只需比较sz-1次

	for (int i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
}
  • 运行结果如下:

在这里插入图片描述

  • 我们发现此时已经把最大的那个元素放到最后面了,那如果我们能把10除外的其他元素再进行同样的比较,然后把第二个大的数放到倒数第二个位置……以此类推,最终就可以完成数组升序的操作了
  • 代码如下:
#include <stdio.h>
int main()
{
	int arr[10] = {10,9,8,7,6,5,4,3,2,1};
	int sz = sizeof(arr) / sizeof(arr[0]);

	for (int i = 0; i < sz - 1; i++)
	{
		for (int 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;
			}
		}//循环一次后最大的数已经放到末尾了,无需再参与比较,即每次循环后可以减少一次比较
	}//循环sz-1次后,下标从1开始的数一定比下标为0的大,无需再比较

	for (int i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
}
  • 运行结果:

在这里插入图片描述

4.2 冒泡排序的优化

  • 此时又有一个问题了,如果我一开始的时候就是有序的,那还需要比较嘛,显然是不需要的了,所以我们可以再引入一个变量 flag 来标记是否数组已经有序了,
  • 优化后代码如下:
#include <stdio.h>
int main()
{
	int arr[10] = {1,2,3,4,5,6,7,8,9,10};
	int sz = sizeof(arr) / sizeof(arr[0]);

	for (int i = 0; i < sz - 1; i++)
	{
		int flag = 1;//假设数组有序

		for (int 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;
				flag = 0;
			}
		}//循环一次后最大的数已经放到末尾了,无需再参与比较,即每次循环后可以减少一次比较

		if (flag == 1)
		{
			break;
		}//判断数组是否已经有序
	}//循环sz-1次后,下标从1开始的数一定比下标为0的大,无需再比较

	for (int i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
}

5. 二级指针

5.1 二级指针的概念

  • 指针变量也是变量,是变量就有地址,那指针变量的地址存放在哪里?
  • 这就是二级指针

在这里插入图片描述

  • 二级指针可以这样理解:

在这里插入图片描述

5.2 二级指针的运算

  • 对于⼆级指针的运算有:
  1. *ppa 通过对ppa中的地址进行解引用,这样找到的是 pa , *ppa 其实访问的就是 pa
int b = 20;
*ppa = &b;//等价于 pa = &b;
  1. **ppa 先通过 *ppa 找到 pa ,然后对 pa 进行解引用操作: *pa ,那找到的是 a
**ppa = 30;
//等价于*pa = 30;
//等价于a = 30;

6. 指针数组

  • 指针数组是指针还是数组?
  • 我们类比⼀下,整型数组,是存放整型的数组,字符数组是存放字符的数组。
  • 那指针数组呢?是存放指针的数组

在这里插入图片描述

  • 而指针数组的每个元素是地址,又可以指向⼀块区域
  • 这就有点像二维数组了

7. 指针数组模拟二维数组

#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]);//parr[i][j]可以理解成*(*(i+parr)+j)
		}
		printf("\n");
	}
	return 0;
}
  • 如图:

在这里插入图片描述

  • parr[i]是访问parr数组的元素,parr[i]找到的数组元素指向了整型⼀维数组,parr[i][j]就是整型⼀维数组中的元素
  • 上述的代码模拟出⼆维数组的效果,实际上并非完全是⼆维数组,因为每⼀行并非是连续的

最后,
恭喜你今天又遥遥领先了别人!

在这里插入图片描述

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

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

相关文章

重生奇迹MU 骑装选择攻略--剑士

剑士作为唯一一个攻防兼备的近战职业&#xff0c;战士大部分时间需要承担团队的坦克职责&#xff0c;因此我们需要尽可量的提升自己的血量以及防御属性&#xff0c;这样才能在面对敌人和大量野怪时保护好我方的后排目标&#xff0c;并且保证自己能够在猛烈的攻击下支撑更长的时…

HarmonyOS 线程讲解(任务分发、线程通信)

一、简单说明 说起鸿蒙的线程就不得不说Android的线程&#xff0c;相信都知道在Android中&#xff0c;每一个应用都会有自己的主线程和其他的子线程&#xff0c;主线程负责处理大部分业务&#xff0c;负责UI的显示和更新等操作&#xff0c;所以又称之为UI线程&#xff0c;同时…

Docker的使用方式

一、Docker概念 Docker类似于一个轻量的虚拟机。 容器和镜像是Docker中最重要的两个概念&#xff0c;镜像可以保存为tar文件&#xff0c;Dockerfile是配置文件&#xff0c;仓库保存了很多第三方已经做好的镜像。 基本指令 查找镜像 docker search nginx 拉取nginx镜像 do…

文件归类整理,文件归类软件

数字化时代&#xff0c;我们经常面对的一个问题是&#xff1a;电脑中的文件越积越多&#xff0c;找起东西来如同大海捞针。这个时候&#xff0c;一款好的文件归类软件无疑能够帮你节省大量的时间和精力。那还等什么&#xff1f;快来试试这款【文件批量改名高手】软件吧&#xf…

MyISAM和InnoDB区别

MyISAM是括全文索引、压缩、空间函数等&#xff0c;但MyISAM不支持事MySQL的默认数据库引擎&#xff08;5.5版之前&#xff09;。虽然性能极佳&#xff0c;而且提供了大量的特性&#xff0c;包务和行级锁&#xff0c;而且最大的缺陷就是崩溃后无法安全恢复。不过&#xff0c;5.…

Leetcode—1828. 统计一个圆中点的数目【中等】

2024每日刷题&#xff08;一零五&#xff09; Leetcode—1828. 统计一个圆中点的数目 实现代码 class Solution { public:vector<int> countPoints(vector<vector<int>>& points, vector<vector<int>>& queries) {vector<int> a…

WIN11 - WSL(Windows Subsystem for Linux) 安装教程

前言 WSL&#xff0c;即Windows Subsystem for Linux&#xff0c;是一种在Windows操作系统上运行Linux二进制文件的兼容层。该层提供了Linux环境和GNU工具&#xff0c;可以在Windows系统上运行Linux应用程序。WSL使得开发人员可以在Windows系统上使用Linux工具和命令行界面&am…

GIS系统的类型

GIS&#xff08;地理信息系统&#xff09;系统是一种用于采集、存储、管理、分析和展示地理信息的软件系统。这些系统可以根据其功能和应用领域划分为不同的类型。以下是一些常见类型的GIS系统以及在其开发中的关键考虑因素&#xff0c;希望对大家有所帮助。北京木奇移动技术有…

万兆电口模块10GBase-T:提升网络性能的利器

随着数字化时代的到来&#xff0c;数据传输速度已经成为各行各业不可或缺的一项需求。而在数据中心和企业网络中&#xff0c;网络设备也正面临着越来越高的带宽需求。在满足这一需求的过程中&#xff0c;万兆电口模块10GBase-T成为了一种重要的解决方案。本文将围绕万兆电口模块…

ElementUI组件:Link 文字链接

ElementUI安装与使用指南 Link 文字链接 点击下载learnelementuispringboot项目源码 效果图 el-link.vue页面效果图 项目里el-link.vue文件代码 <script> export default {name: el_link }</script> <!--https://element.eleme.cn/#/zh-CN/component/link …

数据结构—栈实现前缀表达式的计算

前缀表达式计算 过程分析 中缀表达式&#xff1a;&#xff08;1 5&#xff09;*3 > 前缀表达式&#xff1a;*153 &#xff08;可参考这篇文章&#xff1a;中缀转前缀&#xff09; 第一步&#xff1a;从右至左扫描前缀表达式&#xff08;已存放在字符数组中&#xff09;&a…

百川终入海 ,一站式海量数据迁移工具 X2Doris 正式发布

在大数据分析领域&#xff0c;Apache Doris 作为广受认可的开源实时数据仓库&#xff0c;已经在越来越多行业用户的真实业务场景中得到广泛应用&#xff0c;成为许多企业数据分析基础设施的重要基座。尤其在过去一年多的时间里&#xff0c;越来越多企业选择基于 Apache Doris 进…

LeetCode.2808. 使循环数组所有元素相等的最少秒数

题目 题目链接 分析 我们最终形成的数组一定是当前数组nums 中的一个数字。 所以我们的想法就是枚举数组 nums 中的所有数字&#xff0c;取最小值。 题目告诉我们每一秒都可以向左右扩散一位&#xff0c;那么多个相同的 x 同时扩散&#xff0c;扩散完整个数组耗时就取决于两…

医院如何筛选安全合规的内外网文件交换系统?

医院内外网文件交换系统是专为医疗机构设计的&#xff0c;用于在内部网络&#xff08;内网&#xff09;和外部网络&#xff08;外网&#xff09;之间安全、高效地传输敏感医疗数据和文件的解决方案。这种系统对于保护患者隐私、遵守医疗数据保护法规以及确保医疗服务的连续性和…

NFTScan 与 Merlin Protocol 共同推出 BRC20 Indexer Oracle,于今日正式上线!

近日&#xff0c;NFT 数据基础设施 NFTScan 与 Merlin Protocol 进行战略合作&#xff0c;联合推出了比特币网络原生资产 Indexer Oracle 服务&#xff0c;现在该服务已在 NFTScan 开发者平台上线&#xff0c;任何开发者都可以注册使用&#xff01; Merlin Protocol 是一个专用…

python_蓝桥杯刷题记录_笔记_入门2

前言 现在正式进入蓝桥杯的刷题啦&#xff0c;用python来做算法题&#xff0c;因为我之前其实都是用C来做题的&#xff0c;但是今年的话我打算换python来试试&#xff0c;很明显因为也才这学期接触python 加上之前C做题也比较菜&#xff0c;所以我打算用python重新来做题&#…

Node.js Express 框架 2024版 笔记

1.0 操作命令 Node.js express 框架 https://www.expressjs.com.cn/ npm install -g express-generator expressexpress --pug --git // --pug 添加对 pug 模板引擎的支持 // --git 添加 .gitignore 代码仓库排除 //无法直接安装新版pug模板 npm i npm …

Linux进程间通信(IPC)机制之一:共享内存

&#x1f3ac;慕斯主页&#xff1a;修仙—别有洞天 ♈️今日夜电波&#xff1a;Nonsense—Sabrina Carpenter 0:50━━━━━━️&#x1f49f;──────── 2:43 &#x1f504; ◀️ ⏸ ▶️ …

社区投稿|Desig质押聚合器功能,帮助用户选出更适合的质押策略

在Sui上&#xff0c;不同的质押协议提供收益各异的产品&#xff0c;因此用户面临着众多可以质押token的协议&#xff0c;眼花缭乱无从选择。Desig质押聚合器功能现已整合到Desig钱包扩展中&#xff0c;极大地简化了寻找质押策略的流程。事实上&#xff0c;其智能质押功能支持完…

泰迪智能科技生成式人工智能(AIGC)实验室解决方案

AIGC&#xff08;Artificial Intelligence Generated Content&#xff0c;生成式人工智能&#xff09;是一种新的人工智能技术&#xff0c;指的是利用人工智能技术来生成内容。这种技术可以自动生成文本、图像、音频和视频等多种类型的内容&#xff0c;而且内容的质量较高&…