当前位置: 首页 > news >正文

C语言指针详解-包过系列(二)目录版

C语言指针详解-包过系列(二)目录版

    • 1、数组名的深入理解
      • 1.1、数组名的本质
      • 1.2、数组名本质的两个例外
        • 1.2.1、sizeof(数组名)
        • 1.2.2、&数组名
    • 2、使用指针访问数组
    • 3、一维数组传参本质
    • 4、二级指针
      • 4.1、二级指针介绍
      • 4.2、二级指针运算
    • 5、指针数组(模拟实现二维数组)

1、数组名的深入理解

1.1、数组名的本质

数组名的本质其实就是数组首元素的地址。我们以下面的代码测试为例:

#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;
}

在这里插入图片描述
可见二者一模一样,故验证了数组名即为数组首元素地址。
但是可能会有读者提出以下质疑:

#include<stdio.h>
int main()
{int arr[10] = {1,2,3,4,5,6,7,8,9,10};printf("%d", sizeof(arr));return 0;
}

在这里插入图片描述
若数组名 arr 表示的是数组首元素的地址的话,那么输出结果应当为一个地址的大小,也就是4/8字节。这就涉及到了数组名是数组首元素地址的两个例外了,请见 1.2

1.2、数组名本质的两个例外

1.2.1、sizeof(数组名)

在关键字 sizeof 中单独放入数组名时,这个数组名表示的是整个数组,计算的是整个数组的大小,单位是字节。如上 1.1 中所示。
注意:只有是单独放入数组名时,数组名才表示整个数组,其余情况下均是计算数据类型大小。
如下面代码所示:

#include<stdio.h>
int main()
{int arr[10] = {1,2,3,4,5,6,7,8,9,10};printf("%d", sizeof(arr + 1));//此时 arr 就又恢复成数组首元素的地址了return 0;
}

在 x86 环境下的运行结果:
在这里插入图片描述
在 x64 环境下的运行结果:
在这里插入图片描述
如上所示,当sizeof内部非单独放置数组名时,sizeof 计算的就是对应运行环境的地址的大小。

1.2.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;
}

在这里插入图片描述
我们发现三个输出结果完全一样。不急,我们通过对地址进行加减整数运算来使他们原形毕露。演示如下:

#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;
}

在这里插入图片描述
通过上述演示我们发现,在 x86 环境下,&arr[0] 和 &arr[0] + 1 之间相差4字节,arr 与 arr + 1 也是相差4字节。这是因为二者均表示首元素的地址, + 1 也就是跳过一个元素。
但是 &arr 和 &arr + 1 则相差40个字节,这就是因为 &arr 表示的是整个数组的地址,+ 1 操作是跳过整个数组的。

2、使用指针访问数组

由上文我们可知数组名表示数组首元素的地址,那么数组名加整数就可以访问首元素后面的元素。
请看下面代码:

#include<stdio.h>
int main()
{int arr[10] = { 0 };int i = 0;size_t sz = sizeof(arr) / sizeof(arr[0]);int* parr = arr;//将数组首元素地址存储到指针 parr 中//输入for (i = 0; i < sz; i++){scanf("%d", parr + i);//遍历数组地址//注意,这里parr+i就已经表示数组对应下标元素的地址了,&parr+i表示的就是指针变量parr的地址加减整数了。//scanf("%d", arr + i);也可以这样写}//输出for (i = 0; i < sz; i++){printf("parr[%d] = %d\n", i, *(parr + i));//输出数组元素}return 0;
}

在这里插入图片描述
在这里我们补充一点数组 arr[ i ] 在编译前系统会将其转化为 *(arr + i) 然后再进行编译。
在这里插入图片描述
由上所述,我们进一步分析,数组名 arr 是首元素地址,可以赋值给 parr ,其实数组名 arr 和 parr 在这里是等价的。既然我们可以使用 arr[i] 访问数组,那 parr[i]也应该能访问数组。如下演示:

#include<stdio.h>
int main()
{int arr[10] = { 0 };int i = 0;size_t sz = sizeof(arr) / sizeof(arr[0]);int* parr = arr;//将数组首元素地址存储到指针 parr 中//输入for (i = 0; i < sz; i++){scanf("%d", parr + i);}//输出for (i = 0; i < sz; i++){printf("parr[%d] = %d\n", i, parr[i]);//等价于arr[i]}return 0;
}

