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

C语言学习笔记——数组

前言

     数组是C语言中的一种自定义数据类型,它的使用非常广泛。但是很多新手在使用数组时,经常在一些细节上出问题,导致程序崩溃或者无法编译。今天,我就来详细聊聊数组的使用和我注意到的一些细节。


一、常见的数组类型与数组的创建

1. 常见数组类型 

       与常见指针类型类似,常见的数组类型也是与常见的数据类型一一对应的,这里简单列举几个最常用的。

int arr1[10]; //整型数组
char arr2[20];  //字符数组
double arr3[5];  //双精度浮点型数组
struct stru arr[6];  //结构体数组

2. 数组的创建 

int arr1[10];  //创建数组
int arr2[5] = {0,0,0,0,0};  //创建数组并完全初始化
int arr3[10] = {0};    //创建数组并不完全初始化
int arr4[] = {1,2,3,4,5};   //创建未给定大小的数组并初始化

       我们以整型数组为例,上方为整形数组的四种创建与初始化方式。

       首先来介绍第一行,也是最基础的数组创建方式,“数据类型 + 数组名 + [数组大小]”。其中,数组名和[ ]之间不能出现空格,数组大小只能为常量(可以为标识符常量)且只能为正整数。那么我们来解读第一行,就是一个名为“arr1”,可以存放10个整型数据的数组(数组大小为10个整型,即40个字节),该数组的数据类型为"int[10]"。

       我们可以用“sizeof()”来计算数组在内存中占用的空间大小,如下图。

       接下来看第二行,数组arr2为一个大小为5的整型数组,它的五个数据均被初始化为0。其中花括号中的第一个0即对应数组的第一个数据,称为数组的第一个元素,以此类推。

       同理,第三行的arr3数组为一个大小为10的整型数组,它的第一个元素被初始化为0,剩余的9个元素未初始化。

       第四行的arr4数组在创建时未给定大小,那么它的大小就由初始化的元素个数决定。由此得出arr4的大小为5。  

补充:变长数组 (VS编译器不支持)

       变长数组与常规数组的区别在于,常规数组在定义时,其数组大小只能为常量,而变长数组的大小可以为变量。因此,变长数组可以精准地开辟内存空间,从而避免空间浪费。

3. 字符数组的初始化

char ch1[] = {'a','b','c'};
char ch2[] = "abc";

       字符数组可以按照常规数组初始化方式进行初始化,也可以按照上图第二行的方式,直接用字符串进行初始化。那么上图中的ch1,ch2的大小分别是多少呢?显然,ch1的大小为3,那么ch2的大小也为3吗?我们来看下图。

       我们发现,数组ch2的大小为4,这又是为什么呢?我们不妨直接将这两个数组以字符串形式打印出来看看。

       可以看见,第一行出现了许多乱码,而第二行则是正常的"abc"。这是为什么呢?关于这个问题,等到介绍数组在内存中的存储方式时,我再来详细解释。

二、数组元素的访问 

       当我们创建了一个数组后,我们应该如何调用数组中的元素呢?换句话说,如何访问数组的元素呢?

1. 下标引用操作符 

//  [] -  下标引用操作符 int arr[10];
arr[0] = 1;

       数组的每个元素都有自己对应的下标,通过其下标即可访问该元素。我们可以通过下标引用操作符“[ ]”。如图,arr为一个大小为10的整型数组,当我们创建了数组之后,我们通过“数组名 + [元素下标]”,访问了arr数组中下标为0的元素,并将其赋值为1。

       注意,上方代码中创建数组和访问数组元素均使用了“[ ]”,但这两个“[ ]”的含义不同,第一行的“[ ]”代表arr为一个"int[10]"类型的数组,而第二行的“[ ]”则是下标引用操作符,用来访问数组元素。

       数组中第n个元素的下标为n-1,例如,第一个元素的下标为0。

2. 指针访问

int arr[10];
*(arr+1) = 2;  //等价于: arr[1] = 2;

       本质上,数组名就是指向数组起始地址的指针。由于数组arr为整型数组,故访问数组元素时,其数组名本质上为" int* "类型的指针,可通过指针的解引用操作符访问其指向的元素。图中“arr+1”即指针向后偏移一位,指向了数组中的第二个元素,因此等价于访问数组第二个元素。

