【C语言基础】:深入理解指针(三)

news2024/11/17 12:35:24

文章目录

    • 深入理解指针
      • 一、冒泡排序
      • 二、二级指针
      • 三、指针数组
        • 3.1 指针数组模拟二维数组
      • 四、字符指针变量
      • 五、数组指针变量
        • 5.1 数组指针变量是什么?
        • 5.2 数组指针变量的初始化
      • 六、二维数组传参的本质

深入理解指针

指针系列回顾
【C语言基础】:深入理解指针(一)
【C语言基础】:深入理解指针(二)

一、冒泡排序

冒泡排序的核心思想就是:两两相邻的元素进行比较。

#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 = 0;
				tmp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = tmp;
			}
		}
	}
}

int main()
{
	int arr[] = { 10,9,8,7,6,5,4,3,2,1 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	bubble_sort(arr, sz);
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

在这里插入图片描述
可以看到,这段代码对arr数组进行了排序,但这个代码还有一些缺陷,那就是无论数组内部的元素是否有序,他都会循环9次,这样肯定是不合理的,要进行优化一下。

我们在bubble_sort函数的第一层循环里面定义一个变量flag,进入第二层循环就修改flag的值,第二层循环结束时给变量flag来个判断,如果变量flag没有发生改变,说明没有进入第二层循环,也就是说这时数组里的元素是有序的,就会直接跳出第一层循环。另外,我们还可以在最外面定义一个全局变量用来计数,每进行一次元素交换就会自增1。

#include<stdio.h>
int count = 0;
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++)
		{
			count++;
			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[] = { 0,9,8,6,5,3,1,2,4,7 };
	int sz = sizeof(arr) / sizeof(arr[0]);  // 求出数组中的元素个数
	bubble_sort(arr, sz);
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
	printf("%d\n", count);
	return 0;
}

在这里插入图片描述
运行之后可以发现,代码的运行效率明显提升了,理论来说上述数组元素在未优化的代码下运行是要进行45次交换的,现在明显次数减少了。

二、二级指针

通过前面的知识我们知道,指针变量也是一个变量,只要是一个变量就会有地址,那么,指针变量的地址放在哪里呢?
我们把存放地址的指针称为二级指针,也就是指针的指针

#include<stdio.h>
int main()
{
	int a = 10;
	int* pa = &a;
	int** ppa = &pa;
	return 0;
}

而三级指针就是存放二级指针变量的地址,四级指针、五级指针以此类推…

对于二级指针的运算有:

  • *ppa通过对ppa进行解引用,就可以找到pa,*ppa其实访问的就是pa
int b = 20;
*ppa = &b;  // 等价于pa = &b
  • **pa先通过*ppa找到pa,然后对pa进行解引用操作:*pa,找到的就是a。
**ppa = 30;
//等价于*pa = 30;
//等价于a = 30;

三、指针数组

首先我们要弄懂一个问题:指针数组到底是一个指针还是一个数组?
这里我们可以类比一下:

整数数组:就是一个存放整型的数组
字符数组:就是一个存放字符的数组

那指针数组呢?没错,就是存放指针的数组
在这里插入图片描述
指针数组里的每个元素都是用来存放地址(指针)的。

如下图:
在这里插入图片描述
通过这张图可以看到,指针数组里的每一个元素都是一个指针,而这里面的每一个指针又可以指向一块区域。

3.1 指针数组模拟二维数组
#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[] = { 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;
}

在这里插入图片描述
在这里插入图片描述
parr[i]是访问parr数组的元素,parr[i]找到的数组元素指向了整型⼀维数组,parr[i][j]就是整型⼀维数组中的元素。
注意:这只是利用指针数组模拟的二维数组,并非是真的二维数组,因为每一行的地址并非连续的

四、字符指针变量

在指针的类型中我们知道有一种指针类型为字辅指针char*
一般使用:

#include<stdio.h>
int main()
{
	char ch = 'w';
	char* pc = &ch;
	*pc = 'w';
	return 0;
}

还有一种使用方式如下:

#include<stdio.h>
int main()
{
	const char* pc = "hello world";
	printf("%s\n", pc);//这里是把一个字符串放到pc指针变量里了吗?
	return 0;
}

在这里插入图片描述
注意:这里const char* pc = “hello world”; 特别容易让人认为是把字符串hello world放到字符指针pc里了,但其实本质上是字符串hello world 首字符的地址放到了pc中。
在这里插入图片描述
上面的代码的意思是把一个常量字符串的首字符h的地址存放到指针变量pc中。

