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

c语言从入门到实战——基于指针的数组与指针数组

基于指针的数组与指针数组

  • 前言
  • 1. 数组名的理解
  • 2. 使用指针访问数组
  • 3. 一维数组传参的本质
  • 4. 冒泡排序
  • 5. 二级指针
  • 6. 指针数组
  • 7. 指针数组模拟二维数组


前言

指针的数组是指数组中的元素都是指针类型,它们指向某种数据类型的变量。


1. 数组名的理解

我们在使用指针访问数组的内容时,有这样的代码:

int arr[10] = {1,2,3,4,5,6,7,8,9,10};
int *p = &arr[0];

这里我们使用 &arr[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 = %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\n", sizeof(arr));return 0;
}

输出的结果是:40,如果arr是数组首元素的地址,那输出应该的应该是4/8才对。

其实数组名就是数组首元素(第一个元素)的地址是对的,但是有两个例外:

  • sizeof(数组名)sizeof中单独放数组名,这里的数组名表示整个数组,计算的是整个数组的大小,单位是字节

  • &数组名,这里的数组名表示整个数组,取出的是整个数组的地址(整个数组的地址和数组首元素的地址是有区别的)

除此之外,任何地方使用数组名,数组名都表示首元素的地址。

可以再试一下这个代码:

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

三个打印结果一模一样,这时候又纳闷了,那arr&arr有啥区别呢?

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

输出结果:

&arr[0] = 0077F820
&arr[0]+1 = 0077F824
arr = 0077F820
arr+1 = 0077F824
&arr = 0077F820
&arr+1 = 0077F848

这里我们发现&arr[0]&arr[0]+1相差4个字节,arrarr+1相差4个字节,是因为&arr[0]arr都是首元素的地址,+1就是跳过一个元素。

但是&arr&arr+1相差40个字节,这就是因为&arr是数组的地址,+1操作是跳过整个数组的。

到这里大家应该搞清楚数组名的意义了吧。

数组名是数组首元素的地址,但是有2个例外。

2. 使用指针访问数组

有了前面知识的支持,再结合数组的特点,我们就可以很方便的使用指针访问数组了。

#include <stdio.h>
int main()
{int arr[10] = {0};
//输入int i = 0;int sz = sizeof(arr)/sizeof(arr[0]);
//输入int* p = arr;for(i=0; i<sz; i++){scanf("%d", p+i);
//scanf("%d", arr+i);//也可以这样写}
//输出for(i=0; i<sz; i++){printf("%d ", *(p+i));}return 0;
}

这个代码搞明白后,我们再试一下,如果我们再分析一下,数组名arr是数组首元素的地址,可以赋值给p,其实数组名arrp在这里是等价的。那我们可以使用arr[i]可以访问数组的元素,那p[i]是否也可以访问数组呢?

#include <stdio.h>
int main()
{int arr[10] = {0};
//输入int i = 0;int sz = sizeof(arr)/sizeof(arr[0]);
//输入int* p = arr;for(i=0; i<sz; i++){scanf("%d", p+i);
//scanf("%d", arr+i);//也可以这样写}
//输出for(i=0; i<sz; i++){printf("%d ", p[i]);//我们也可以使用i[p] ,编译器会编译成*(i + P) 和* (p + i )同理}return 0;
}

在第18行的地方,将*(p+i)换成p[i]也是能够正常打印的,所以本质上p[i]是等价于*(p+i)

同理arr[i]应该等价于*(arr+i),数组元素的访问在编译器处理的时候,也是转换成首元素的地址+偏移量求出元素的地址,然后解引用来访问的。

3. 一维数组传参的本质

数组是可以传递给函数的。

首先从一个问题开始,我们之前都是在函数外部计算数组的元素个数,那我们可以把函数传给一个函数后,函数内部求数组的元素个数吗?

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

输出的结果:

在这里插入图片描述
我们发现在函数内部是没有正确获得数组的元素个数。

这就要学习数组传参的本质了,上篇文章我讲了:数组名是数组首元素的地址;那么在数组传参的时候,传递的是数组名,也就是说本质上数组传参本质上传递的是数组首元素的地址。

所以函数形参的部分理论上应该使用指针变量来接收首元素的地址。那么在函数内部我们写
sizeof(arr) 计算的是一个地址的大小(单位字节)而不是数组的大小(单位字节)。

正是因为函数的参数部分是本质是指针,所以在函数内部是没办法求的数组元素个数的。

void test(int arr[]) //参数写成数组形式,本质上还是指针
{printf("%d\n", sizeof(arr));
}
void test(int* arr) //参数写成指针形式
{printf("%d\n", sizeof(arr)); //计算⼀个指针变量的大小
}
int main()
{int arr[10] = {1,2,3,4,5,6,7,8,9,10};test(arr);return 0;
}

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