int arr[10];
int* p = arr;
*(p+1) = 2;

       同样地,我们也可以用其它指针储存arr所指向的地址,并通过该指针访问数组元素,如图中的指针p。 

三、数组在内存中的存储 

       相信通过上面的访问方式,聪明的你一定已经猜到数组在内存中的存储形式了。数组在内存上占用一块连续的空间。我们通过VS的调试器来观察数组在内存中的存储,如下图。每个整型数据占用四个字节的空间,故地址每隔四存放一个数据。

        而数组名arr即是首元素的地址,我们可以通过打印地址来查看。这也就解释了为什么数组元素可以通过指针的方式来访问。

 

        接下来我们来解决之前字符数组留下来的问题。

       通过监视窗口,我们可以看到,ch1中只有三个元素,相比之下,ch2多出了第四个元素'\0'。'\0'是一个字符,用来终止字符串。

       由于ch2中存在'\0',打印ch2时才不会出现后面的随机值。而ch1中没有'\0',因此当ch1的元素全部打印完后,编译器会继续打印数组外的内容,由于未被赋值,这些内容通常为随机值,因此一直打印到遇到随机的'\0',打印才结束。

       使用双引号" "引用的字符串的末尾都会自带一个'\0',这就是为什么用"abc"初始化ch2后会多出一个'\0'。 

四、二维数组

1. 二维数组的创建

int arr1[3][3] = {0};
int arr2[][3] = {0};

       二维数组有以上两种创建方式,其中前后两个“[ ]”中的数字我们分别称为行数和列数。二维数组的大小等于行数乘以列数。

       在第一行中,我们给定了数组arr1的行数和列数,均为3,因此arr1的大小为9,即可容纳九个元素。同样,如果行数和列数都给定了,就可以选择不初始化。

       在第二行中,我们给定了数组arr2的列数,但没有给定行数,这样一来数组的大小就根据初始化的值来确定了,如图中将第一个元素初始化为0,那么为了存储元素0,数组必须开辟一行的空间,由于一行有三列,因此数组arr2的大小为3。从中我们可以看出,为给定行数的二维数组的大小是由列数和初始化元素个数决定的。其大小必须为列数的整数倍,并且必须大于元素个数。

2. 二维数组元素的访问 

int arr[3][3];
arr[0][0]=1;

       二维数组的元素也是通过下标来访问的,只不过由于二维数组有行和列,因此下标也分为行标和列标。同样的,行标和列标也都是从0开始,如图中arr[0][0]即是访问第一行第一列的元素。

       关于二维数组的指针访问,本文就不过多解释了,因为相对来说比较复杂,涉及到二维数组的原理。有兴趣的小伙伴可以自行推导。(提示:要用到二级指针)

3. 二维数组在内存中的存储 

       在我们理解二维数组时,我们把二维数组分成行和列,如上图。我们知道,一维数组在内存中是占用一块连续的空间。那么二维数组在内存中又是如何存储的呢?我们再次通过VS的内存窗口观察二维数组arr在内存中的存储情况。我们发现,二维数组在内存中也是连续存储的,并不是像我们理解的那样分为行和列来存储。

五、数组问题中一些常见的错误和技巧

1. 越界访问

int arr[10] = {1,2,3,4,5,6,7,8,9,10};
int a = arr[10];

       我们来看上面这段代码。首先创建一个大小为10的整型数组并完全初始化,然后创建一个整型变量a,并初始化为arr数组中下标为10的元素。但是数组arr的大小为10,那么它最大的下标应该为9,也就是说不存在下标为10的元素。这就是所谓的越界访问。

       由于数组创建时只开辟了10个整型大小的合法空间,而上方代码访问了第11个整型的空间,因此形成非法访问。

2. 初始化与修改数组元素

//错误示范
int arr[] = {1,2,3,4,5};
arr = {1,2,3,0,0};
char ch[] = "hello";
ch = "world";//正确做法
int arr[] = {1,2,3,4,5};
arr[3] = 0;
arr[4] = 0; 
char ch[] = "hello";
ch = strcpy(ch,"world");

       在讲数组的初始化时,我提到可以使用花括号和字符串进行初始化。但是要注意,当数组创建完毕并初始化后就不能再使用花括号或字符串来修改数组了。如果数组创建完成后需要修改数组元素,则需通过下标访问需要修改的元素进行修改,若需要整体修改则可借助循环遍历数组。

