14 数组的相关概念,数组的定义、(越界)访问、长度计算、循环遍历

2024-08-23 04:12

本文主要是介绍14 数组的相关概念,数组的定义、(越界)访问、长度计算、循环遍历,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

1 数组的概念

1.1 为什么需要数组

1.1.1 需求分析1

1.1.2 需求分析2

1.1.3 容器的概念

1.2 什么是数组

1.3 数组相关概念

1.4 数组的特点

2 数组操作

2.1 数组的定义

2.2 访问数组元素

2.3 数组越界访问

2.4 计算数组长度

2.5 遍历数组

2.5.1 输出各个元素

2.5.2 初始化各个元素

2.6 案例演示

2.6.1 计算元素和与平均数

2.6.2 取元素最大值

3 测试题


1 数组的概念

1.1 为什么需要数组

1.1.1 需求分析1

        需要统计某公司 50 个员工的工资情况,例如计算平均工资、找到最高工资等。用之前知识,首先需要声明 50 个变量来分别记录每位员工的工资,这样会很麻烦。因此我们可以将所有的数据全部存储到一个容器中统一管理,并使用容器进行计算

1.1.2 需求分析2

        左边图例中有许多条新闻信息,如果用变量来表示一条新闻信息,我们将需要非常多的变量,如果将多条新闻信息存储到一个容器中统一管理将会非常方便。右边图例中有许多条外卖商家数据,也是同样的道理,将多条外卖商家数据存储到一个容器中统一管理将会非常方便。

1.1.3 容器的概念

        生活中的容器:水杯(装水等液体),衣柜(装衣服等物品),集装箱(装货物等)。

        程序中的容器:将多个数据存储到一起,每个数据称为该容器的元素

1.2 什么是数组

        数组(Array),是多个相同类型数据按一定顺序排列的集合,并使用一个标识符命名,并通过编号(索引,亦称为下标或角标)的方式对这些数据进行统一管理。

1.3 数组相关概念

        数组名:本质上是一个标识符常量,命名需要符合标识符规范

        元素:同一个数组中的元素必须是相同的数据类型

        下标(索引、角标):从 0 开始的连续数字

        数组的长度:表示元素的个数

1.4 数组的特点

        创建数组时会在内存中开辟一整块连续的空间,占据的空间的大小,取决于数组的长度和数组中元素的类型

        数组中的元素在内存中是依次紧密排列的且有序的

        数组一旦初始化完成,其长度就是确定的,数组的长度一旦确定,就不能修改

        我们可以直接通过索引(下标)获取指定位置的元素,速度很快。


2 数组操作

2.1 数组的定义

        方式一:先指定元素的个数和类型,后面再进行初始化。

// 定义数组,数组名字是 arr1,元素类型是 int,元素个数是 3 个  
int arr1[3];// 定义完成后再给元素赋值
arr1[0] = 100;
arr1[1] = 200;
arr1[2] = 300;

        方式二:指定元素的类型和个数并同时进行初始化。

        当初始化的元素个数与定义的数组长度相等时,所有的元素都会被赋值。

        当初始化的元素个数少于定义的数组长度时,未被初始化的元素将默认被赋值为 0

        当初始化的元素个数多于定义的数组长度时,编译器会报错,因为多余的初始化值没有对应的位置存储。

// 定义完数组直接进行初始化
int arr2[3] = {4,5,6};// 赋值的元素个数和定义的不一致,多
int arr3[3] = {4,5,6,7};   // 报错// 赋值的元素个数和定义的不一致,少,后面的元素值默认为 0
int arr4[5] = {4,5};   // arr4[0] = 4, arr4[1] = 5, arr4[2] …… arr4[4] 均为 0

        方式三:指定元素的类型,不指定元素个数(系统会自动计算),同时进行初始化。

// 没有指定元素个数,系统会自动计算
int arr3[] = {7,8,9,10};

2.2 访问数组元素

        通过 “数组名[下标]” 可以访问数组中的元素,案例如下 :

#include <stdio.h>int main()
{// 定义一个包含 4个 整数的数组,并初始化这些元素为 10, 20, 30, 40int nums[4] = {10, 20, 30, 40};// 修改数组中元素的值nums[0] += 10;nums[1] += 20;nums[2] += 30;nums[2] = 66; // 再次修改nums[3] = 88;// 读取并打印数组中每个元素的值printf("第一个元素的值:%d\n", nums[0]); // 输出第一个元素的值:20printf("第二个元素的值:%d\n", nums[1]); // 输出第二个元素的值,40printf("第三个元素的值:%d\n", nums[2]); // 输出第三个元素的值:66printf("第四个元素的值:%d\n", nums[3]); // 输出第四个元素的值:88return 0;
}