4. 冒泡排序

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

//方法1
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 = arr[j];arr[j] = arr[j+1];arr[j+1] = tmp;}}}
}
int main()
{int arr[] = {3,1,7,5,8,9,0,2,4,6};int sz = sizeof(arr)/sizeof(arr[0]);bubble_sort(arr, sz);for(i=0; i<sz; i++){printf("%d ", arr[i]);}return 0;
}
//方法2 - 优化
void 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++){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[] = {3,1,7,5,8,9,0,2,4,6};int sz = sizeof(arr)/sizeof(arr[0]);bubble_sort(arr, sz);for(i=0; i<sz; i++){printf("%d ", arr[i]);}
return 0;
}

5. 二级指针

指针变量也是变量,是变量就有地址,那指针变量的地址存放在哪里——二级指针

在这里插入图片描述
对于二级指针的运算有:

  • *ppa 通过对ppa中的地址进行解引用,这样找到的是 pa*ppa 其实访问的就是 pa .
int b = 20;
*ppa = &b; //等价于 pa = &b;

**ppa 先通过 *ppa 找到 pa ,然后对 pa 进行解引用操作: *pa ,那找到的是 a

**ppa = 30;
//等价于*pa = 30;
//等价于a = 30;

6. 指针数组

指针数组是指针还是数组

我们类比一下,整型数组,是存放整型的数组,字符数组是存放字符的数组。

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

如下图:
在这里插入图片描述
指针数组的每个元素是地址,又可以指向一块区域。

7. 指针数组模拟二维数组

#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[3] = {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]就是整型一维数组中的元素。

上述的代码模拟出二维数组的效果,实际上并非完全是二维数组,因为每一行并非是连续的。


相关文章:

c语言从入门到实战——基于指针的数组与指针数组

基于指针的数组与指针数组 前言1. 数组名的理解2. 使用指针访问数组3. 一维数组传参的本质4. 冒泡排序5. 二级指针6. 指针数组7. 指针数组模拟二维数组 前言 指针的数组是指数组中的元素都是指针类型&#xff0c;它们指向某种数据类型的变量。 1. 数组名的理解 我们在使用指针…...

AUTOSAR汽车电子嵌入式编程精讲300篇-面向车载CAN网络的路由和ECU刷写方法

目录 前言 研究现状 车载CAN的“高层协议”研究现状 车载ECU刷写方法研究现状...

软考 系统架构设计师系列知识点之云计算(3)

接前一篇文章&#xff1a;软考 系统架构设计师系列知识点之云计算&#xff08;2&#xff09; 所属章节&#xff1a; 第11章. 未来信息综合技术 第6节. 云计算和大数据技术概述 4. 云计算的发展历程 根据云计算的定义和内涵&#xff0c;这里将从虚拟化技术、分布式技术和软件应…...

【万字长文】Python 日志记录器logging 百科全书 之 日志过滤

Python 日志记录器logging 百科全书 之 日志过滤 前言 在Python的logging模块中&#xff0c;日志过滤器&#xff08;Filter&#xff09;用于提供更细粒度的日志控制。通过过滤器&#xff0c;我们可以决定哪些日志记录应该被输出&#xff0c;哪些应该被忽略。这对于复杂的应用…...

redis基线检查

1、禁止使用 root 用户启动 | 访问控制 描述: 使用root权限来运行网络服务存在较大的风险。Nginx和Apache都有独立的work用户,而Redis没有。例如,Redis的Crackit漏洞就是利用root用户权限替换或增加authorize_keys,从而获取root登录权限。 加固建议: 使用root切换到re…...

第五章ARM处理器的嵌入式硬件系统设计——课后习题

1ARM处理器的工作状态 ARM处理器有两种工作状态。具体而言&#xff0c;ARM处理器执行32位ARM指令集时&#xff0c;工作在ARM状态&#xff0c;当ARM处理器执行16位thumb指令集时候&#xff0c;工作在thumb状态。 1ARM指令特点 1一个大的&#xff0c;统一的寄存器文件。 2基于…...

Python - GFPGAN + MoviePy 提高人物视频画质

目录 一.引言 二.gif_to_png 三.gfp_gan 四.png_to_gif 五.总结 一.引言 前面我们介绍了 GFP-GAN 提高人脸质量 与 OCR 提取视频台词、字幕&#xff0c;前者可以提高图像质量&#xff0c;后者可以从视频中抽帧&#xff0c;于是博主便想到了将二者进行结合并优化人物 GIF …...

