C语言—指针(2)

news2025/1/12 18:12:13

目录

一、数组名的理解

二、使用指针访问数组

三、一维数组传参的本质

四、冒泡排序

五、二级指针

六、指针数组

七、字符指针变量

八、数组指针变量

(8.1)什么是数组指针变量

 (8.2)数组指针变量的初始化

九、二维数组传参的本质

十、函数指针变量

(10.1)函数指针变量的创建

(10.2)函数指针变量类型的使用

(10.3)typedef关键字

十一、函数指针数组


一、数组名的理解

通常情况下,我们使⽤ &arr[0] (随便写的一个数组名)的⽅式拿到数组第⼀个元素的地址,但是其实数组名本来就是地址,⽽且是数组⾸元素的地址。测试:

我们发现数组名和数组⾸元素的地址打印出的结果⼀模⼀样,数组名就是数组⾸元素(第⼀个元素)的地址。但是,有两种特殊情况,数组名不表示数组首元素的地址。

1、 sizeof(数组名),sizeof中单独放数组名,这⾥的数组名表⽰整个数组,计算的是整个数组的⼤⼩,单位是字节。
2、 &数组名,这⾥的数组名表⽰整个数组,取出的是整个数组的地址(整个数组的地址和数组⾸元素的地址是有区别的)。

除此之外,任何地⽅使⽤数组名,数组名都表⽰⾸元素的地址。测试:

这里sizeof(arr)确实是整个数组的大小,但是 &arr 和数组首元素地址相同,这是为什么呢?其实,&arr 取出的的确是整个数组的地址,不过因为数组在内存中是连续存放的,只要拿到首元素地址就能找到其他元素,所以整个数组的地址就是数组首元素的地址。但是它们也是有区别的。

打印结果:(这里是十六进制,计算相差字节数需要转换为十进制)

这⾥我们发现&arr[0]和&arr[0]+1相差4个字节,arr和arr+1相差4个字节,这是因为&arr[0] 和 arr 都是⾸元素的地址,+1就是跳过⼀个元素。但是&arr 和 &arr+1相差40个字节,这就是因为&arr是数组的地址,+1 操作是跳过整个数组。

二、使用指针访问数组

使用指针访问数组有两种基本方式,第一种:

打印效果:

上述代码中,先使用指针记住数组首元素地址,在通过指针+i 使指针指向不同的数组元素,进而实现赋值和打印。

第二种:

打印效果:

这里是因为,数组名arr是数组⾸元素的地址,指针p也是数组首元素的地址,所以它们是等价的,那我们可以使⽤arr[i]访问数组的元素,就可以使用p[i]访问数组的元素。同时,这里将*(p+i)换成p[i]也是能够正常打印的,所以本质上p[i] 还等价于 *(p+i)。同理arr[i] 应该等价于 *(arr+i),数组元素的访问在编译器处理的时候,也是转换成⾸元素的地址+偏移量求出元素的地址,然后解引⽤来访问的。

三、一维数组传参的本质

我们知道 ,数组是可以传递给函数的,我们之前都是在函数外部计算数组的元素个数,那我们可以把数组传给⼀个函数后,在函数内部求数组的元素个数吗?

我们发现在函数内部是没有正确获得数组的元素个数的。

其实,数组名是数组⾸元素的地址;那么在数组传参的时候,传递的是数组名,也就是说数组传参本质上传递的是数组⾸元素的地址。所以函数形参的部分理论上应该使⽤指针变量来接收⾸元素的地址。那么在函数内部我们写sizeof(arr) 计算的是⼀个地址的⼤⼩(单位字节)⽽不是数组的⼤⼩(单位字节)。正是因为函数的参数部分是本质是指针,所以在函数内部是没办法求的数组元素个数的。

打印结果:

总结:⼀维数组传参,形参的部分可以写成数组的形式,也可以写成指针的形式。

四、冒泡排序

上述代码演示的是升序排列,冒泡排序的核⼼思想就是:两两相邻的元素进⾏⽐较。如图:

从图中可以看出,第一次内层循环遍历结束后,数组中最大的数被放置到数组末尾,所以第二次进入内层循环时,最后一个元素没有必要参与比较,第二次内层循环结束后,次大的数又被排好,所以每次都会有一个参与比较的数据中的最大值被排好,所以每次内层循环都可以少排一个数据,所以内层循环的条件是sz-i-1,第一次 i 等于0,sz个数据需要比较sz - 1次,之后每次 i +1,内层循环次数每次就减少一次。又因为每次都是排好一个数据,一共sz个数据,当sz - 1个都排好后,最后一个就不需要比较了,所以外层循环需要sz - 1次。