综上,我们可以总结出以下四点:
1、数组就是数组,是一块连续的空间(与数组大小,数组元素个数,数组元素类型都有关系)
2、指针变量就是指针变量,是一个变量(4/8bytes)
3、数组名除两个特例外,表示的都是数组首元素的地址
4、可以使用指针来访问数组,数组在系统编译时转化的内容就是指针。

3、一维数组传参本质

在之前函数章节,我们已经讲过数组可以作为参数传递给函数,在本节我们讲述一下数组传参本质。
我们先用一个问题来作引,我们之前在函数外部计算过数组元素的个数,在本节我们不妨尝试在函数内部计算求数组元素个数。

#include<stdio.h>void test(int arr[])
{size_t sz2 = sizeof(arr) / sizeof(arr[0]);printf("sz2 = %d\n", sz2);
}int main()
{int arr[] = { 1,2,3,4,5,6,7,8,9,10 };size_t sz1 = sizeof(arr) / sizeof(arr[0]);printf("sz1 = %d\n", sz1);test(arr);return 0;
}

在 x86 环境下的结果:
在这里插入图片描述
我们可见 sz2 的值并不等于 sz1 。这就涉及到数组传参的本质了。数组传参的时候传递的是数组名,也就是数组首元素的地址。
所以函数形参部分理论上应当使用指针来接收数组首元素地址。故在函数内部我们所写的sizeof(arr)计算的其实是地址的大小在 x86 环境下就是4字节。sizeof(arr[0])计算的是数组首元素的大小即int类型,大小为4字节。故 sz2 的结果为1.
补充:在x64环境下的运行结果:
在这里插入图片描述

sizeof(arr)/sizeof(arr[0]) == 8/4 == 2

在 x64 环境下地址的大小为8字节。

#include<stdio.h>void test(int* arr)
{printf("%d\n", sizeof(arr));//计算一个指针变量的大小
}int main()
{int arr[] = { 1,2,3,4,5,6,7,8,9,10 };size_t sz1 = sizeof(arr) / sizeof(arr[0]);//计算数组内元素个数多少。printf("sz1 = %d\n", sz1);test(arr);return 0;
}

综上所述,我们总结出以下两点:
1、数组传参的本质是传递了数组首元素的地址,所以形参访问的数组和实参的数组是同一个数组。故在形参数组中更改数组内容也会更改实参数组中的内容(传址调用由此区别于传值调用)。
2、形参的数组是不会再单独创建空间,所以形参的数组是可以省略数组大小的。

4、二级指针

4.1、二级指针介绍

指针变量也是变量,是变量就有地址,那指针变量的地址存放于何处?这就涉及到二级指针。

#include<stdio.h>
int main()
{int a = 0;int* p = &a;//一级指针p存储a的地址int** pp = &p;//二级指针存储p的地址printf("p = %p\n", p);printf("pp = %p\n", pp);return 0;
}

在这里插入图片描述

4.2、二级指针运算

1、*pp 通过对 pp 中地址的解引用,如此找到 p ,*pp访问的就是 p

#include<stdio.h>
int main()
{int a = 0;int b = 10;int* p = &a;//一级指针p存储a的地址int** pp = &p;//二级指针存储p的地址*pp = &b;//通过解引用更改一级指针 p 所指向的对象printf("%d", *p);return 0;
}

在这里插入图片描述
2、**pp 先通过*pp 找到 p 然后再通过对 p 解引用找到 a。

#include<stdio.h>
int main()
{int a = 0;int* p = &a;//一级指针p存储a的地址int** pp = &p;//二级指针存储p的地址**pp = 20;//通过两次解引用更改一级指针 p 所指向的对象的内容。//等价于*p =20//等价于 a = 20printf("%d", a);return 0;
}

在这里插入图片描述

5、指针数组(模拟实现二维数组)

我们知道有整型数组,字符型数组,浮点型数组等,由此可知指针数组就是存储的元素是指针的数组。指针数组的每个元素又指向一块区域。
我们用指针数组模拟二维数组为例:
首先先编写好3个一维数组:

#include<stdio.h>
int main()
{int arr1[] = { 1,2,3,4,5 };int arr2[] = { 6,7,8,9,10 };int arr3[] = { 11,12,13,14,15 };return 0;
}

然后来设置指针数组,让指针数组内存储一维数组的地址,由上文知就是数组名。