下面是一道和字符串相关的题,我们可以学一下:

#include<stdio.h>
int main()
{
	char str1[] = "hello world";
	char str2[] = "hello world";
	const char* str3 = "hello world";
	const char* str4 = "hello world";
	if (str1 == str2)
		printf("str1 and str2 are same\n");  // 序号1
	else
		printf("str1 and str2 are not same\n");  // 序号2
	if (str3 == str4)
		printf("str3 and str4 are same\n");  // 序号3
	else
		printf("str3 and str4 are not same\n");  // 序号4
	return 0;
}

在这里插入图片描述
运行之后我们会发现,打印的是序号2和序号3的语句,这是为什么呢?
这里的str3和str4指向的是同一个常量字符串。C/C++会把常量字符串存储到一个单独的内存区域,当几个指针指向同一个字符串的时候,它们实际会指向同一块内存。但是用相同的常量字符串去初始化不同的数组的时候就会开辟出不同的内存块。所以str1和str2不同,str3和str4相同。

五、数组指针变量

5.1 数组指针变量是什么?

上面在学指针数组的时候我们知道,指针数组是数组元素为指针的数组。那么数组指针是什么呢?
上面学习指针数组的时候类比了一下,那么这里不妨也来类比一下:

  • 整型指针变量int * pint; 存放的是整型变量的地址,能够指向整形数据的指针。
  • 浮点型指针变量float * pf; 存放浮点型变量的地址,能够指向浮点型数据的指针。

那么数组指针就是存放数组的地址,能够指向数组数据的指针变量

我们判断一下下面那个是数组指针

int *p1[10];
int (*p2)[10];

int * p1[10]; 是一个指针数组,是 10 个整型指针的数组。
int ( * p2)[10]; 是数组指针,表示一个指向包含 10 个整型数据的数组的指针。因为 p2 是一个指针,所以 p2 是一个指针类型。

数组指针变量

int (*p2)[10];

这里p会先和 * 号结合,说明p是一个指针变量,然后指向的是一个大小为10个整型数据大小的数组,所以p是一个指针,指向一个数组,叫数组指针
注意:[]的优先级是高于 * 号的,所以必须要加上()来保证 p会先和 * 结合。

5.2 数组指针变量的初始化

数组指针变量是用来存放数组地址的,那怎么获得数组的地址呢?其实就是我们之前学的 &数组名

int main()
{
	int arr[10] = { 0 };
	&arr;  //得到的就是数组的地址
	return 0;
}

如果要存放个数组的地址,就得存放在数组指针变量中,如下:

int main()
{
	int arr[10] = { 0 };
	int(*pa)[10] = &arr;
	return 0;
}

在这里插入图片描述
在调试的过程中我们也可以看到 &arrpa的类型是完全一致的。

数组指针类型解析:

int (*p) [10] = &arr;
 |    |    |
 |    |    |
 |    |    | p指向数组的元素个数
 |    p是数组指针变量名
 p指向的数组的元素类型

六、二维数组传参的本质

有了数组指针的理解,我们就能够讲⼀下二维数组传参的本质了。
过去我们有一个二维数组的需要传参给⼀个函数的时候,我们是这样写的:

#include<stdio.h>

void test(int a[3][5], int r, int c)
{
	int i = 0;
	int j = 0;
	for (i = 0; i < r; i++)
	{
		for (j = 0; j < c; j++)
		{
			printf("%d ", a[i][j]);
		}
		printf("\n");
	}
}
int main()
{
	int arr[3][5] = { {1,2,3,4,5},{2,3,4,5,6},{3,4,5,6,7} };
	test(arr, 3, 5);
	return 0;
}

在这里插入图片描述
这里实参是二维数组,形参也写成⼆维数组的形式,那还有什么其他的写法吗?
首先我们再次理解⼀下二维数组,二维数组其实可以看做是每个元素是⼀维数组的数组,也就是⼆维数组的每个元素是⼀个⼀维数组。那么⼆维数组的首元素就是第一行,是个⼀维数组。
如下图:
在这里插入图片描述
所以,根据数组名是数组首元素的地址这个规则,⼆维数组的数组名表示的就是第一行的地址,是⼀维数组的地址。根据上面的例子,第一行的⼀维数组的类型就是 int [5] ,所以第一行的地址的类型就是数组指针类型 int(*)[5] 。那就意味着⼆维数组传参本质上也是传递了地址,传递的是第一行这个⼀维数组的地址,那么形参也是可以写成指针形式的。如下:

