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

相关文章

Maven中引入 springboot 相关依赖的方式(最新推荐)

《Maven中引入springboot相关依赖的方式(最新推荐)》:本文主要介绍Maven中引入springboot相关依赖的方式(最新推荐),本文给大家介绍的非常详细,对大家的学习或工作具有... 目录Maven中引入 springboot 相关依赖的方式1. 不使用版本管理(不推荐)2、使用版本管理(推

Spring 中的循环引用问题解决方法

《Spring中的循环引用问题解决方法》:本文主要介绍Spring中的循环引用问题解决方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录什么是循环引用?循环依赖三级缓存解决循环依赖二级缓存三级缓存本章来聊聊Spring 中的循环引用问题该如何解决。这里聊

Python的time模块一些常用功能(各种与时间相关的函数)

《Python的time模块一些常用功能(各种与时间相关的函数)》Python的time模块提供了各种与时间相关的函数,包括获取当前时间、处理时间间隔、执行时间测量等,:本文主要介绍Python的... 目录1. 获取当前时间2. 时间格式化3. 延时执行4. 时间戳运算5. 计算代码执行时间6. 转换为指

C 语言中enum枚举的定义和使用小结

《C语言中enum枚举的定义和使用小结》在C语言里,enum(枚举)是一种用户自定义的数据类型,它能够让你创建一组具名的整数常量,下面我会从定义、使用、特性等方面详细介绍enum,感兴趣的朋友一起看... 目录1、引言2、基本定义3、定义枚举变量4、自定义枚举常量的值5、枚举与switch语句结合使用6、枚

C# foreach 循环中获取索引的实现方式

《C#foreach循环中获取索引的实现方式》:本文主要介绍C#foreach循环中获取索引的实现方式,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录一、手动维护索引变量二、LINQ Select + 元组解构三、扩展方法封装索引四、使用 for 循环替代

Java数组初始化的五种方式

《Java数组初始化的五种方式》数组是Java中最基础且常用的数据结构之一,其初始化方式多样且各具特点,本文详细讲解Java数组初始化的五种方式,分析其适用场景、优劣势对比及注意事项,帮助避免常见陷阱... 目录1. 静态初始化:简洁但固定代码示例核心特点适用场景注意事项2. 动态初始化:灵活但需手动管理代

Spring Boot循环依赖原理、解决方案与最佳实践(全解析)

《SpringBoot循环依赖原理、解决方案与最佳实践(全解析)》循环依赖指两个或多个Bean相互直接或间接引用,形成闭环依赖关系,:本文主要介绍SpringBoot循环依赖原理、解决方案与最... 目录一、循环依赖的本质与危害1.1 什么是循环依赖?1.2 核心危害二、Spring的三级缓存机制2.1 三

C++中初始化二维数组的几种常见方法

《C++中初始化二维数组的几种常见方法》本文详细介绍了在C++中初始化二维数组的不同方式,包括静态初始化、循环、全部为零、部分初始化、std::array和std::vector,以及std::vec... 目录1. 静态初始化2. 使用循环初始化3. 全部初始化为零4. 部分初始化5. 使用 std::a

shell编程之函数与数组的使用详解

《shell编程之函数与数组的使用详解》:本文主要介绍shell编程之函数与数组的使用,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录shell函数函数的用法俩个数求和系统资源监控并报警函数函数变量的作用范围函数的参数递归函数shell数组获取数组的长度读取某下的

Python 迭代器和生成器概念及场景分析

《Python迭代器和生成器概念及场景分析》yield是Python中实现惰性计算和协程的核心工具,结合send()、throw()、close()等方法,能够构建高效、灵活的数据流和控制流模型,这... 目录迭代器的介绍自定义迭代器省略的迭代器生产器的介绍yield的普通用法yield的高级用法yidle