《C语言深度解剖》(19):从头开始全面理解C语言指针和数组

news2024/11/25 2:42:21

🤡博客主页:醉竺

🥰本文专栏:《C语言深度解剖》《精通C指针》

😻欢迎关注:感谢大家的点赞评论+关注,祝您学有所成!


✨✨💜💛想要学习更多C语言深度解剖点击专栏链接查看💛💜✨✨ 


目录

前情提示:

1. 指针是什么 

1.1 指针的内存布局 

1.2 指针解引用 

2. 指针和数组 

2.1 数组的内存布局  

2.2 理解 &a[0] 和 &a 的区别

2.3 指针和数组的恩怨 

2.4 以指针的形式访问和以下标的形式访问 

2.5 a 和&a的区别

3. 指针数组和数组指针

3.1 指针数组和数组指针的内存布局 

3.2 int(*)[10] p2 也许应该这么定义数组指针 

4. 多维数组和多级指针

4.1 二维数组 

4.1.1 基本概念 

4.1.2 基本内存布局 

 4.1.3 空间布局

5. 数组参数和指针参数 

5.1 一维数组传参 

5.2 一级指针传参 

5.3 二维数组参数和二级指针参数 

6. 函数指针 

7. 函数指针数组

8. 函数指针数组指针

9. 强烈推荐两个专栏


前情提示:

在之前的两个专栏中——《C语言深度解剖》icon-default.png?t=N7T8https://blog.csdn.net/weixin_43382136/category_12344236.html
《精通C指针》icon-default.png?t=N7T8https://blog.csdn.net/weixin_43382136/category_12659166.html我已经由浅入深地讲解了指针。无论您是在阅读本文之前还是之后,都可以查阅以下文章以获得更全面的理解:

指针就是这么简单https://blog.csdn.net/weixin_43382136/article/details/137881292?spm=1001.2014.3001.5501icon-default.png?t=N7T8https://blog.csdn.net/weixin_43382136/article/details/137881292?spm=1001.2014.3001.5501

数组指针、指针数组和数组指针数组https://blog.csdn.net/weixin_43382136/article/details/138248557?spm=1001.2014.3001.5501icon-default.png?t=N7T8https://blog.csdn.net/weixin_43382136/article/details/138248557?spm=1001.2014.3001.5501

函数指针、函数指针数组、指向函数指针数组的指针、回调函数https://blog.csdn.net/weixin_43382136/article/details/138322410?spm=1001.2014.3001.5501icon-default.png?t=N7T8https://blog.csdn.net/weixin_43382136/article/details/138322410?spm=1001.2014.3001.5501本文将从宏观角度审视指针的难点易错点,若您已认真学习过上述内容,阅读本文将更为轻松。


1. 指针是什么 

在回答这个问题之前,我想先问几个问题? 

1. 如何看待下面代码中的a变量? 

结论:

同样一个a变量,在不同的应用场景中,a本身的含义是不同的。 

本质区别就是 左值和右值的区别。

变量的空间:左值; 变量的内容:右值;

重新理解变量:

定义一个变量,本质是在内存中根据类型来进行开辟空间。有了空间,就必须具有地址来标识空间,来方便CPU进行寻址。有了空间,就可以把数据保存起来。

2. 什么是指针? 

指针就是地址!那么地址本质是什么呢?地址是数据,那么数据可不可以被保存在变量空间里面呢?当然可以。

3. 有没有指针变量这个概念? 

保存指针(地址)数据的变量就叫做指针变量 

4. 指针 和 指针变量又有何不同?我们口语中的"定义一个指针"究竟是什么意思?我们该如何理解这种说法? 

  • 严格意义上,指针和指针变量是不同的,指针就是地址值,而指针变量是一个变量,要在特定区域开辟空间,要用来保存地址数据,还可以被取地址。(先分开)
  • 但是,我们经常在口语化表达的时候,又经常将这两个概念混合,具体原因无从考证,不过个人认为与最早的C语言资料(书, 文档之类)的翻译有关。然后,书与书之间互相借鉴,形成了这样的说法。
  • 同时,简化说法,也更符合人的表达习惯,估计老外也是这么想的。(在关联)
  • 那么我们以后怎么认为呢?我们分开理解,但是依旧关联使用。自己使用的时候,混合使用可以。和别人讨论,最好明确概念。 