3. 数组传参 

void test(int* arr)
{arr[0] = arr[1];
}int main()
{int arr[3] = {0,1,2};test(arr);return 0;
}

       之前介绍访问数组的方法时就提到,数组名其实就是指向数组首地址的指针,也就是说,数组传参其实也就等价于指针传参。在声明函数的参数时,若需接受数组,只需要声明一个同类型的指针变量即可。


结束语 

       以上就是我对数组的一些基础知识的总结啦。关于数组,我个人还是更喜欢将其理解为指针的一种变体,因为C语言中,指针可以说与内存息息相关,而数组则是通过一个指针(数组名)来管理该数组空间内的所有数据。不知道大家都是如何理解的呢?欢迎在评论区留言。

相关文章:

C语言学习笔记——数组

前言 数组是C语言中的一种自定义数据类型,它的使用非常广泛。但是很多新手在使用数组时,经常在一些细节上出问题,导致程序崩溃或者无法编译。今天,我就来详细聊聊数组的使用和我注意到的一些细节。 一、常见的数组类型与数组的创建…...

类和对象 - 中

本文已收录至《C语言》专栏! 作者:ARMCSKGT 目录 前言 正文 构造函数 对比C和C的初始化 构造函数的使用与特性 默认构造函数 C11关于默认构造缺陷的补丁 析构函数 析构函数特性 默认析构和自定义析构 拷贝构造函数 问题聚焦 拷贝构造的定…...

Android之屏幕适配方案

在说明适配方案之前,我们需要对如下几个概念有所了解:屏幕尺寸,屏幕分辨率,屏幕像素密度。 屏幕尺寸 屏幕尺寸指屏幕的对角线的物理长度,单位是英寸,1英寸2.54厘米。 比如常见的屏幕尺寸:5.0、5…...

SpringBoot+jersey跨域文件上传

一、配置tomcat服务器 1.1、添加upload文件夹 在webapps\Root文件夹下创建用于接收上传文件的upload文件夹 1.2、修改conf\web.xml设置允许上传文件 <init-param><param-name>readonly</param-name><param-value>false</param-value></ini…...

数据结构One——绪论

本喵是FW视频封面最终版宝子&#xff0c;你不点个赞吗&#xff1f;不评个论吗&#xff1f;不收个藏吗&#xff1f; 最后的最后&#xff0c;关注我&#xff0c;关注我&#xff0c;关注我&#xff0c;你会看到更多有趣的博客哦&#xff01;&#xff01;&#xff01; 喵喵喵&#…...

JVM篇之内存及GC

目录一、JVM内存区域1.1程序计数器1.2虚拟机栈1.3本地方法栈1.4堆1.5方法区二、JVM运行时内存2.1新生代(轻量级GC)2.2老年代&#xff08;重量级GC&#xff09;一、JVM内存区域 JVM 内存区域主要分为线程私有区域【程序计数器、虚拟机栈、本地方法栈】、线程共享区域【JAVA 堆、…...

Linux驱动操作地址(寄存器)的一些方式