#include<stdio.h>

void test(int(*p)[5], int r, int c)
{
	int i = 0;
	int j = 0;
	for (i = 0; i < r; i++)
	{
		for (j = 0; j < c; j++)
		{
			printf("%d ", *((*p + i) + j));
		}
		printf("\n");
	}
}

int main()
{
	int arr[3][5] = { {1,2,3,4,5},{2,3,4,5,6},{3,4,5,6,7} };
	test(arr, 3, 5);
	return 0;
}

在这里插入图片描述
总结:二维数组传参,形参的部分可以写成数组,也可以写成指针形式。

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

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

相关文章

[uni-app ] createAnimation锚点旋转 及 二次失效问题处理

记录一下: 锚点定位到左下角, 旋转动画 必须沿Z轴,转动 但是,此时会出现 后续动画在微信小程序失效问题 解决: 清空 this.animationData

关于 OpenAI Sora的一些探索和思考

关于 OpenAI Sora的一些探索和思考 探索 OpenAI 的 Sora&#xff1a;一种突破性的文本到视频 AI&#xff0c;将在 2024 年彻底改变多模态 AI。探索其功能、创新和潜在影响。 OpenAI 最近宣布了其最新的突破性技术——Sora。到目前为止&#xff0c;这种文本到视频的生成式 AI …

基于ACM32 MCU的电动滑板车方案了,助力低碳出行

随着智能科技的快速发展&#xff0c;电动滑板车的驱动系统也得到了长足的发展。国内外的电动滑板车用电机驱动系统分为传统刷式电机和无刷电机两种类型。其中&#xff0c;传统的刷式电机已经逐渐被无刷电机所取代&#xff0c;无刷电机的性能和寿命都更出色&#xff0c;已成为电…