结论:指针就是地址,指针变量是一个变量,变量内部保存指针(地址)数据。 

为什么要有指针?

回答一个问题:为何每间宿舍都要有门牌号呢?
结论:提高查找效率。 

类比到计算机中:

CPU在内存中寻址的基本单位是多大?

在32位机器下,最多能够识别多大的物理内存?

既然CPU寻址按照字节寻址,但是内存又很大,所以,内存可以看做众多字节的集合。

其中,每个内存字节空间,相当于一个学生宿舍,字节空间里面能放8个比特位,就好比同学们住的八人间,每个人是一个比特位。

每间宿舍都有门牌号就等价于每个字节空间对应的地址,即该空间对应的指针。

那么,为何要存在指针呢?为了CPU寻址的效率。如果没有,该怎么找在字节空间中的数据呢?

究竟该如何理解编址? 

1.1 指针的内存布局 

1.2 指针解引用 

*p完整理解是,取出p中的地址,访问该地址指向的内存单元(空间或者内容)(其实通过指针变量访问,本质是一种间接寻址的方式) 

口诀:对指针解引用,就是指针指向的目标。所以*p,就是a


2. 指针和数组 

2.1 数组的内存布局  

概念:数组是具有相同数据类型的集合。 

先看下面的例子:

int a[5];

所有人都明白这里定义了一个数组,其包含了5个int型的数据。我们可以用a[0]、a[1]等来访问数组里面的每一个元素,那么这些元素的名字就是a[0]、a[1]....吗?看看下图内容:

如上图所示,当我们定义一个数组a时,编译器根据指定的元素个数和元素类型分配确定大小(元素类型大小×元素个数)的一块内存,并把这块内存的名字命名为a。

先看下面一段代码:

我们发现,先定义的变量,地址是比较大的,后续依次减小。

这是为什么呢?

a,b,c都在main函数中定义,也就是在栈上开辟的临时变量。而a先定义意味着,a先开辟空间,那么a就先入栈,所以a的地址最高,其他类似。

运行结果:

  

2.2 理解 &a[0] 和 &a 的区别

看下面的例子: 

口诀:对指针+1,本质是加上其所指向类型的大小。 

2.3 指针和数组的恩怨 

        很多初学者弄不清指针和数组到底有什么样的关系,我现在就告诉你:它们之间没有任何关系,只是它们经常穿着相似的衣服来逗你玩罢了。
        指针就是指针,指针变量在 32 位系统下,永远占 4 字节,其值为某一个内存的地址。指针可以指向任何地方,但是不是任何地方你都能通过这个指针变量访问到呢? 

        数组就是数组,其大小与元素的类型和个数有关;定义数组时必须指定其元素的类型和个数;数组可以存任何类型的数据,但不能存函数。
        既然它们之间没有任何关系,那为何很多人经常把数组和指针混淆,甚至很多人认为指针和数组是一样的呢?这就与市面上C语言的书有关了,很少有书把这个问题讲得透彻,讲得明白。

2.4 以指针的形式访问和以下标的形式访问 

可以理解成:

[] 是对 *() 的缩写

结论:指针和数组指向或者表示一块空间的时候,访问方式是可以互通的,具有相似性。但是具有相似性,不代表是一个东西或者具有相关性。

2.5 a 和&a的区别

结论: &a叫做数组的地址,a做右值叫做数组首元素的地址,本质是类型不同,进而进行+-计算步长不同 


3. 指针数组和数组指针

3.1 指针数组和数组指针的内存布局 

        初学者总是分不出指针数组和数组指针的区别,其实这很好理解。
        指针数组首先它是一个数组,数组的元素都是指针,数组占多少字节由数组本身决定。它是“储存指针的数组”的简称
        数组指针:首先它是一个指针,它指向一个数组。在32位系统下永远是占4字节,至于它指向的数组占多少字节并不知道。它是“指向数组的指针”的简称
        下面到底哪个是数组指针,哪个是指针数组呢?

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