#include<stdio.h>
int main()
{int arr1[] = { 1,2,3,4,5 };int arr2[] = { 6,7,8,9,10 };int arr3[] = { 11,12,13,14,15 };int* arr[] = { arr1 , arr2 , arr3 };//编写指针数组int i = 0;for (i = 0; i < 3; i++)//打印模拟的二维数组{int j = 0;for (j = 0; j < 5; j++){printf("%d ", arr[i][j]);}printf("\n");}return 0;
}

在这里插入图片描述
在这里插入图片描述
上述代码只是模拟出了二维数组的效果,但并不是完全等于二维数组,因为上面模拟出的数组的每一行并不是连续的。

全文至此结束!!!
写作不易,不知各位老板能否给个一键三连或是一个免费的赞呢(-▽-)(-▽-),这将是对我最大的肯定与支持!!!谢谢!!!(-▽-)(-▽-)

相关文章:

C语言指针详解-包过系列(二)目录版

C语言指针详解-包过系列&#xff08;二&#xff09;目录版 1、数组名的深入理解1.1、数组名的本质1.2、数组名本质的两个例外1.2.1、sizeof&#xff08;数组名&#xff09;1.2.2、&数组名 2、使用指针访问数组3、一维数组传参本质4、二级指针4.1、二级指针介绍4.2、二级指针…...

驰骋BPM RunSQL_Init SQL注入漏洞复现

0x01 产品简介 驰骋BPM系统由济南驰骋信息技术有限公司研发,具有悠久的历史和丰富的行业经验。其工作流引擎CCFlow自2003年开始研发,是国内知名的老牌工作流引擎,在BPM领域拥有广泛的研究群体与应用客户群。统提供.net与java两个版本,且两个版本的代码结构、数据库结构、设…...

谷粒商城实战笔记-143-性能压测-压力测试-JMeter在windows下地址占用bug解决

文章目录 问题背景解决步骤1. 修改端口范围2. 调整端口回收时间3. 重启机器 补充说明为什么端口会被用完&#xff1f;为什么Windows要设置这样的限制&#xff1f; 注意事项参考链接 在进行性能压测或压力测试时&#xff0c;使用JMeter这样的工具来模拟大量用户请求&#xff0c;…...

Pod的调度机制

文章目录 一、Pod调度概述二、Pod调度策略实现方式三、kube-scheduler调度1、kube-scheduler调度的流程2、过滤阶段3、打分阶段4、kube-scheduler 调度示例4.1、创建 Deployment 资源清单4.2、应用Deployment4.3、查看被kube-scheduler自动调度的Pod 四、nodeName调度1、创建Po…...

JIT(即时编译)编译器

JIT&#xff08;即时编译&#xff09;编译器是Java虚拟机&#xff08;JVM&#xff09;中的一个重要组件&#xff0c;它的主要作用是将Java字节码&#xff08;bytecode&#xff09;在运行时动态编译成高效的机器码&#xff0c;从而显著提高Java程序的执行效率。JIT编译器优化是J…...

循环执行时数据的同步方式

在dataX-web中循环执行时数据的同步方式 解决中文comment中文乱码 在mysql中 # &#xff08;0&#xff09;修改库注释 alter table DBS modify column desc varchar(256) character set utf8; alter table DATABASE_PARAMS modify column PARAM_VALUE varchar(256) characte…...

Jvm G1与ZGC启动参数

G1GC G1GC 启动参数示例 -XX:UseG1GC -Xms4g -Xmx4g -XX:MaxGCPauseMillis200 -XX:InitiatingHeapOccupancyPercent45 -XX:G1HeapRegionSize16m -XX:ConcGCThreads2 -XX:ParallelGCThreads4 -XX:MetaspaceSize128m -XX:MaxMetaspaceSize512m -XX:ParallelRefProcEnabled -XX:…...

zdpgo_gin_zap 整合zdpgo_gin框架和zdpgo_zap框架的日志中间件,用于实现路由日志记录

zdpgo_gin_zap 整合zdpgo_gin框架和zdpgo_zap框架的日志中间件&#xff0c;用于实现路由日志记录 安装 go get github.com/zhangdapeng520/zdpgo_gin_zap使用教程 基本用法 package mainimport ("fmt""time"gin "github.com/zhangdapeng520/zdpg…...

FOC电流环

