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

文章目录

    • 深入理解指针
      • 一、冒泡排序
      • 二、二级指针
      • 三、指针数组
        • 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");  // 序号1elseprintf("str1 and str2 are not same\n");  // 序号2if (str3 == str4)printf("str3 and str4 are same\n");  // 序号3elseprintf("str3 and str4 are not same\n");  // 序号4return 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.hqwc.cn/news/520994.html

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

相关文章

117.龙芯2k1000-pmon(16)- linux下升级pmon

pmon的升级总是有些不方便&#xff0c;至少是要借助串口和串口工具 如果现场不方便连接串口&#xff0c;是不是可以使用网线升级pmon呢&#xff1f; 答案当然是可行的。 环境&#xff1a;2k1000linux3.10麒麟的文件系统 如今我已经把这个工具开发出来了。 GitHub - zhaozhi…

安卓 OpenGL ES 学习笔记

文章目录 OpenGL 学习笔记OpenGL 是什么&#xff1f;OpenGL ES是什么&#xff1f;怎么用&#xff1f;hello world如何实现动画效果 参考文章 OpenGL 学习笔记 OpenGL 是什么&#xff1f; OpenGL&#xff08;Open Graphics Library&#xff09;是一个跨平台的图形编程接口&…

AOP理解

AOP就是面向特定的方法进行编程&#xff0c;在不改动原始方法的基础上&#xff0c;可以增强原始方法的功能&#xff0c;或者改变某些功能&#xff0c;我们可以通过AOP记录数据库的操作日志 AOP的底层实现就是动态代理技术&#xff0c;在执行原始方法前&#xff0c;生成一个代理…

南京观海微电子---PCIe协议接口

1. PCIe引脚定义 PCI Express&#xff0c;官方简称PCIe&#xff0c;是计算机总线的一个重要分支, 插槽图片如下&#xff1a; 下表列出在边缘连接器上的PCI Express卡两侧的导线。在印刷电路板&#xff08;PCB&#xff09;的焊接侧为A侧&#xff0c;并且组件侧的B侧。PRSNT1# 和…

Uber/Google Golang编码标准深度分析

良好的代码风格对于开发优秀的产品至关重要&#xff0c;本文通过分析比较三部流传甚广的Golang代码风格指南&#xff0c;介绍了Go代码风格要点&#xff0c;并介绍了通过工具实现代码检查的方式。原文: Mastering Go: In-Depth Analysis of Uber and Google’s Coding Standards…

气相白炭黑外资垄断格局被打破 国内本土企业数量增加

气相白炭黑外资垄断格局被打破 国内本土企业数量增加 气相白炭黑又名气相二氧化硅&#xff0c;是一种无毒、无味、无嗅&#xff0c;无污染的非金属氧化物&#xff0c;主要由硅的卤化物在氢氧火焰中高温水解生成的带有表面羟基和吸附水的无定形的纳米级颗粒。气相白炭黑主要用于…

Unity 关节:铰链、弹簧、固定、物理材质:摩檫力、 特效:拖尾、

组件-物理-关节&#xff1a;铰链&#xff08;类似门轴&#xff09; 自动动作、多少力可以将其断开、 弹簧可以连接另一个刚体&#xff08;拖动即可&#xff09; 固定一般是等待一个断裂力&#xff0c;造成四分五裂的效果。 物理材质 设置摩檫力&#xff0c;则可以创造冰面的…

能量不等式证明

波动方程初值问题能量不等式的证明 Gronwall 不等式 若非负函数 G ( τ ) G(\tau) G(τ) 在 [ 0 , T ] [0,T] [0,T] 上连续可微&#xff0c; G ( 0 ) 0 G(0)0 G(0)0&#xff0c;且对 τ ∈ [ 0 , T ] \tau\in[0,T] τ∈[0,T]满足 d G ( τ ) d τ ≤ C G ( τ ) F ( τ …

27.基于springboot + vue实现的前后端分离-网上租赁交易系统(项目 + 论文)

项目介绍 本课题是根据用户的需要以及网络的优势建立的一个基于Spring Boot的网上租贸系统&#xff0c;来满足用户网络商品租赁的需求。本网上租贸系统应用Java技术&#xff0c;MYSQL数据库存储数据&#xff0c;基于Spring Boot框架开发。在网站的整个开发过程中&#xff0c;首…

XXE-XML实体注入漏洞

目录 1.xml基础 1.1什么是xml 1.2xml文档结构 1.3 什么是DTD 1.4 什么是实体 1.5 什么是外部实体 2.xxe漏洞 2.1xxe漏洞基本介绍 2.2xxe漏洞的危害 经典漏洞案例分析 3.xxe漏洞挖掘和利用 3.1. 识别潜在的XML入口 3.2. 检查XML处理逻辑 3.3. 构造试探Payload 常…

为什么企业CRM系统用不起来?提高使用率的有效策略揭秘

大家好&#xff0c;我是小编卡林&#xff0c;今天为大家带来的是文章是为什么企业CRM系统用不起来&#xff1f;CRM系统使用率低的原因有很多&#xff0c;其中一个共识是假如一款CRM管理系统让员工普遍认为只是领导查询自己工作的系统&#xff0c;那么&#xff0c;这个项目离失败…

LeetCode Python - 40.组合总和②

目录 题目答案运行结果 题目 给定一个候选人编号的集合 candidates 和一个目标数 target &#xff0c;找出 candidates 中所有可以使数字和为 target 的组合。 candidates 中的每个数字在每个组合中只能使用 一次 。 注意&#xff1a;解集不能包含重复的组合。 示例 1: 输入…