Linux驱动操作地址(寄存器&#xff09;的一些方式 文章目录Linux驱动操作地址(寄存器&#xff09;的一些方式1.对绝对地址赋值操作2. ioremap2.1 void __iomem *地址2.2 volatile unsigned int *地址2.3 structioremap1.对绝对地址赋值操作 对绝对地址0x100000赋值操作 *&…...

Java日志框架介绍

Log4j Apache Log4j是一个基于Java的日志记录工具。它是由Ceki Glc首创的&#xff0c;现在则是Apache软件基金会的一个项目。 Log4j是几种Java日志框架之一。 Log4j 2 Apache Log4j 2是apache开发的一款Log4j的升级产品。 Commons Logging Apache基金会所属的项目&#xff0c;是…...

编程中遇到的计算机大小端概念

概念大小端&#xff08;Endian&#xff09;是指在一个多字节的数据中&#xff0c;字节的存储顺序的规定。通俗来说&#xff0c;就是指数据在计算机内部存储时的顺序问题。在计算机系统中&#xff0c;一个数据项可能占据多个存储单元。在这种情况下&#xff0c;这个数据项的存储…...

日志与可视化方案:从ELK到EFK,再到ClickHouse

EFK方案 从ELK谈起 ELK是三个开源软件的缩写&#xff0c;分别表示&#xff1a;Elasticsearch&#xff0c;Logstash&#xff0c;Kibana。新增了一个FlieBeat&#xff0c;它是一个轻量级的日志收集处理工具&#xff0c;FlieBeat占用资源少&#xff0c;适用于在各个服务器上搜集…...

字符函数和字符串函数(上)——“C”

各位CSDN的uu们你们好呀&#xff0c;今天小雅兰来给大家介绍一个全新的知识点&#xff0c;就是字符函数和字符串函数啦&#xff0c;其实其中有些函数我之前已经学习过了&#xff0c;比如strlen、strcpy&#xff1b;也有一些之前不是很熟悉的函数&#xff0c;比如strstr、strtok…...

九龙证券|下周解禁市值超400亿元,3股解禁压力较大

下周3股解禁比例超50%。 百利电气昨日盘中直线拉升封板&#xff0c;至此&#xff0c;百利电气两连板&#xff0c;累计涨幅20.85%。 昨日晚间&#xff0c;百利电气发布股票交易反常动摇公告称&#xff0c;公司不触及“室温超导”相关业务&#xff0c;也未打开相关研发和投入。公…...

一个大型网站架构的演变历程

正序&#xff1a; Rome was not built in a day&#xff08;罗马不是一天建成的。&#xff09;一个成熟的大型网站从来都不是一蹴而就的&#xff0c;需要经过多次架构的调整和升级&#xff0c;我们熟知的大型网站比如京东、淘宝、亚马逊&#xff0c;它们每天都有巨大的用户访问…...

前端前沿web 3d可视化技术 ThreeJS学习全记录

前端前沿web 3d可视化技术 随着浏览器性能和网络带宽的提升 使得3D技术不再是桌面的专利 打破传统平面展示模式 前端方向主要流向的3D图形库包括Three.js和WebGL WebGL灵活高性能&#xff0c;但代码量大&#xff0c;难度大&#xff0c;需要掌握很多底层知识和数学知识 Threej…...

链表经典笔试题(LeetCode刷题)

本篇文章主要是对力扣和牛客网上一些经典的和链表有关的笔试题的总结归纳&#xff0c;希望对你有所帮助。 目录 一、移除链表元素 1.1 问题描述 1.2 思路一 1.2.1 分析 1.2.2 代码 1.3 思路二 1.3.1 分析 1.2.3 思路三 1.3 代码实现 1.3.1 思路1的代码 1.3.2 思路2的…...

SpringCloud五大组件

微服务SpringCloud整合技术组件基本流程&#xff1a; 引入组件启动器依赖坐标覆盖默认配置即application.properties配置文件(每个微服务只有一个并且服务启动默认加载)引导类(微服务入口即main方法)自定义开启组件注解 SpringCloudEureka 服务注册中心&#xff0c;分为Eure…...

Echart的使用初体验,Echarts的基本使用及语法格式,简单图表绘制和使用及图例添加【学习笔记】

Echart&#xff1f; ECharts 是一个使用 JavaScript 实现的开源可视化库&#xff0c;涵盖各行业图表&#xff0c;满足各种需求。 ECharts 遵循 Apache-2.0 开源协议&#xff0c;免费商用。 ECharts 兼容当前绝大部分浏览器&#xff08;IE8/9/10/11&#xff0c;Chrome&#xf…...

聊聊腾讯T13技术专家被开除

这两天腾讯的技术大佬stonehuang被曝离开腾讯&#xff0c;据他老婆在小红书上发的帖子称是遭遇了裁员&#xff0c;说实话刚看到这个消息我挺震惊的&#xff0c;stonehuang在中国大前端领域是排得上号的专家&#xff0c;同时他2005年就加入了腾讯&#xff0c;在qq空间的发展历程…...

c++ 常见宏、模板用法【1】

目录1、宏定义实现简单的断言2、可变参数模板3、变量模板4、宏定义实现范围内的for循环5、模板实现函数对象6、宏定义实现作用域限定7、类型萃取模板1、宏定义实现简单的断言 #define ASSERT(expr) \if(!(expr)) { \std::cout << "assertion failed: " <&l…...

【25】Verilog进阶 - 序列检测

VL25 输入序列连续的序列检测 本题并不难【中等】难度给高了 【做题关键】 (1)需要使用移位寄存器的思路。其实reg型是寄存器,也可以当做是移位寄存器,重要的是对其的处理,使用的是移位寄存器的思路 (2)注意新移入数据存放在低位 1 题目 + 代码 + TestBench 很简单,没…...

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…...

label-studio的使用教程(导入本地路径)

文章目录 1. 准备环境2. 脚本启动2.1 Windows2.2 Linux 3. 安装label-studio机器学习后端3.1 pip安装(推荐)3.2 GitHub仓库安装 4. 后端配置4.1 yolo环境4.2 引入后端模型4.3 修改脚本4.4 启动后端 5. 标注工程5.1 创建工程5.2 配置图片路径5.3 配置工程类型标签5.4 配置模型5.…...

mongodb源码分析session执行handleRequest命令find过程

mongo/transport/service_state_machine.cpp已经分析startSession创建ASIOSession过程&#xff0c;并且验证connection是否超过限制ASIOSession和connection是循环接受客户端命令&#xff0c;把数据流转换成Message&#xff0c;状态转变流程是&#xff1a;State::Created 》 St…...

基于Flask实现的医疗保险欺诈识别监测模型

基于Flask实现的医疗保险欺诈识别监测模型 项目截图 项目简介 社会医疗保险是国家通过立法形式强制实施&#xff0c;由雇主和个人按一定比例缴纳保险费&#xff0c;建立社会医疗保险基金&#xff0c;支付雇员医疗费用的一种医疗保险制度&#xff0c; 它是促进社会文明和进步的…...

Python爬虫实战:研究feedparser库相关技术

1. 引言 1.1 研究背景与意义 在当今信息爆炸的时代,互联网上存在着海量的信息资源。RSS(Really Simple Syndication)作为一种标准化的信息聚合技术,被广泛用于网站内容的发布和订阅。通过 RSS,用户可以方便地获取网站更新的内容,而无需频繁访问各个网站。 然而,互联网…...

【大模型RAG】Docker 一键部署 Milvus 完整攻略

本文概要 Milvus 2.5 Stand-alone 版可通过 Docker 在几分钟内完成安装&#xff1b;只需暴露 19530&#xff08;gRPC&#xff09;与 9091&#xff08;HTTP/WebUI&#xff09;两个端口&#xff0c;即可让本地电脑通过 PyMilvus 或浏览器访问远程 Linux 服务器上的 Milvus。下面…...

汽车生产虚拟实训中的技能提升与生产优化​

在制造业蓬勃发展的大背景下&#xff0c;虚拟教学实训宛如一颗璀璨的新星&#xff0c;正发挥着不可或缺且日益凸显的关键作用&#xff0c;源源不断地为企业的稳健前行与创新发展注入磅礴强大的动力。就以汽车制造企业这一极具代表性的行业主体为例&#xff0c;汽车生产线上各类…...

VTK如何让部分单位不可见

最近遇到一个需求&#xff0c;需要让一个vtkDataSet中的部分单元不可见&#xff0c;查阅了一些资料大概有以下几种方式 1.通过颜色映射表来进行&#xff0c;是最正规的做法 vtkNew<vtkLookupTable> lut; //值为0不显示&#xff0c;主要是最后一个参数&#xff0c;透明度…...

GruntJS-前端自动化任务运行器从入门到实战

Grunt 完全指南&#xff1a;从入门到实战 一、Grunt 是什么&#xff1f; Grunt是一个基于 Node.js 的前端自动化任务运行器&#xff0c;主要用于自动化执行项目开发中重复性高的任务&#xff0c;例如文件压缩、代码编译、语法检查、单元测试、文件合并等。通过配置简洁的任务…...

深入浅出深度学习基础:从感知机到全连接神经网络的核心原理与应用

文章目录 前言一、感知机 (Perceptron)1.1 基础介绍1.1.1 感知机是什么&#xff1f;1.1.2 感知机的工作原理 1.2 感知机的简单应用&#xff1a;基本逻辑门1.2.1 逻辑与 (Logic AND)1.2.2 逻辑或 (Logic OR)1.2.3 逻辑与非 (Logic NAND) 1.3 感知机的实现1.3.1 简单实现 (基于阈…...