走进数组的奇妙之旅

引言:

在前几篇文章中,我们深入探讨了函数的奥秘。在讲述函数知识的过程中,我们邂逅了一个新的概念,你或许还记得在演示 strcpy函数时,出现的这行代码:char1[20]={0};。当时,你是否感到好奇,心中是否升起了疑问:这是什么呢?没错,这正是我们本篇文章的主角——数组

在数字的世界里,数组就像是一座有序排列的宝库,它以一种简洁而高效的方式存储和管理着大量的数据。但你是否真正了解它的奥秘呢?让我们一起踏上探索数组的旅程吧!


1、一维数组

1.1 数组的创建

数组是一组相同类型元素的集合。

你可以这么理解什么是数组:想象一个书架,上面整齐地排列着同一类书籍,这就类似于一个数组。每本书的位置都是固定的,而且它们都属于同一类型(比如都是小说或者都是传记)。

在了解了什么是数组的情况下,我们又产生一个疑问,为什么要引入数组这个概念呢?

我们拿一个例子来解释一下:

存放一串整数,我们用代码可以怎么表示?

#include <stdio.h>
int main()
{int a = 1;int b = 2;int c = 3;//如果我们要存1~100个数字呢?//数组时一组相同类型的元素的集合return 0;
}

如果我们要存1~100的数字,难道我们就这样写下去吗?那也太麻烦了,这时数组就应运而生了!

当我们了解了为什么需要数组后,我们还需要知道该怎么使用它。

数组的创建方式:

type_t  arr_name    [const_n];
// type_t 是指数组的元素类型

// arr_name是指数组的名
// const_n 是一个常量表达式,用来指定数组的大小

比如说:

int arr[10];//整型数组,名为arr [10]指存放了10个元素
double data[20];
char ch[5];//字符数组,名为ch [5]指存放五个字符

也可以同时创建多个相同类型的数组

int arr 1 [10];
int arr 2 [2+8];

这里你或许还会有新的疑问, [ ] 这里面只能用常量或常量表达式吗? 

但是呢,并不是所有情况下都可以使用变量,比如

补充知识点:

在C99标准之前,数组的大小必须是常量或者常量表达式;

在C99之后,数组的大小可以是变量,这是为了支持变长数组;

变长数组的意思是 数组的大小是通过变量来指定的。

上图中的代码想要实现该怎么办呢?

#include <stdio.h>
int main()
{	int n = 10;scanf("%d",&n);int arr[n];	return 0;
}

但是呢,这里还有一个限制条件,就是只能在支持C99标准的编译器上编译

支持C99的编译器:

  • GCC:GNU Compiler Collection 的缩写,它在其编译器集合中提供了 C 编译器,支持 C99 标准。GCC 是一款广泛使用的开源编译器。
  • Clang:基于 LLVM 的 C 编译器,支持 C11 标准,同时也对 C99 有较好的支持。
  • Intel C++ Compiler:英特尔的 C++编译器,也支持 C99 标准。
  • Keil:在 Keil 编译器中,可通过相关设置使其支持 C99(变量声明在执行语句之后)。具体操作是在“Options for Target”中的“C/C++”选项卡下,勾选“C99 Mode”。

1.2 数组的初始化

数组的初始化:在创建数组的同时给数组的内容一些合理初始值(初始化)。

直接代码演示:

int arr1[10] = { 1,2,3 };//不完全初始化,剩余的元素,默认初始化为0
int arr2[] = { 1,2,3,4 };
int arr3[5] = { 1,2,3,4,5 };//完全初始化
char arr4[3] = { 'a',98,'c' };
char arr5[] = { 'a','b','c'};
char arr6[] = { "abcdef" };//[]括号里面也是可以不指定大小的

注意:

1. 不完全初始化(所给元素比指定元素要少),剩余的元素,默认初始化为 0;

2. 括号[ ]里面是可以不指定大小的,这样的话该数组大小(指定值)就等于所给的元素(初始化)。

数组在创建的时候如果不想不指定数组的确定的大小就得初始化。数组的元素个数根据初始化的内容来确定。

但是对于下面的代码要会区分,内存中如何分配。

char arr1[10] = { 'a','b','c'};
char arr2[10] = { "abc" };

对于arr1,里面的字符为:a b c 0 0 0 0 0 0 0