这个代码还可以改进,图中演示的是最坏的情况,不一定所有情况都要排sz - 1次。如图:

这种情况一次就排好了。所以代码可以改进为:

五、二级指针

指针变量也是变量,是变量就有地址,那指针变量的地址存放在哪⾥呢?存放指针变量的就叫⼆级指针 。

图中的ppa就是二级指针,int * 代表它指向的对象类型是整型指针,后面的第二个 * 代表它自己是指针。对于二级指针而言,*ppa 通过对ppa中的地址进⾏解引⽤,这样找到的是 pa , *ppa 其实访问的就是 pa ,**ppa 先通过 *ppa 找到 pa ,然后对 pa 进⾏解引⽤操作: *pa ,那找到的就是 a 

六、指针数组

首先我们要知道,指针数组是指针还是数组?我们类⽐⼀下,整型数组,是存放整型的数组,字符数组是存放字符的数组。所以指针数组,是存放指针的数组。指针数组的每个元素都是⽤来存放地址(指针)的。如图:

指针数组的每个元素是地址,⼜可以指向⼀块区域。例如:

打印效果:

原理图:

parr[i]是访问parr数组的元素,parr[i]找到的数组元素指向了整型⼀维数组,parr[i][j]就是整型⼀维数组中的元素。

七、字符指针变量

一般情况下,我们可以这样使用字符指针:

但是还有一种使用方式:

这种方式的本质是把字符串 hello bit. 的⾸字符的地址放到了pstr中。

我们可以看这样一道题:

结果:

解释:上述代码中,str1和str2是两个数组,它们会分别在内存中开辟自己的空间,存放相同的字符串,前面说过,数组名是数组首元素的地址,而str1和str2都有各自的空间,只是空间里存的内容一样而已,地址并不相同,所以第一个打印出来的是 not same,而str3和str4指向的是同⼀个常量字符串。C/C++会把常量字符串存储到单独的⼀个内存区域,当⼏个指针指向同⼀个字符串的时候,他们实际会指向同⼀块内存。但是⽤相同的常量字符串去初始化不同的数组的时候就会开辟出不同的内存块。所以str3和str4打印出的是 same。

八、数组指针变量

(8.1)什么是数组指针变量

类比以前学过的内容:整形指针变量: int * pint; 存放的是整形变量的地址,能够指向整形数据的指针;浮点型指针变量: float * pf; 存放浮点型变量的地址,能够指向浮点型数据的指针。那数组指针变量应该是:存放的应该是数组的地址,能够指向数组的指针变量。

数组指针和指针数组的基本格式很像,我们要区分开,首先我们需要知道 [ ] 的优先级要⾼于 * 号的,所以数组指针要加上(),例如上图:p2先和*结合,表明p2是⼀个指针变量,然后指向的是⼀个⼤⼩为10个整型的数组。所以p2是⼀个指针,指向⼀个数组,叫数组指针。p1没有(),先和 [ ] 结合,成为一个数组,再和 * 结合,表明数组中存放的是整型指针,所以p1是指针数组。

 (8.2)数组指针变量的初始化

数组指针变量是⽤来存放数组地址的,要想获得数组的地址,就要用到前面说的:&数组名

例如:

通过调试我们可以看到 &arr 和 p 的类型是完全⼀致的。

数组指针类型解析:

九、二维数组传参的本质

一般情况下, 二维数组传参,形参也写成⼆维数组的形式。

二维数组传参还有另一种写法,不过我们要再次理解⼀下⼆维数组,⼆维数组起始可以看做是每个元素是⼀维数组的数组,也就是⼆维数组的每个元素是⼀个⼀维数组。那么⼆维数组的⾸元素就是第⼀⾏,是个⼀维数组。如图:

所以,根据数组名是数组⾸元素的地址这个规则,⼆维数组的数组名表⽰的就是第⼀⾏的地址,是⼀维数组的地址。根据上⾯的例⼦,第⼀⾏的⼀维数组的类型就是 int [3] ,所以第⼀⾏的地址的类型就是数组指针类型 int(*)[3] 。那就意味着⼆维数组传参本质上也是传递了地址,传递的是第⼀⾏这个⼀维数组的地址,那么形参也是可以写成指针形式的。如下:

p 是数组指针,p + i 每次会跳过二维数组的一行,*(p+i)是得到当前数组指针指向的一维数组,*(p+i)+j是让指针移动到当前数组指针指向的这一行的一维数组的第 j 个元素,再解引用就能拿到这个元素了。

总结:⼆维数组传参,形参的部分可以写成数组,也可以写成指针形式。