uniapp插件开发

安装android studio&#xff1a;安装目录下bin下的此文件&#xff0c;是用来修改分配给android studio的占用内存。 Android 11足够用。 创建新项目&#xff1a; 目录结构介绍&#xff1a; UI组件介绍&#xff1a;在设计程序界面时可以使用可视化拖拽的方式&#xff0c;没有必要…...

11 Go的作用域

概述 在上一节的内容中&#xff0c;我们介绍了Go的映射&#xff0c;包括&#xff1a;声明映射、初始化映射、操作映射等。在本节中&#xff0c;我们将介绍Go的作用域。在Go语言中&#xff0c;作用域是指变量的可见性范围&#xff0c;它定义了变量在程序中的生命周期和可访问性。…...

RabbitMQ之消息应答和持久化

文章目录 前言一、消息应答1.概念2.自动应答3.消息应答方法4.Multiple 的解释5.消息自动重新入队6.消息手动应答代码7.手动应答效果演示 二、RabbitMQ持久化1.概念2.队列如何实现持久化3.消息实现持久化4.不公平分发5.预取值 总结 前言 在RabbitMQ中&#xff0c;我们的消费者在…...

【分享】Excel“只读方式”的两种模式

查阅Excel表格的时候&#xff0c;担心不小心修改了内容&#xff0c;可以给Excel设置以“只读方式”打开&#xff0c;这样就算修改了内容也不能直接保存表格。Excel表格可以设置两种“只读方式”&#xff0c;一起来看看吧&#xff01; “只读方式” 1&#xff1a; 打开Excel表…...

华为与美团达成合作,正式启动鸿蒙原生应用开发。

11月13日&#xff0c;华为宣布与美团以HarmonyOS为基础进行产业创新、技术应用、商业发展等方面展开全面合作&#xff0c;全力支持美团启动开发鸿蒙原生应用工作。 自9月25日华为宣布全新HarmonyOS NEXT蓄势待发、鸿蒙原生应用全面启动以来&#xff0c;已有金融、旅行、社交等…...

The 8th China Open Source Conference Successfully Concludes

由开源社主办的第八届中国开源年会&#xff08;COSCon23&#xff09;于 2023年10月29日在成都圆满收官。本次大会&#xff0c;为期两天&#xff0c;线下参会报名逾千人次&#xff0c;在线直播观看人数总计 168610 人&#xff0c;直播观看次数达 248725 次&#xff0c;官网累计浏…...

【星海出品】SDN neutron (四) 流分析

Neutron框架之流分析 1.控制端neutron-server通过wsgi接收北向REST API请求&#xff0c;neutron-plugin通过rpc与设备端进行南向通信。 2.设备端agent则向上通过rpc与控制端进行通信&#xff0c;向下则直接在本地对网络设备进行配置。 3.Neutron-agent的实现很多&#xff0c;彼…...

数据分析法宝,一个 SQL 语句查询多个异构数据源

随着企业数据量呈现出爆炸式增长&#xff0c;跨部门、跨应用、跨平台的数据交互需求越来越频繁&#xff0c;传统的数据查询方式已经难以满足这些需求。同时&#xff0c;不同数据库系统之间的数据格式、查询语言等都存在差异&#xff0c;直接进行跨库查询十分困难。 原生跨库查…...

解决:element ui表格表头自定义输入框单元格el-input不能输入问题

表格表头如图所示&#xff0c;有 40-45&#xff0c;45-50 数据&#xff0c;且以输入框形式呈现&#xff0c;现想修改其数据或点击右侧加号增加新数据编辑。结果不能输入&#xff0c;部分代码如下 <template v-if"columnData.length > 0"><el-table-colu…...

Android 透明度设置

目录 一、透明度对照表 二、透明度介绍 三、透明度设置 3.1 xml设置 3.2 代码设置 一、透明度对照表 注&#xff1a;00是完全透明&#xff0c;FF就是完全不透明 我们的UI小姐姐就喜欢给「不透明度」&#xff0c;这个需要自己判断一下。 完全透明:0% HEX: 00 透明度:1%…...

python语言的由来与发展历程

Python语言的由来可以追溯到1989年&#xff0c;由Guido van Rossum&#xff08;吉多范罗苏姆&#xff09;创造。在他的业余时间里&#xff0c;Guido van Rossum为了打发时间&#xff0c;决定创造一种新的编程语言。他受到了ABC语言的启发&#xff0c;ABC语言是一种过程式编程语…...

electronjs入门-编辑器应用程序