这里需要明白一个符号之间的优先级问题。“ [ ] ”的优先级比“ * ”要高,p1 先与“ [ ] "结合,构成一个数组的定义,数组名为 p1,int * 修饰的是数组的内容,即数组的每个元素。那现在我们清楚,这是一个数组,其包含 10 个指向 int 类型数据的指针,即指针数组。至于 p2 就更好理解了,这里“() ” 的优先级比“ [ ] ”高,“ * ”号和 p2 构成一个指针的定义,指针变量名为 p2, int 修饰的是数组的内容,即数组的每个元素。数组在这里并没有名字,是个匿名数组。那现在我们清楚 p2 是一个指针,它指向一个包含10个int类型数据的数组,即数组指针。 

我们看下图进行理解

3.2 int(*)[10] p2 也许应该这么定义数组指针 

很多小伙伴学不会数组指针或者指针数组等比较复杂的指针有时候并不是怪自己!而是C语言在复杂类型的设计上确实不太 “优雅”!

这里有个有意思的话题值得探讨一下:平时我们定义指针都是在数据类型后面加上指针变量名,这个数组指针 p2 的定义怎么不是按照这个语法来定义的呢?也许我们应该这样来定义p2: 

int(*)[10] p2;

int(*)[10] 确实是指针类型,p2是指针变量。其实数组指针的原型确实就是这样子的,只不过C语言的设计风格,把指针变量p2前移了,与“ * ” 号紧挨着在一个小括号里。你私下完全可以这么理解,有助于判断复杂数据类型的学习,只不过编译器语法不通过罢了。 


4. 多维数组和多级指针

4.1 二维数组 

4.1.1 基本概念 

几乎大部分书中所画的二维数组,都是矩阵样子,具体可以参考书中的图,但是,现在我们要在这里澄清,书中的图,最多只能称之为示意图,并非真的内存布局图。
可以想象一些问题:如果按照书中矩阵样子画二维数组的话,那么三维数组,四维数组又该如何画呢?

4.1.2 基本内存布局 

运行结果:
  

结论:二维数组在内存地址空间排布上,也是线性连续且递增的 

 4.1.3 空间布局

以它为例:char a[3][4] = { 0 };


5. 数组参数和指针参数 

5.1 一维数组传参 

说明:如果把数组作为函数的参数,那么编译器会进行优化。 编译器会将数组参数转换成 指针类型的变量,用于接收数组的首元素的地址。 

C语言中,当一维数组作为函数参数的时候,编译器总是把它解析成一个指向其首元素类型的指针。 

所以说一维数组当做函数参数的时候,数组元素的个数可以忽略不写,因为降维成了指向其首元素类型的指针了,已经指向了这块数组空间了,可以进行访问了,传参时的个数已经不重要了。

5.2 一级指针传参 

1. 能否把指针变量本身传递给一个函数

因为指针变量,也是变量,在传参上,它也必须符合变量的要求,进行临时拷贝! 

2.无法把指针变量本身传递给一个函数 

这很像孙悟空拔下一根猴毛变成自己的样子去忽悠小妖怪,与其类似,fun函数实际运行时,用到的都是_p2这个变量而非p2本身。如此,我们看下面的例子: 

5.3 二维数组参数和二级指针参数 

前面详细分析了二维数组和二级指针,那它们作为参数时与不作为参数时又有什么区别呢?看例子:

void fun(char a[3][4]); 

我们按照上面的分析,完全可以把 a[3][4] 理解为一个一维数组 a[3],其每个元素都是一个含有4个char类型数据的数组。上面的规则:“C语言中,当一维数组作为函数参数的时候,编译器总是把它解析成一个指向其首元素类型的指针。”在这里同样适用,也就是说我们可以把这个函数声明改写为:

结论:

任何维度的数组,传参的时候,都要发生降维,降维成指向其首元素类型的指针

那么,二维数组,内部“元素”是一维数组!那么降维成指向一维数组的指针 


6. 函数指针 

顾名思义,函数指针就是函数的指针。它是一个指针,指向一个函数。看例子: 

1. (*(void (*) ()) 0) ()-这是什么

7. 函数指针数组

8. 函数指针数组指针