2.3 数组越界访问

        数组下标必须在指定范围内使用,超出范围视为越界

#include <stdio.h>int main()
{// 定义一个包含 4个 整数的数组,并初始化这些元素为 10, 20, 30, 40int nums[5] = {10, 20, 30, 40, 50};// 修改数组中元素的值nums[0] += 10;nums[1] += 20;nums[2] += 30;nums[2] = 66; // 再次修改nums[3] = 88;nums[4] = 99;// 越界访问,修改到了其它内存空间,危险行为nums[6] = 300;// 读取并打印数组中每个元素的值printf("第一个元素的值:%d\n", nums[0]); // 输出第一个元素的值:20printf("第二个元素的值:%d\n", nums[1]); // 输出第二个元素的值,40printf("第三个元素的值:%d\n", nums[2]); // 输出第三个元素的值:66printf("第四个元素的值:%d\n", nums[3]); // 输出第四个元素的值:88printf("第五个元素的值:%d\n", nums[4]); // 输出第五个元素的值:99// 越界访问// 尝试访问下标为 -1,5的元素,这是未定义行为,可能输出垃圾值printf("越界访问,垃圾数据 nums[-1]:%d\n", nums[-1]); // 不确定的垃圾值printf("越界访问,垃圾数据nums[5]:%d\n", nums[5]);    // 不确定的垃圾值// 上面 nums[6] = 300; 修改了这个内存地址的值为 300,不安全printf("越界访问,不安全行为 nums[6]:%d\n", nums[6]); // 越界访问 nums[6]:300return 0;
}

        输出结果如下所示:

        注意:C 语言在越界访问数组时通常不会直接报错(即不会像一些高级语言那样在运行时抛出异常或错误)。这是因为 C 语言是一种低级语言,它直接与硬件打交道,提供了对内存的直接访问能力,但同时也要求程序员负责内存的管理和安全。

        提示:在 C 语言中,数组是通过指针进行访问的,而数组名在表达式中会被转换成指向数组首元素的指针。当你通过数组名加上索引来访问数组元素时,实际上是在进行指针运算,即根据索引值计算出目标元素的地址,然后访问该地址处的数据。如果索引超出了数组分配的内存范围,编译器并不会检查这一点,因为 C 语言的设计哲学之一就是“相信程序员”。因此,编译器会生成直接访问该内存地址的代码,如果那个地址是可访问的(比如没有超出进程的地址空间),程序就会继续执行,但是访问到的数据可能不是预期的,这可能导致数据损坏、程序崩溃或安全漏洞。 

2.4 计算数组长度

        数组长度(元素个数)是在数组定义时明确指定且固定的,我们不能在运行时直接获取数组长度,但是,我们可以通过 sizeof 运算符间接计算出数组长度,计算步骤如下:

  1. 使用 sizeof 运算符计算出整个数组的字节长度。sizeof(array)
  2. 由于数组成员是同一类型,每个元素字节长度相等,用整个数组的字节长度除以单个元素的字节长度就可以得到数组的长度。 常用:sizeof(array) / sizeof(array[0]);
  3. 也可以用整个数组的字节长度除以数组的数据类型得到数组的长度。 sizeof(array) / sizeof(数组的基本数据类型);

#include <stdio.h>int main()
{// 定义一个整型数组,没有显式指定数组的长度// 但编译器会根据初始化时提供的元素数量自动确定长度int nums1[] = {10, 20, 30, 40, 50, 60, 70};int nums2[] = {10, 20, 30, 40, 50, 60, 70, 80, 90, 100};// 使用 sizeof 运算符计算整个数组所占用的字节长度// sizeof 是编译时运算符,所以 arrByteLen 的值是在编译时就确定的// 计算基本数据类型的大小,必须使用括号将数据类型关键字包裹起来。// 对于字面量和变量,sizeof 运算符可以直接作用于它们,括号是可选的,可以省略括号。int arrByteLen1 = sizeof nums1;  // 可以不加括号int arrByteLen2 = sizeof(nums2); // 加上括号更直观// 用整个数组的字节长度除以数组中单个元素(即 int 类型)的字节长度// 以计算出数组中元素的数量(即数组的长度)int arrLen1 = arrByteLen1 / sizeof nums1[0];int arrLen2 = arrByteLen2 / sizeof(nums1[0]);int arrLen3 = arrByteLen2 / sizeof(nums1[1]); // 随便一个元素都行(都是同类型的)// 也可以除以数组的基本数据类型int arrLen4 = arrByteLen2 / sizeof(int);// 打印计算出的数组长度printf("数组nums1的长度:%d\n", arrLen1); // 输出:数组的长度:7printf("数组nums2的长度:%d\n", arrLen2); // 输出:数组的长度:10printf("数组nums2的长度:%d\n", arrLen3); // 输出:数组的长度:10printf("数组nums2的长度:%d\n", arrLen4); // 输出:数组的长度:10return 0;
}

         知识点回顾:sizeof 计算基本数据类型的大小,必须使用括号将数据类型关键字包裹起来。对于字面量和变量,sizeof 运算符可以直接作用于它们,括号是可选的,可以省略括号。