JavaWeb实验 JSP 基本语法(续

实验目的 掌握JSP基本语法&#xff1b;掌握JSP常见用法。 实验内容 【1】创建index.jsp、first.jsp和second.jsp三个jsp文件&#xff0c;页面的内容分别显示“This is my JSP page of index.jsp.”、“This is my JSP page of first.jsp.”和“This is my JSP page of secon…

SpringMVC | SpringMVC的“入门“

目录: Spring MVC入门 :Spring MVC 概述第一个Spring MVC应用SpringMVC 的 “工作流程” Spring MVC入门 : 作者简介 &#xff1a;一只大皮卡丘&#xff0c;计算机专业学生&#xff0c;正在努力学习、努力敲代码中! 让我们一起继续努力学习&#xff01; 该文章参考学习教材为&a…

docker 部署prometheus+grafana

首先进行部署docker 配置阿里云依赖&#xff1a; curl -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-7.repo # 配置centos 7的镜像源 yum install -y yum-utils device-mapper-persistent-data lvm2 # 安装一些后期或需要的的一下依…

MVO-CNN-BiLSTM多输入分类预测|多元宇宙优化算法-卷积-双向长短期神经网络分类预测(Matlab)

目录 一、程序及算法内容介绍&#xff1a; 基本内容&#xff1a; 亮点与优势&#xff1a; 二、实际运行效果&#xff1a; 三、算法介绍&#xff1a; 四、完整程序下载&#xff1a; 一、程序及算法内容介绍&#xff1a; 基本内容&#xff1a; 本代码基于Matlab平台编译&am…

Android14音频进阶:AudioTrack如何拿到AudioFlinger创建的匿名共享内存(六十)

简介: CSDN博客专家,专注Android/Linux系统,分享多mic语音方案、音视频、编解码等技术,与大家一起成长! 优质专栏:Audio工程师进阶系列【原创干货持续更新中……】🚀 优质专栏:多媒体系统工程师系列【原创干货持续更新中……】🚀 人生格言: 人生从来没有捷径,只…

微软免费AI基础中文课程;马斯克提出撤诉OpenAI条件:“改名ClosedAI”

&#x1f989; AI新闻 &#x1f680; 马斯克提出撤诉OpenAI条件&#xff1a;“改名ClosedAI” 摘要&#xff1a;埃隆・马斯克针对OpenAI及其CEO萨姆・阿尔特曼提起诉讼&#xff0c;主因双方在人工智能开发的合作协议上出现分歧。马斯克称&#xff0c;OpenAI的转型为营利组织与…

C语言-----qsort函数的功能以及模拟实现

1.冒泡排序 &#xff08;1&#xff09;冒泡排序就是数据两个两个的进行比较每一趟都是一个数和其他的所有的数字比较&#xff0c; &#xff08;2&#xff09;这个要的是升序排列&#xff0c;所以前面的大的话&#xff0c;就会调换位置 &#xff08;3&#xff09;冒泡排序的缺…

Git 开源的版本控制系统-04-branch manage 分支管理

拓展阅读 Subversion 开源的版本控制系统入门介绍 VCS Git 开源的版本控制系统-01-入门使用介绍 Git 开源的版本控制系统-02-base usage 基本用法 Git 开源的版本控制系统-03-时间数据回溯 Git 开源的版本控制系统-04-branch manage 分支管理 Git 开源的版本控制系统-05-…

蓝桥-求和

目录 法一&#xff1a;暴力 法二&#xff1a;利用前缀和 法一&#xff1a;暴力 通过率60% #include <bits/stdc.h> using namespace std; long long a[200010]; long long s;int main() {int n;cin>>n;for(int i0;i<n;i){cin>>a[i];}for(int i0;i<…

MS9708/MS9710/MS9714

产品简述 MS9708/MS9710/MS9714 是一个 8-Bit/10-Bit/14-Bit 高速、低功耗 D/A 转换器。当采样速率达到 125MSPS 时&#xff0c; MS9708/MS9710/MS9714 也能提供优越的 AC 和 DC 性能。 MS9708/MS9710/MS9714 的正常工作电压范围为 2.7V 到 5.5V &#xff0c;…

GPT-SoVITS项目的API改良与使用

GPT-SoVITS是一个非常棒的少样本中文声音克隆项目&#xff0c;之前有一篇文章详细介绍过如何部署和训练自己的模型&#xff0c;并使用该模型在web界面中合成声音&#xff0c;可惜它自带的 api 在调用方面支持比较差&#xff0c;比如不能中英混合、无法按标点切分句子等&#xf…

perf的安装与迁移

前言 perf是性能优化很重要的工具之一&#xff0c;本篇博客就来看一下perf的安装以及遇到的问题。 嵌入式驱动学习专栏将详细记录博主学习驱动的详细过程&#xff0c;未来预计四个月将高强度更新本专栏&#xff0c;喜欢的可以关注本博主并订阅本专栏&#xff0c;一起讨论一起学…

ElasticSearch之文档的存储

写在前面 本文看下文档的存储相关内容。 1&#xff1a;如何确定文档存储在哪个分片&#xff1f; 我们需要确保文档均匀分布在所有的分片中&#xff0c;避免某些部分机器空闲&#xff0c;部分机器繁忙的情况出现&#xff0c;想要实现均匀分布我们可以考虑如下的几种分片路由算…

HUAWEI Pocket 2说的防晒检测,能做到什么程度?

防晒是必须&#xff0c;还是噱头&#xff1f;我们来看一下经典案例&#xff1a; 《新英格兰医学杂志》调查一名69岁的卡车司机发现&#xff0c;他位于车窗侧、遭受更多阳光直晒的左半边脸&#xff0c;无论是皱纹还是皮肤厚度&#xff0c;都要比远离阳光的右半边脸要严重得多&am…

如何阅读“计算机界三大神书”之一 ——《计算机程序的构造和解释》SICP

&#x1f468;‍&#x1f393;博主简介 &#x1f3c5;云计算领域优质创作者   &#x1f3c5;华为云开发者社区专家博主   &#x1f3c5;阿里云开发者社区专家博主 &#x1f48a;交流社区&#xff1a;运维交流社区 欢迎大家的加入&#xff01; &#x1f40b; 希望大家多多支…

【设计模式 05】原型模式

有的时候&#xff0c;我们创建对象&#xff0c;需要耗费大量时间在一些资源型操作上&#xff0c;这个时候&#xff0c;我们就可以先创建出一个模板&#xff0c;然后每次创建的时候直接从模板复制即可&#xff0c;不用反复进行耗时的资源型操作。 python代码&#xff1a; impo…

【C++干货基地】六大默认成员函数: This指针 | 构造函数 | 析构函数

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏: 《C干货基地》《粉丝福利》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 引入 哈喽各位铁汁们好啊&#xff0c;我是博主鸽芷咕《C干货基地》是由我的襄阳家乡零食基地有感而发&#xff0c;不知道各位的…