对于arr2,里面的字符为:a b c \0 0 0 0 0 0 0

1.3 一维数组的使用

对于数组的使用,我们要介绍了一个操作符: [ ] , 下标引用操作符。它其实就是数组访问的操作符。

int main()
{int arr[] = { 1,2,3,4,5,6,7,8,9,10 };	//编号        0 1 2 3 4 5 6 7 8 9//编号是0开始的,这里的编号就是数组的下标       printf("%d\n", arr[4]);return 0;
}

   打印结果是:

 图中可知打印 arr[4]的结果,我们可以得到整数5

使用下标引用操作符arr[i]来访问数组中索引为 i 的元素。

如果我们想把数组的内容全部打印出来,那么范围该怎么确定?

范围不需要我们计算,使用sizeof函数会自动帮我们计算范围。

代码如下:

int main()
{int arr[] = { 1, 2, 3 ,4 ,5 ,6, 7,8 ,9,10 };	//下标范围    0                        9         //printf("%d\n", arr[4]);int i = 0;int sz = sizeof(arr) / sizeof(arr[0]);//自动计算范围 左边计算的时总数组的大小  右边的是第一个元素大小for (i = 0; i < sz; i++)//正序打印{printf("%d ", arr[i]);//i是0时打印1,i是1时打印2...}for(i = sz-1;i >= 0;i--)//倒序打印{printf("%d ", arr[i]);}return 0;
}

  下标用来定位数组

注意;使用下标引用操作符时,一定要确保索引值在有效范围内,否则可能导致访问越界错误。

总结:

1.数组是使用下标来访问的,下标是从0开始的。

2.数组的大小可以通过计算得到。                                                    

int sz = sizeof(arr) / sizeof(arr[0]);//计算公式

1.4 一维数组在内存中的存储

数组在内存中是连续存储的,这意味着数组中的元素在内存中是一个紧挨着一个排列的。

 比如说,有一个整数类型的一维数组 int arr[5] = {1, 2, 3, 4, 5} 。

注:地址是16进制的

在 C 语言中,一个整数通常占用 4 个字节的内存空间。

假设这段连续存储空间的起始地址为 0*1000 ,由于在 C 语言中,一个整数通常占用 4 个字节的存储空间。那么,数组中的第一个元素将被存储在地址 0*1000 ,第二个元素紧接着存储在地址 0*1004 ,第三个元素在 0*1008 ,第四个元素在 0*100C ,第五个元素则在 0*1010 。

这种连续存储有两个重要的特点和影响:

一方面,它使得随机访问数组元素的速度非常快。比如说,如果想要获取第三个元素,只需要通过简单的计算 起始地址 + 元素大小 * 索引 (也就是 1000 + 4 * 2 = 1008 ),就能直接找到并访问到第三个元素 3 ,几乎不需要额外的时间去查找。

另一方面,它也带来了一些不便。比如说,如果要在数组中间插入一个新元素,那就需要把插入位置后面的所有元素都向后移动,以腾出空间插入新元素。这是一个比较耗时的操作。同样,删除数组中间的元素时,也需要把后面的元素向前移动来填补空缺。

为了更加直观的解释,我们来用表格辅助理解:

假设有一个整数数组int num[3] = {10,20,30 } ,其在内存中的存储情况如下:

内存地址存储的值
0 * 200010
0 * 200420
0 * 200830

如果现在要在第二个位置插入一个新元素 15 ,那么原有的 20 和 30 都需要向后移动 4 个字节,变成:

内存地址存储的值
0 * 200010
0 * 200415
0 * 200820
0 * 200C30

同样,如果要删除第二个元素 15 ,则 20 和 30 需要向前移动:

内存地址存储的值
0 * 200010
0 * 200420
0 * 200830

综上所述,C 语言中一维数组在内存中的连续存储方式在提供快速随机访问的同时,也在插入和删除操作上带来了一定的复杂性。

当我们知道这一特点后,有助于我们在编程实践中根据具体需求合理地选择和使用数组,或者考虑其他更适合特定操作的数据结构。

所以,理解 C 语言中一维数组在内存中的连续存储方式,对于我们有效地使用数组、优化程序性能以及避免一些常见的错误(比如内存越界访问)都非常重要。

代码展示:

int main()
{int arr[] = { 1, 2, 3 ,4 ,5 ,6, 7,8 ,9,10 };	//下标范围    0                        9         //printf("%d\n", arr[4]);int i = 0;int sz = sizeof(arr) / sizeof(arr[0]);//打印数组的每个元素的地址for (i = 0; i < sz; i++){printf("&arr[%d] = %p\n",i, &arr[i]);}return 0;
}

结果展示:

随着数组下标的增加,元素的地址,也在有规律的递增。

2、二维数组

二维数组可以看作是由多个一维数组组成的数组

2.1 二维数组的创建

//数组创建
int arr[3][4];
char arr[3][3];
double arr[1][2];
//3 2 1
//1 2 3
//3 1 2
int main()
{int arr[3][3];return 0;
}

2.2 二维数组的初始化

int main()
{int arr[3][3] = { 3,2,1,1,2,3,3,1,2 };//完全初始化return 0;
}

这样排列元素,会先找前三个放在第一行,再找三个放在第二行,最后三个放在第三行。也就是会按顺序放 

如果所给的元素不够怎么办?

int main()
{int arr[3][3] = { 3,2,1,1,2,3,3};//不完全初始化return 0;
}

 

 我们可以看到少的位置被自动补上了 0 

我们还有另一种方法排放元素

int main()
{int arr[3][3] = { { 3,2},{1,1},{2,3} };//不完全初始化return 0;
}

如果数据不够的时候,我们可以按照分小组的方法,把想要的数据放在适当的位置。

二维数组如果有初始化,行可以省略,列不能省略

int arr[ ][4] = {{1,2},{3,4}};

2.3 二维数组的使用

代码展示: 

#include <stdio.h>
int main()
{int arr[3][4] = { 1,2,3,4,2,3,4,5,3,4,5,6 };int i = 0;for (i = 0;i < 3; i++)//确定行{int j = 0;for (j = 0; j < 4; j++)//确定列{printf("%d", arr[i][j]);}printf("\n");}return 0;
}

 结果展示:

也可以打印其中的单个元素

printf("%d\n",arr[2][0]);
//结果:3

2.4 二维数组在内存中的存储

二维数组可以理解为:一维数组的数组

在 C 语言中,二维数组在内存中是按照行优先(row-major order)的方式连续存储的。

假设我们有一个二维数组 int arr[2][3] ,其在内存中的存储方式类似于将其看作一个一维数组。 先存储第一行的所有元素,然后再存储第二行的元素。

也就是说,内存中元素的排列顺序是 arr[0][0] 、 arr[0][1] 、 arr[0][2] 、 arr[1][0] 、 arr[1][1] 、 arr[1][2] 。

以具体的内存地址为例,如果 arr[0][0] 的地址为 1000 ,且每个 int 类型占用 4 个字节,那么 arr[0][1] 的地址就是 1004 , arr[0][2] 的地址是 1008 , arr[1][0] 的地址是 1012 ,依此类推。

这种连续存储的方式使得可以通过简单的地址计算来快速访问二维数组中的元素。

但需要注意的是,在处理二维数组时,要确保索引不越界,以免访问到非法的内存地址导致程序出错。

2.5 二维数组的实际应用

二维数组在实际编程中有许多应用场景,以下是一些常见的例子:

1. 图像处理:可以用来存储图像的像素信息,其中行和列分别对应图像的高度和宽度。 - 例如,灰度图像可以用二维数组存储每个像素的灰度值。

2. 矩阵运算:如线性代数中的矩阵相加、相乘等操作。 - 在科学计算、机器学习和数值分析中经常用到。

3. 电子表格:类似于 Excel 中的表格数据,可以用二维数组表示行和列的数据。

4. 地图表示:将地图划分为网格,用二维数组存储每个网格的相关信息,如地形、资源等。

5. 游戏开发: - 表示游戏中的棋盘、地图布局。 - 存储游戏中多个角色的位置和状态。

6. 座位安排:例如在会议室、教室等场景中安排座位。

 7. 文本处理:分析文本的二维结构,如表格形式的文本。

这些只是二维数组的一些常见应用场景,实际上,只要数据具有二维的特性并且需要进行批量处理,都可以考虑使用二维数组来进行存储和操作。


结语:

本篇文章即将落下帷幕,在这篇文章中,我们共同探索了一元数组与二元数组的奥秘。在下篇文章中,我们将继续深入挖掘数组知识的精髓,期待您的持续关注与阅读。让我们共同期待,在知识的海洋中,不断探索,不断前行。

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

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

相关文章

PHP萌宠之家微信小程序系统源码

&#x1f43e;萌宠之家微信小程序&#x1f43e; —— 铲屎官们的温馨小窝✨ &#x1f3e0;【一键开启萌宠乐园】&#x1f3e0; 亲们&#xff0c;是不是每次刷手机都忍不住想看看那些软萌可爱的毛孩子&#xff1f;现在&#xff0c;有了“萌宠之家”微信小程序&#xff0c;你的…

通信流程:https【SSL/TLS】,git仓库【https/SSH】,蓝牙【面对面快传/AirDrop】

目录 HTTPS HTTP&#xff08;80端口&#xff09; SSL/TLS协议&#xff08;传输层&#xff0c;443端口&#xff09; 密文传输&#xff1a;SSL的后续版本TLS TLS1.2握手 1.摘要算法(散列函数 Hash Function)&#xff1a;验证信息的完整性&#xff0c;不可逆 第三方认证 引…

数据结构之初始二叉树(2)

找往期文章包括但不限于本期文章中不懂的知识点&#xff1a; 个人主页&#xff1a;我要学编程(ಥ_ಥ)-CSDN博客 所属专栏&#xff1a;数据结构&#xff08;Java版&#xff09; 二叉树的前置知识&#xff08;概念、性质、、遍历&#xff09; 通过上篇文章的学习&#xff0c;我们…

iOS——MRC与ARC以及自动释放池深入底层学习

MRC与ARC再回顾 在前面&#xff0c;我们简单学了MRC与ARC。MRC指手动内存管理&#xff0c;需要开发者使用retain、release等手动管理对象的引用计数&#xff0c;确保对象在必要时被释放。ARC指自动内存管理&#xff0c;由编译器自动管理对象的引用计数&#xff0c;开发者不需要…

如何用EXCEL自动解方程/方程组?利用 矩阵乘法X=A-*B,X=mmult(minverse(A), B)

目录 问题的由来 1 数据 → 模拟分析 → 单变量求解 1.1 找一个单元格填入公式 1.2 功能入口 1.3 选择单变量求解&#xff0c;分别填入内容 1.4 求解 1.5 这个感觉用处不大 2 重点介绍&#xff0c;用EXCEL进行矩阵运算解方程的操作 2.1 运用EXCEL进行矩阵运算&…

Sentinel-1 Level 1数据处理的详细算法定义(四)

《Sentinel-1 Level 1数据处理的详细算法定义》文档定义和描述了Sentinel-1实现的Level 1处理算法和方程,以便生成Level 1产品。这些算法适用于Sentinel-1的Stripmap、Interferometric Wide-swath (IW)、Extra-wide-swath (EW)和Wave模式。 今天介绍的内容如下: Sentinel-1 L…

操作系统内核源码杂谈篇:临界区

临界资源&#xff0c;是指同一时刻只能由一个线程&#xff08;linux下为进程&#xff09;访问的资源&#xff0c;而临界区就是为了确保临界资源访问是单一数据流。 临界区的代码执行&#xff0c;也就是进行原子操作&#xff0c;不会被打断。 先分析RTOS的运行架构&#xff0c…

人工智能算法工程师(高级)课程1-单类目标识别之人脸检测识别技术MTCNN模型介绍与代码详解

大家好&#xff0c;我是微学AI&#xff0c;今天给大家介绍一下人工智能算法工程师(高级)课程1-单类目标识别之人脸检测识别技术MTCNN模型介绍与代码详解。本文深入探讨了基于PyTorch的人脸检测与识别技术&#xff0c;详细介绍了MTCNN模型、Siamese network以及center loss、sof…

qml 实现一个listview

主要通过qml实现listvie功能&#xff0c;主要包括右键菜单&#xff0c;滚动条&#xff0c;拖动改变内容等&#xff0c;c 与 qml之间的变量和函数的调用。 main.cpp #include <QQuickItem> #include <QQmlContext> #include "testlistmodel.h" int main…

Java里的引用详解