2.5 遍历数组

2.5.1 输出各个元素

        遍历数组是指按顺序访问数组中的每个元素,以便读取或修改它们,编程中一般使用循环结构对数组进行遍历。

#include <stdio.h>int main()
{int arr[10] = {12, 2, 31, 24, 15, 36, 67, 108, 29, 51};// 计算数组的总长度(即元素个数)// sizeof arr 计算的是整个数组所占用的字节数// sizeof arr[0] 计算的是数组中单个元素所占用的字节数// 将两者相除得到的就是数组的长度(元素个数)int len = sizeof arr / sizeof arr[0];// 遍历数组中的每个元素// 使用 for 循环,循环变量 i 从 0 开始,直到小于数组的长度(即最后一个元素的索引)printf("遍历数组中的元素:\n");for (int i = 0; i < len; i++){printf("%d: %d \n", i, arr[i]);}return 0;
}

        输出结果如下所示:

2.5.2 初始化各个元素

        我们还可以通过数组遍历完成数组的初始化赋值,看下面案例。

        创建长度为 10 的数组,元素依次赋值为 100,200,300,400,500,600,700,800,900,1000,并按逆序输出每个元素,代码如下:

#include <stdio.h>int main()
{// 声明一个整型数组 arr,大小为 10,初始时数组中的元素值未定义int arr[10];int len = sizeof arr / sizeof arr[0];// 遍历数组进行初始化赋值,依次将元素赋值为 100……1000for (int i = 0; i < len; i++){arr[i] = (i + 1) * 100;}// 通过遍历逆序输出数组元素// 从数组的最后一个元素开始,直到第一个元素// 注意逆序第一个下标不是 len 而是 len - 1for (int i = len - 1; i >= 0; i--){printf("%d ", arr[i]); // 1000 900 800 700 600 500 400 300 200 100}return 0;
}

2.6 案例演示

2.6.1 计算元素和与平均数

        计算数组中所有元素的和以及平均数。

#include <stdio.h>int main()
{// 定义并初始化一个整型数组 arr,包含 10 个元素int arr[10] = {12, 2, 31, 24, 15, 36, 67, 108, 29, 51};// 计算数组的长度int len = sizeof arr / sizeof arr[0];// 定义一个整型变量 sum,用于存储数组所有元素的总和int sum = 0;// 使用 or 循环遍历数组中的每个元素// i 是循环变量,从 0 开始,直到小于数组的长度 lenfor (int i = 0; i < len; i++){// 在每次循环中,将当前元素的值加到 sum 上sum += arr[i];}// 计算平均值// 将总和 sum 除以元素数量 len,得到平均值double avg = (double)sum / len;printf("数组元素之和:%d\n", sum);     // 375printf("数组元素平均值:%.2f\n", avg); // 37.50return 0;
}

2.6.2 取元素最大值

        取出数组中之值最大的元素。

#include <stdio.h>int main()
{// 定义一个整型数组 arr,包含 10 个元素,并初始化它们int arr[10] = {12, 2, 31, 24, 15, 36, 67, 108, 29, 51};// 计算数组的长度。int len = sizeof arr / sizeof arr[0];// 定义一个整型变量 max,用于存储数组中的最大值,初始化为数组的第一个元素的值// 先假设第一个元素为最大值,然后循环遍历int max = arr[0];// 方法一:使用 for 循环遍历数组中的每个元素for (int i = 0; i < len; i++){// 在循环体内,使用 if 语句检查当前元素(arr[i])是否大于 max// 如果是,就将当前元素的值赋给 max,更新 max 为当前找到的最大值if (arr[i] > max){max = arr[i];}}printf("最大的元素值:%d\n", max); // 108// 方法二:使用 for 循环遍历数组中的每个元素for (int i = 0; i < len; i++){// 或者用三元运算符max = arr[i] > max ? arr[i] : max;}printf("最大的元素值:%d\n", max); // 108return 0;
}