十、函数指针变量

(10.1)函数指针变量的创建

函数指针变量是⽤来存放函数地址的,未来通过地址能够调⽤函数的。如何取出函数地址呢?如图:

从上述代码中可以看出:函数名就是函数的地址,当然也可以通过 &函数名 的⽅式获得函数的地址。如果我们要将函数的地址存放起来,就得创建函数指针变量,函数指针变量的写法其实和数组指针⾮常类似。如下:

函数指针类型解析:

(10.2)函数指针变量类型的使用

可以通过函数指针调⽤指针指向的函数。例如:

这里可以通过解引用来找到函数指针指向的函数并进行调用,也可以直接用指针进行调用,因为函数名就是函数的地址,函数指针也是函数的地址,所以函数指针等价于函数名,可以直接用函数指针对函数进行调用。

(10.3)typedef关键字

typedef 是⽤来类型重命名的,可以将复杂的类型简单化。⽐如,你觉得 unsigned int 写起来不⽅便,如果能写成 uint 就⽅便多了,那么我们可以使⽤:

指针类型也是可以重命名的,⽐如,将 int* 重命名为 ptr_t ,这样写:

但是对于数组指针和函数指针稍微有点区别:
⽐如我们有数组指针类型 int(*)[5] ,需要重命名为 parr_t ,那可以这样写:

函数指针类型的重命名也是⼀样的,⽐如,将 void(*)(int) 类型重命名为 pfun_t ,就可以这样写:

十一、函数指针数组

基本格式:

解释:parr1 先和 [ ] 结合,说明 parr1是数组,数组的内容是int (*)() 类型的函数指针。

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

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

相关文章

Linux入门——08 进程间通讯——管道

1.进程间通讯 1.1什么是通讯 进程具有独立性(每个进程都有自己的PCB,独立地址空间,页表)但是要进行进程的通信,通信的成本一定不低,打破了独立性 进程间通信目的 数据传输:一个进程需要将它的数据发送给…

探索风扇产品模型的3D可视化魅力

在这个科技日新月异的时代,每一个细微的创新都能为我们的生活带来前所未有的便捷与享受。今天,就让我们一起踏入一场视觉与科技的盛宴,探索风扇产品模型如何通过3D可视化技术,重新定义家居生活的舒适与美学。 想象一下&#xff0c…

Redis—缓存机制

Redis 缓存机制 1. 缓存三兄弟1.1 缓存击穿1.2 缓存穿透1.3 缓存雪崩 2. 布隆过滤器3. 缓存和数据库数据一致性3.1 缓存更新策略3.2 缓存不一致处理 4. 热点 key4.1 热点 key 处理4.2 热点 key 重建 5. 缓存预热 Redis,一个轻量级的开源内存数据结构存储系统&#x…

Java:循环练习