1.体验方法引用 方法引用的出现原因 在使用Lambda表达式的时候&#xff0c;我们实际上传递进去的代码就是一种解决方案&#xff1a;拿参数做操作 那么考虑一种情况&#xff1a;如果我们在Lambda中所指定的操作方案&#xff0c;已经有地方存在相同方案&#xff0c;那是否还有必要…

PHP房产中介租房卖房平台微信小程序系统源码

​&#x1f3e0;【租房卖房新选择】揭秘房产中介小程序&#xff0c;一键搞定置业大事&#xff01;&#x1f3e1; &#x1f50d;【开篇&#xff1a;告别繁琐&#xff0c;拥抱便捷】&#x1f50d; 还在为找房子跑断腿&#xff1f;为卖房发愁吗&#xff1f;今天给大家安利一个超…

JavaScript 获取 url(get)参数

https://andi.cn/page/621584.html

pytorch学习(八)Dataset加载分类数据集

我们之前用torchvision加载了pytorch的网络数据集&#xff0c;现在我们用Dataset加载自己的数据集&#xff0c;并且使用DataLoader做成训练数据集。 图像是从网上下载的&#xff0c;网址是 点这里&#xff0c;标签是图像文件夹名字。下载完成后作为自己的数据集。 1.加载自己…

PyTorch 深度学习实践-循环神经网络基础篇

视频指路 参考博客笔记 参考笔记二 文章目录 上课笔记基于RNNCell实现总代码 基于RNN实现总代码 含嵌入层的RNN网络嵌入层的作用含嵌入层的RNN网络架构总代码 其他RNN扩展基本注意力机制自注意力机制&#xff08;Self-Attention&#xff09;自注意力计算多头注意力机制&#xf…

RPC与服务的注册发现

文章目录 1. 什么是远程过程调用(RPC)?2. RPC的流程3. RPC实践4. RPC与REST的区别4.1 RPC与REST的相似之处4.2 RPC与REST的架构原则4.3 RPC与REST的主要区别 5. RPC与服务发现5.1 以zookeeper为服务注册中心5.2 以etcd为服务注册中心 6. 小结参考 1. 什么是远程过程调用(RPC)?…

TCP三次握手与四次挥手详解

1.什么是TCP TCP&#xff08;Transmission Control Protocol&#xff0c;传输控制协议&#xff09;是一种面向连接的、可靠的、基于字节流的通信协议&#xff0c;属于互联网协议族&#xff08;TCP/IP&#xff09;的一部分。TCP 提供可靠的、顺序的、无差错的数据传输服务&…

win11家庭版怎么升级专业版

随着Windows 11的推出&#xff0c;许多用户享受到了全新的用户界面和功能。然而&#xff0c;Windows 11家庭版在某些高级功能上有所限制&#xff0c;例如&#xff0c;组策略管理、远程桌面连接等。为了满足更多的工作需求&#xff0c;许多用户希望将Windows 11家庭版升级到专业…

十、Java集合 ★ ✔(模块18-20)【泛型、通配符、List、Set、TreeSet、自然排序和比较器排序、Collections、可变参数、Map】

day05 泛型,数据结构,List,Set 今日目标 泛型使用 数据结构 List Set 1 泛型 1.1 泛型的介绍 ★ 泛型是一种类型参数&#xff0c;专门用来保存类型用的 最早接触泛型是在ArrayList&#xff0c;这个E就是所谓的泛型了。使用ArrayList时&#xff0c;只要给E指定某一个类型…

分布式IO系统BL201 Profinet耦合器

BL201耦合器是一个数据采集和控制系统&#xff0c;基于强大的32 位微处理器设计&#xff0c;采用Linux操作系统&#xff0c;是一种模块化的分布式I/O系统。该系统由3部分组成&#xff1a;现场总线耦合器和各种类型的&#xff08;数字和模拟信号以及特殊功能&#xff09;I/O模块…

Keka for Mac v1.4.3 中文下载 解压/压缩工具

Mac分享吧 文章目录 效果一、下载软件二、开始安装1、双击运行软件&#xff0c;将其从左侧拖入右侧文件夹中&#xff0c;等待安装完毕2、应用程序显示软件图标&#xff0c;表示安装成功 三、运行测试1、打开软件2、文件访问权限修改3、访达扩展 安装完成&#xff01;&#xff…