我们将在Electron中创建一个新项目&#xff0c;如我们在第1章中所示&#xff0c;名为“编辑器”&#xff0c;我们将在下一章中使用它来创建编辑器&#xff1b;在index.js中&#xff0c;这是我们的主要过程&#xff1b;请记住为Electron软件包放置必要的依赖项&#xff1a; npm…...

Xilinx Kintex7中端FPGA解码MIPI视频,基于MIPI CSI-2 RX Subsystem架构实现,提供工程源码和技术支持

目录 1、前言免责声明 2、我这里已有的 MIPI 编解码方案3、本 MIPI CSI2 模块性能及其优缺点4、详细设计方案设计原理框图OV5640及其配置权电阻硬件方案MIPI CSI-2 RX SubsystemSensor Demosaic图像格式转换Gammer LUT伽马校正VDMA图像缓存AXI4-Stream toVideo OutHDMI输出 5、…...

7.4.分块查找

一.分块查找的算法思想&#xff1a; 1.实例&#xff1a; 以上述图片的顺序表为例&#xff0c; 该顺序表的数据元素从整体来看是乱序的&#xff0c;但如果把这些数据元素分成一块一块的小区间&#xff0c; 第一个区间[0,1]索引上的数据元素都是小于等于10的&#xff0c; 第二…...

突破不可导策略的训练难题:零阶优化与强化学习的深度嵌合

强化学习&#xff08;Reinforcement Learning, RL&#xff09;是工业领域智能控制的重要方法。它的基本原理是将最优控制问题建模为马尔可夫决策过程&#xff0c;然后使用强化学习的Actor-Critic机制&#xff08;中文译作“知行互动”机制&#xff09;&#xff0c;逐步迭代求解…...

三维GIS开发cesium智慧地铁教程(5)Cesium相机控制

一、环境搭建 <script src"../cesium1.99/Build/Cesium/Cesium.js"></script> <link rel"stylesheet" href"../cesium1.99/Build/Cesium/Widgets/widgets.css"> 关键配置点&#xff1a; 路径验证&#xff1a;确保相对路径.…...

【Java学习笔记】Arrays类

Arrays 类 1. 导入包&#xff1a;import java.util.Arrays 2. 常用方法一览表 方法描述Arrays.toString()返回数组的字符串形式Arrays.sort()排序&#xff08;自然排序和定制排序&#xff09;Arrays.binarySearch()通过二分搜索法进行查找&#xff08;前提&#xff1a;数组是…...

Nuxt.js 中的路由配置详解

Nuxt.js 通过其内置的路由系统简化了应用的路由配置&#xff0c;使得开发者可以轻松地管理页面导航和 URL 结构。路由配置主要涉及页面组件的组织、动态路由的设置以及路由元信息的配置。 自动路由生成 Nuxt.js 会根据 pages 目录下的文件结构自动生成路由配置。每个文件都会对…...

拉力测试cuda pytorch 把 4070显卡拉满

import torch import timedef stress_test_gpu(matrix_size16384, duration300):"""对GPU进行压力测试&#xff0c;通过持续的矩阵乘法来最大化GPU利用率参数:matrix_size: 矩阵维度大小&#xff0c;增大可提高计算复杂度duration: 测试持续时间&#xff08;秒&…...

零基础设计模式——行为型模式 - 责任链模式

第四部分&#xff1a;行为型模式 - 责任链模式 (Chain of Responsibility Pattern) 欢迎来到行为型模式的学习&#xff01;行为型模式关注对象之间的职责分配、算法封装和对象间的交互。我们将学习的第一个行为型模式是责任链模式。 核心思想&#xff1a;使多个对象都有机会处…...

华为OD机考-机房布局

import java.util.*;public class DemoTest5 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseSystem.out.println(solve(in.nextLine()));}}priv…...

LabVIEW双光子成像系统技术

双光子成像技术的核心特性 双光子成像通过双低能量光子协同激发机制&#xff0c;展现出显著的技术优势&#xff1a; 深层组织穿透能力&#xff1a;适用于活体组织深度成像 高分辨率观测性能&#xff1a;满足微观结构的精细研究需求 低光毒性特点&#xff1a;减少对样本的损伤…...

根目录0xa0属性对应的Ntfs!_SCB中的FileObject是什么时候被建立的----NTFS源代码分析--重要

根目录0xa0属性对应的Ntfs!_SCB中的FileObject是什么时候被建立的 第一部分&#xff1a; 0: kd> g Breakpoint 9 hit Ntfs!ReadIndexBuffer: f7173886 55 push ebp 0: kd> kc # 00 Ntfs!ReadIndexBuffer 01 Ntfs!FindFirstIndexEntry 02 Ntfs!NtfsUpda…...