目录 1. 回文判断 2. 减法求商余 3. 求平方根 4.求质数 5. 猜数字 1. 回文判断 输入一个数字,判断是否为回文,回文就是正着读和反着读都一样,如121是回文,123则不是。 import java.util.Scanner;public class DemoNew {publ…

白卡无法注册TDSCDMA问题分析

1、问题描述 MTK平台 实验室白卡测试TDSCDMA,默认无法注册。使用移动卡测试,无此问题。 2、问题分析 查看Radio log,Radio中反复下发EFUN去开关飞行模式。 39191: 08-14 22:45:57.159210 1469 1512 D RmcWp : [0] ECSRA info E…

CART决策树-基尼指数(全网最详解)

文章目录 一、基尼指数的定义二、基尼指数在CART决策树中的应用三、基尼指数与CART决策树的构建1.计算每个子集的基尼系数:2.计算基尼指数3.选择最优特征4.其余基尼指数5.构建决策树 四、总结 CART决策树基尼指数是CART(Classification And Regression T…

稳石机器人 | 工业级AMR S1200L,专为多样化需求设计,柔性拓展更易用

近日,稳石机器人重磅推出基于新品控制器ROC1000的全新移动机器人AMR S1200L,专为满足生产制造和仓储物流的多样化需求而设计,无需改造现场,最快可在1周内完成部署。 重载型AMR-S1200L设计注重实用性和灵活性,可在室内…

Excel 中找出每列第一个和最后一个非空格对应的行--Excel难题#87

Excel表格的第2-6列有空格。 ABCDEF1StartDateQID1QID2QID3QID4QID5210/03/2024 10:561yes32310/03/2024 03:102no423409/03/2024 19:253yes22509/03/2024 11:404no1yes609/03/2024 03:555yes5no708/03/2024 20:106808/03/2024 12:257no908/03/2024 04:408yes1007/03/2024 20:…

Linux云计算 |【第二阶段】SECURITY-DAY4

主要内容: Kali系统、扫描与抓包、Nginx安全加固、Linux基本防护 补充:使用Curl命令查看网页头部信息和页面内容 不加选项,默认查看网页的内容; [ -I ] 选项:访问服务器页面时,显示HTTP的头部信息&#xf…

用阿里云“无影”搭建《黑神话:悟空》电脑环境

目录 《黑神话:悟空》 阿里云无影试用版概述 阿里云无影云电脑试用版情况 具体详细过程(搭建环境) 《黑神话:悟空》 《黑神话:悟空》作为一款高品质的国产游戏,对硬件配置有一定的要求。根据公开发布的…

【鸿蒙学习】HarmonyOS应用开发者高级认证 - 应用性能优化一(界面层面)

学完时间:2024年8月22日 学完排名:第1801名 一、介绍 在开发HarmonyOS应用时,优化应用性能是至关重要的。通过/ArkTS高性能编程、减少丢帧卡顿、提升应用启动和响应速度 可以有效提升用户体验。本文将介绍一些优化HarmonyOS应用性能的方法。 一、Ark…

Go开发桌面客户端软件小试:网站Sitemap生成

在前一篇【手把手教你用Go开发客户端软件(使用Go HTML)】中,我们详细介绍了如何通过Go语言开发一个简单的桌面客户端软件。本次,我们将继续这个系列,使用Go语言结合Sciter的Go绑定库——go-sciter,实战开发…

14.C基础_结构体

定义与使用 1、定义 定义结构体: 定义结构体时,需要注意最后的分号必须加上。 定义结构体时,成员只去声明类型,不进行赋值。赋值在定义结构体变量时进行。 struct 结构体名{结构体成员列表 }; //注意这里的分…

Qt入门学什么?

Qt是一个跨平台的C图形用户界面应用程序框架,它为应用程序开发者提供建立图形界面所需的所有功能。Qt框架以其面向对象、易于扩展的特性而受到广泛欢迎,并且支持多种平台,包括桌面、嵌入式和移动平台 。 对于Qt的入门学习,可以通过…

uniapp+vue3的defineProps传递

//index.vue <view class"topic"><!-- 磨砂背景 --><view class"content"><matte v-for"(item,index) in 8" :key"index"></matte><matte isMore"false"></matte></view>&…

0成本学习Liunx系统【只需要一台笔记本电脑,无需购买云服务器】

【准备工作&#xff0c;需要软件】&#xff1a; 1&#xff1a;MobaXterm 【服务器连接工具&#xff08;免费开源&#xff09;】 2&#xff1a;CentOS-7-x86_64-DVD-2009.iso 【CentOS-7 镜像】 3&#xff1a;VirtualBox-7.0.20-163906-Win.exe 【虚拟机壳子】 4&…

朴素贝叶斯与决策树分类

朴素贝叶斯分类 1贝叶斯分类理论 选择高概率对应的类别 2条件概率 事件B发生的情况下&#xff0c;事件A发生的概率 &#x1d443;(&#x1d434;|&#x1d435;)&#x1d443;(&#x1d434;∩&#x1d435;)/&#x1d443;(&#x1d435;) > &#x1d443;(&#x1d43…

【前端面试】浏览器原理解读

前端进阶——浏览器篇-CSDN博客 浏览器工作原理与Javascript高级&#xff08;前后端异步&#xff09;-CSDN博客 DOM树的建立过程 前端DOM&#xff08;文档对象模型&#xff09;数的建立过程&#xff0c;实际上是浏览器解析HTML文档并构建DOM树的过程。这一过程大致可以分为以…

声音克隆GPT-SoVITS 2.0软件和详细的使用教程!

天命人&#xff0c;请允许我先蹭个热点&#xff01; 原始声音&#xff1a; 播放 克隆声音&#xff1a; 播放 文章写了一半&#xff0c;被《黑神话悟空》刷屏了。突发奇想&#xff0c;用里面的声音来做个素材试试看。 B站捞了一点声音素材&#xff0c;随便剪一剪&#xff0c…

IOS半越狱工具nathanlr越狱教程

简介 nathanlr 是一款半越狱工具&#xff0c;不是完整越狱。 半越狱只能使用一些系统范围的插件。 无法做到完整越狱 Dopamine 越狱一样插件兼容性。 nathanlr支持 iOS 16.5.1 – 16.6.1 系统。 支持 A12 及以上设备。 肯定有人问&#xff0c;为什么仅仅支持这些系统&#xff…