interrupt void ISRTimer0(void) { IQ_Ref2.2;//在电流环下给负电流电机反转 IQ_Fdbiq; IQ_ErrorIQ_Ref-IQ_Fdb; IQ_UpIQ_Kp*IQ_Error; IQ_UiIQ_Ui IQ_Ki*IQ_Up IQ_Kc*IQ_SatErr…...

工业设备数据采集方案的设计实施与应用-天拓四方

随着工业4.0时代的来临&#xff0c;工业设备数据采集成为了提升生产效率、优化能源管理、实现智能化决策的关键环节。本文将围绕工业设备数据采集的重要性、方案设计实施以及TDE工业网关的应用实践展开探讨&#xff0c;以期为相关企业提供一种清晰、专业且实操性强的数据采集方…...

【MySQL】索引——索引的实现、B+ vs B、聚簇索引 VS 非聚簇索引、索引操作、创建索引、查询索引、删除索引

文章目录 MySQL5. 索引的实现5.1 B vs B5.2 聚簇索引 VS 非聚簇索引 6. 索引操作6.1 创建主键索引6.2 创建唯一索引6.3 创建普通索引6.4 创建全文索引6.5 查询索引6.6 删除索引 MySQL 5. 索引的实现 因为MySQL和磁盘交互的基本单位为Page&#xff08;页&#xff09;。 MySQL 中…...

剖析HTML 元素——WEB开发系列02

HTML元素是构成HTML文档结构的基本单位&#xff0c;定义了页面上的不同部分和内容。HTML元素可以包含不同类型的内容&#xff0c;如文本、图片、链接、表格等&#xff0c;每种元素都有其特定的用途和语义。通过组合和嵌套不同的HTML元素&#xff0c;可以创建复杂的网页结构和布…...

影响3D打印零件品质的关键因素探析

随着科技的飞速发展&#xff0c;3D打印技术作为一种革命性的制造方式&#xff0c;正逐渐渗透到各个行业和领域。然而&#xff0c;尽管3D打印技术在设计和制造上带来了前所未有的灵活性和效率&#xff0c;但其零件品质却受到多种因素的复杂影响。本文将从设备性能、材料选择、工…...

Oracle事务是怎么练成的

什么是事务 事务是数据库管理系统执行过程的一个逻辑单位&#xff0c;由一系列有限的数据库操作序列构成&#xff0c;事务必须满足‌ACID属性。ACID理论是数据库中最重要的概念之一&#xff0c;分别代表原子性&#xff08;Atomicity&#xff09;、一致性&#xff08;Consisten…...

线程与多线程(一)

线程与多线程&#xff08;一&#xff09; 一、线程1、概念2、示意图3、虚拟地址转换到物理地址4、与进程相比的优点5、与进程相比的缺点6、与进程的关系&#xff08;1&#xff09;线程独有&#xff08;2&#xff09;共享&#xff08;3&#xff09;示意图 二、POSIX线程库三、创…...

连接其他主机上的redis连接不上`telnet: Unable to connect to remote host: Connection refused`

telnet: Unable to connect to remote host: Connection refused 这个错误通常表示目标主机&#xff08;192.168.8.29&#xff09;上的服务&#xff08;6379端口&#xff09;没有运行&#xff0c;或者主机的防火墙/网络设置阻止了连接。 你可以尝试以下步骤来解决问题&#xf…...

dijkstral算法详解

import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Map;public class test39 {// 定义节点类&#xff0c;表示图中的顶点public static class Node {public int value; // 节点的值&#xff0c;即编号public int in; // 进入…...

创意指南丨AR数学沉浸式空间体验

AR学习种类那么多&#xff0c;哪款最吸引你&#xff1f; 星河造梦坊和Unity联手打造的沉浸式空间AR无疑是其中的佼佼者。 这款应用不仅利用AR技术将抽象的数学概念变得生动有趣&#xff0c;还通过互动体验让学习者仿佛置身于一个充满奇幻色彩的数学世界中。 无论是学生还是教…...

linux文件——深度学习文件fd、文件系统调用

前言&#xff1a;从本片开始正式进入linux文件的学习&#xff0c;本片内容主要是文件的fd。 本篇内容博主将要先带友友回忆C语言中的文件操作接口&#xff0c;然后再过渡到操作系统中的系统调用的学习&#xff0c;最后理解操作系统中的文件操作。 ps&#xff1a;本节内容设计一…...

003集——C#数据类型 及大小端序转换——C#学习笔记

如需得到一个类型或一个变量在特定平台上的准确尺寸&#xff0c;可以使用 sizeof 方法。表达式 sizeof(type) 产生以字节为单位存储对象或类型的存储尺寸。下面举例获取任何机器上 int 类型的存储尺寸&#xff1a; using System;namespace DataTypeApplication {class Program{…...

Opencv中的addweighted函数

一.addweighted函数作用 addweighted&#xff08;&#xff09;是OpenCV库中用于图像处理的函数&#xff0c;主要功能是将两个输入图像&#xff08;尺寸和类型相同&#xff09;按照指定的权重进行加权叠加&#xff08;图像融合&#xff09;&#xff0c;并添加一个标量值&#x…...

Spring Boot面试题精选汇总

&#x1f91f;致敬读者 &#x1f7e9;感谢阅读&#x1f7e6;笑口常开&#x1f7ea;生日快乐⬛早点睡觉 &#x1f4d8;博主相关 &#x1f7e7;博主信息&#x1f7e8;博客首页&#x1f7eb;专栏推荐&#x1f7e5;活动信息 文章目录 Spring Boot面试题精选汇总⚙️ **一、核心概…...

什么是EULA和DPA

文章目录 EULA&#xff08;End User License Agreement&#xff09;DPA&#xff08;Data Protection Agreement&#xff09;一、定义与背景二、核心内容三、法律效力与责任四、实际应用与意义 EULA&#xff08;End User License Agreement&#xff09; 定义&#xff1a; EULA即…...

Android15默认授权浮窗权限

我们经常有那种需求&#xff0c;客户需要定制的apk集成在ROM中&#xff0c;并且默认授予其【显示在其他应用的上层】权限&#xff0c;也就是我们常说的浮窗权限&#xff0c;那么我们就可以通过以下方法在wms、ams等系统服务的systemReady()方法中调用即可实现预置应用默认授权浮…...

Java面试专项一-准备篇

一、企业简历筛选规则 一般企业的简历筛选流程&#xff1a;首先由HR先筛选一部分简历后&#xff0c;在将简历给到对应的项目负责人后再进行下一步的操作。 HR如何筛选简历 例如&#xff1a;Boss直聘&#xff08;招聘方平台&#xff09; 直接按照条件进行筛选 例如&#xff1a…...

OPenCV CUDA模块图像处理-----对图像执行 均值漂移滤波(Mean Shift Filtering)函数meanShiftFiltering()

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 在 GPU 上对图像执行 均值漂移滤波&#xff08;Mean Shift Filtering&#xff09;&#xff0c;用于图像分割或平滑处理。 该函数将输入图像中的…...

AI病理诊断七剑下天山,医疗未来触手可及

一、病理诊断困局&#xff1a;刀尖上的医学艺术 1.1 金标准背后的隐痛 病理诊断被誉为"诊断的诊断"&#xff0c;医生需通过显微镜观察组织切片&#xff0c;在细胞迷宫中捕捉癌变信号。某省病理质控报告显示&#xff0c;基层医院误诊率达12%-15%&#xff0c;专家会诊…...

嵌入式学习笔记DAY33(网络编程——TCP)

一、网络架构 C/S &#xff08;client/server 客户端/服务器&#xff09;&#xff1a;由客户端和服务器端两个部分组成。客户端通常是用户使用的应用程序&#xff0c;负责提供用户界面和交互逻辑 &#xff0c;接收用户输入&#xff0c;向服务器发送请求&#xff0c;并展示服务…...

招商蛇口 | 执笔CID,启幕低密生活新境

作为中国城市生长的力量&#xff0c;招商蛇口以“美好生活承载者”为使命&#xff0c;深耕全球111座城市&#xff0c;以央企担当匠造时代理想人居。从深圳湾的开拓基因到西安高新CID的战略落子&#xff0c;招商蛇口始终与城市发展同频共振&#xff0c;以建筑诠释对土地与生活的…...

JavaScript 数据类型详解

JavaScript 数据类型详解 JavaScript 数据类型分为 原始类型&#xff08;Primitive&#xff09; 和 对象类型&#xff08;Object&#xff09; 两大类&#xff0c;共 8 种&#xff08;ES11&#xff09;&#xff1a; 一、原始类型&#xff08;7种&#xff09; 1. undefined 定…...