3 测试题

1. 如何计算数组的长度

【答案】(1)使用 sizeof 运算符计算出整个数组的字节长度。(2)由于数组成员是同一类型,每个元素字节长度相等,用整个数组的字节长度除以单个元素的字节长度就可以得到数组的长度。sizeof(array) / sizeof(数组的基本数据类型);  或常用:  sizeof(array) / sizeof(array[0]);

这篇关于14 数组的相关概念,数组的定义、(越界)访问、长度计算、循环遍历的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1098316

相关文章

Java AOP面向切面编程的概念和实现方式

《JavaAOP面向切面编程的概念和实现方式》AOP是面向切面编程,通过动态代理将横切关注点(如日志、事务)与核心业务逻辑分离,提升代码复用性和可维护性,本文给大家介绍JavaAOP面向切面编程的概... 目录一、AOP 是什么?二、AOP 的核心概念与实现方式核心概念实现方式三、Spring AOP 的关

JavaScript对象转数组的三种方法实现

《JavaScript对象转数组的三种方法实现》本文介绍了在JavaScript中将对象转换为数组的三种实用方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友... 目录方法1:使用Object.keys()和Array.map()方法2:使用Object.entr

Java Instrumentation从概念到基本用法详解

《JavaInstrumentation从概念到基本用法详解》JavaInstrumentation是java.lang.instrument包提供的API,允许开发者在类被JVM加载时对其进行修改... 目录一、什么是 Java Instrumentation主要用途二、核心概念1. Java Agent

MyBatis/MyBatis-Plus同事务循环调用存储过程获取主键重复问题分析及解决

《MyBatis/MyBatis-Plus同事务循环调用存储过程获取主键重复问题分析及解决》MyBatis默认开启一级缓存,同一事务中循环调用查询方法时会重复使用缓存数据,导致获取的序列主键值均为1,... 目录问题原因解决办法如果是存储过程总结问题myBATis有如下代码获取序列作为主键IdMappe

Kotlin 协程之Channel的概念和基本使用详解

《Kotlin协程之Channel的概念和基本使用详解》文章介绍协程在复杂场景中使用Channel进行数据传递与控制,涵盖创建参数、缓冲策略、操作方式及异常处理,适用于持续数据流、多协程协作等,需注... 目录前言launch / async 适合的场景Channel 的概念和基本使用概念Channel 的

Python实现精确小数计算的完全指南

《Python实现精确小数计算的完全指南》在金融计算、科学实验和工程领域,浮点数精度问题一直是开发者面临的重大挑战,本文将深入解析Python精确小数计算技术体系,感兴趣的小伙伴可以了解一下... 目录引言:小数精度问题的核心挑战一、浮点数精度问题分析1.1 浮点数精度陷阱1.2 浮点数误差来源二、基础解决

JavaScript中比较两个数组是否有相同元素(交集)的三种常用方法

《JavaScript中比较两个数组是否有相同元素(交集)的三种常用方法》:本文主要介绍JavaScript中比较两个数组是否有相同元素(交集)的三种常用方法,每种方法结合实例代码给大家介绍的非常... 目录引言:为什么"相等"判断如此重要?方法1:使用some()+includes()(适合小数组)方法2

Spring 依赖注入与循环依赖总结

《Spring依赖注入与循环依赖总结》这篇文章给大家介绍Spring依赖注入与循环依赖总结篇,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录1. Spring 三级缓存解决循环依赖1. 创建UserService原始对象2. 将原始对象包装成工

redis-sentinel基础概念及部署流程

《redis-sentinel基础概念及部署流程》RedisSentinel是Redis的高可用解决方案,通过监控主从节点、自动故障转移、通知机制及配置提供,实现集群故障恢复与服务持续可用,核心组件包... 目录一. 引言二. 核心功能三. 核心组件四. 故障转移流程五. 服务部署六. sentinel部署

Python文本相似度计算的方法大全

《Python文本相似度计算的方法大全》文本相似度是指两个文本在内容、结构或语义上的相近程度,通常用0到1之间的数值表示,0表示完全不同,1表示完全相同,本文将深入解析多种文本相似度计算方法,帮助您选... 目录前言什么是文本相似度?1. Levenshtein 距离(编辑距离)核心公式实现示例2. Jac