注意,这里的 pf 和 第 7 章节的 pf 就完全是两码事了。4第 7 章节的 pf 并非指针,而是一个数组名;这里的 pf 确实是实实在在的指针。这个指针指向 :
        一个包含了3个元素的数组;这个数组里面存的是指向函数的指针;这些指针指向一些返回值类型为指向字符的指针,参数为一个指向字符的指针的函数。这比第 7 章节的函数指针数组更拗口。其实你不用管这么多,明白这是一个指针就ok了,其用法与前面讲的数组指针没有差别。

9. 强烈推荐两个专栏

《C语言深度解剖》https://blog.csdn.net/weixin_43382136/category_12344236.htmlicon-default.png?t=N7T8https://blog.csdn.net/weixin_43382136/category_12344236.html
《精通C指针》https://blog.csdn.net/weixin_43382136/category_12659166.htmlicon-default.png?t=N7T8https://blog.csdn.net/weixin_43382136/category_12659166.html

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

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

相关文章

实例详解C/C++中static与extern关键字的使用

目录 1、概述 2、编译C++代码时遇到的变量及函数重复定义的问题 3、用 extern 声明外部变量 4、extern与全局函数 5、为何在变量和函数前添加一个static关键字编译就没问题了呢? 6、静态局部变量 7、函数的声明与定义都放置到一个头文件中,不使用static,通过宏控制去…

广东启动“粤企质量提升工作会议” 着力提升产品和服务质量

6月5日,由广东质量峰会组委会牵头,联合相关质量、信用、打假和检验检测等部门共同举办的“粤企质量提升工作会议”在广州正式启动。本次工作会议旨在贯彻落实《质量强国建设纲要》及《广东省质量强省建设纲要》精神,深入开展全民质量行动,弘扬企业家和工匠精神,营造政府重视质量…

实战17:GCN+LSTM图卷积神经网络预警预测 完整代码数据集

直接看视频演示: GCN+LSTM图卷积神经网络预警预测时间序列预测_哔哩哔哩_bilibili 模型图原理: 完整代码: import torch import torch.nn as nn import torch.optim as optim from torch_geometric.nn import GCNConv from torch.utils.data import DataLoader, TensorDat…

十二星座女、具有哪些情感特质。

白羊座(奋不顾身)。金牛座(爱财如命)。双子座(灵活多变)。 巨蟹座(似水柔情)。狮子座(光明磊落)。处女座(尽善尽美)。 天秤座&#xf…

【Unity学习笔记】第十八 基于物理引擎的日月地系统简单实现

转载请注明出处: https://blog.csdn.net/weixin_44013533/article/details/139701843 作者:CSDN|Ringleader| 目录 目标数学理论资源准备数据准备代码实现Unity准备效果展示注意事项后记 目标 目标:利用Unity的物理引擎实现 “日地月三体系统” 。 效果…

CubeMX Keil Configure

// 使用外部高速晶振 外部高速晶振为8M(根据开发板上的晶振频率设置),使用 PLLCLK,HSE 选项 USART1 使用 Asynchronous,PA9,PA10引脚(USART1 引脚根据开发板上引脚设置) 设置MDK AR…

【2024最新华为OD-C/D卷试题汇总】[支持在线评测] URL拼接(100分) - 三语言AC题解(Python/Java/Cpp)

🍭 大家好这里是清隆学长 ,一枚热爱算法的程序员 ✨ 本系列打算持续跟新华为OD-C/D卷的三语言AC题解 💻 ACM银牌🥈| 多次AK大厂笔试 | 编程一对一辅导 👏 感谢大家的订阅➕ 和 喜欢💗 📎在线评测链接 URL拼接(100分) 🌍 评测功能需要订阅专栏后私信联系清隆解…

Pixel Transformer:用像素代替补丁可以提升图像分类精度

在快速发展的人工智能领域,ViTs已成为各种计算机视觉任务的基础模型。ViTs通过将图像划分为小块并将这些小块作为标记来处理图像。6月刚发布一篇论文,引入了一种新颖的方法,即像素级Transformers,它通过将单个像素视为令牌来挑战这…

大数据实训项目(小麦种子)-03、大数据环境Hadoop、Mapreduce、Hive、Hbase、HDFS搭建服务及调试

文章目录 前言一、Linux系统Centos7安装配置JDK8二、Linxu系统Centos7中搭建Hadoop3.1.0服务下载地址服务1:详细步骤(初始化与启动dfs服务)详细步骤配置环境变量 服务2:Hadoop(YARN)环境搭建 三、Linux系统搭建Hive3.1.2服务前提条…

桂电人工智能学院大数据实验,使用 Docker 搭建 hadoop 集群

桂电人工智能学院大数据实验,使用 Docker 搭建 hadoop 集群 第一步 安装 Docker, Windows 上可以使用 Docker Desktop 下载地址:https://www.docker.com/products/docker-desktop/ 安装过程自行谷歌 安装好的标志:打开终端 运行docker p…

产品人生(13):从“产品的RFM分析”看如何探索“职业方向”

我们在做产品分析时,经常会用到一种方法“产品的RFM分析”,它是一种客户细分和价值评估的常用方法,广泛应用于电子商务、零售和其他众多行业,它可以帮助企业和产品团队更好地理解用户行为,优化营销策略,提升…

基于协同过滤算法的电影推荐

基于协同过滤算法的电影推荐 电影推荐系统使用了基于**协同过滤(Collaborative Filtering)的算法来生成推荐。具体来说,使用了基于用户的协同过滤(User-Based Collaborative Filtering)**算法,步骤如下&am…

国外视频搬运素材去哪里找?可搬运下载国外的素材网站库分享

如果你正在寻找高质量的国外视频素材来提升你的视频制作水平,不用担心,今天我将为大家推荐几个知名的视频素材网站,让你轻松找到理想的素材资源。 蛙学府 首先推荐的是国内非常受欢迎的“蛙学府”。蛙学府是一个综合性网站,专门提…

通过Navicat Premium进行sql文件互转db文件

目录 一、获取原来数据sql文件 二、通过sql文件转换db文件 三、db文件转换成功 一、获取原来数据sql文件 原数据main右键》转储SQL文件》结构和数据》main.sql 二、通过sql文件转换db文件 新建db文件》Navicta》新建SQLite(SQ)》导入db文件》 main右键》运行SQL文件》 mai…

【调试笔记-20240611-Linux-配置 OpenWrt-23.05 支持泛域名 acme 更新】

调试笔记-系列文章目录 调试笔记-20240611-Linux-配置 OpenWrt-23.05 支持泛域名 acme 更新 文章目录 调试笔记-系列文章目录调试笔记-20240611-Linux-配置 OpenWrt-23.05 支持泛域名 acme 更新 前言一、调试环境操作系统:Windows 10 专业版调试环境调试目标 二、调…

计算机专业:黄金时代是否依旧?

计算机专业:黄金时代是否依旧? 随着2024年高考落幕,数百万高三学生将面临人生中的重要抉择:选择大学专业。在这个关键节点,计算机相关专业是否仍是“万金油”的选择?在过去的几十年里,计算机科…

Adobe设计替代软件精选列表

Adobe软件的替代列表,最初由 XdanielArt 收集,并由社区改进。您可以随意打开问题或拉出请求,或从数据中创建图像(以便于共享)。列表总是按照免费和开源选项的顺序排列,但根据您的用例,它可能不是最佳选择 替代因素 &am…

【Qt】QT textBrowser 设置字体颜色和大小

1. 效果 2. 代码 {ui->methodText->append("<font size9 colorgreen> dddddddddd </font>");ui->methodText->append("<font size9 colorred> vvvvvvvvvv </font>"); }

测试 halcon算子 derivate_gauss 高斯一阶导数卷积

参上了 matlab fileexchange 有人上传了高斯 dx,dy一阶导卷积代码 卷积核的计算我修改成了核元素绝对值求做分母 归一化 和halcon的 derivate_gauss算子的计算结果对别如下 还是不知道怎么做到两者结果一致. 测试图像: 我的: halcon的: 获取两份图像的灰度值到数组并做对应位…

利用机器学习重构视频中的人脸

引言 中国与英国的研究团队携手合作&#xff0c;开创了一种创新的视频面孔重塑技术。这项技术能够以极高的一致性对视频中的面部结构进行逼真的放大和缩小&#xff0c;且避免了常见伪影的产生。 从研究人员选取的YouTube视频样例中可见&#xff0c;经过处理后&#xff0c;女演…