24 数组基础与应用详解:定义(静态/VLA)、初始化、访问(有效/越界/内存调试)、遍历、大小端字节序
1 数组的概念
1.1 为什么需要数组
在编程过程中,我们常常会遇到一些需要处理大量相关数据的场景,使用传统的一个变量存储一个数据的方式,不仅操作繁琐,还容易出错,此时数组这种数据结构就能很好地解决这些问题。以下是两个典型的需求场景:
需求分析 1:工资统计场景
在现实的企业管理中,统计员工工资是一项常见且重要的工作。假设我们需要统计某公司 50 名员工的工资情况,具体需求可能包括计算全体员工的平均工资、找出最高工资和最低工资等。
如果运用之前所学的编程知识,我们就得为每位员工的工资声明 50 个单独的变量。例如,用 salary1、salary2 一直到 salary50 来分别记录每位员工的工资。在进行平均工资计算时,需要将这 50 个变量相加再除以 50;查找最高工资时,要对这 50 个变量逐一比较。这种方式存在诸多弊端:首先,操作极为繁琐,代码量会随着员工数量的增加而大幅增长;其次,在编写和维护代码的过程中,极易出现变量名拼写错误、遗漏变量等问题,导致程序出错。
因此,我们迫切需要一个能将所有员工工资数据集中存储的容器,以便进行统一管理。通过这个容器,我们可以更便捷地对工资数据进行各种计算和分析,大大提高工作效率和代码的可维护性。
需求分析 2:数据管理场景
除了员工工资统计,在许多其他的数据管理场景中,也面临着类似的问题。例如,在开发一个学生成绩管理系统时,我们需要记录一个班级里众多学生的各科成绩;在处理一个电商平台的商品信息时,要管理大量商品的名称、价格、库存等数据。
如果为每一条学生成绩或商品信息都使用单独的变量来存储,代码将变得异常复杂,后续的修改、扩展和维护都会面临巨大的困难。而如果能将这些相关的数据存储到一个容器中进行统一管理,就可以更高效地对数据进行增删改查等操作,提升程序的整体性能和可扩展性。
容器的概念
在生活中,容器是我们经常接触到的物品,它们具有特定的用途。比如水杯,主要用于装水等液体,方便我们饮用;衣柜用于收纳衣服等物品,使衣物摆放整齐、便于取用;集装箱则用于存放货物,便于货物的运输和存储。
在程序世界里,容器有着类似的功能。它能够将多个数据整合存储在一起,形成一个集合。其中的每一个数据都被称为该容器的元素。数组就是这样一种在程序中常用的容器,它可以存储相同类型的多个元素,让我们能够更方便地对这些数据进行统一管理和操作。
1.2 什么是数组
数组(Array)是一种用于存储多个相同类型数据的数据结构。
它将这些数据按照一定的顺序排列,形成一个集合,并使用一个特定的标识符(即数组名)来命名该集合。
通过编号(索引,也称为下标或角标),我们可以对这些数据进行统一管理和操作。
1.3 数组相关概念
- 数组名:数组名本质上是一个标识符常量,用于在程序中唯一标识一个数组。其命名需要遵循编程语言中标识符的规范,例如不能以数字开头,不能包含除下划线以外的特殊字符,并且区分大小写。
- 元素:同一个数组中的所有元素必须是相同的数据类型,例如整型、浮点型、字符型等。这种数据类型的一致性使得数组在内存中的存储和管理更加高效。
- 下标(索引、角标):数组中的每个元素都有一个与之对应的下标,用于标识元素在数组中的位置。在 C 语言中,下标是从 0 开始的连续数字。例如,一个长度为 5 的数组,其下标范围为 0 到 4。
- 数组的长度:数组的长度表示数组中元素的个数。它是数组的一个重要属性,在声明数组时通常需要指定长度(静态数组),或者在运行时动态确定长度(动态数组)。
1.4 数组的特点
- 内存分配:当创建一个数组时,系统会在内存中开辟一整块连续的空间来存储数组的所有元素。这块空间的大小取决于数组的长度以及数组中元素的数据类型。例如,一个包含 10 个整型元素的数组,由于整型元素通常占用 4 个字节,那么该数组在内存中会占据 40 个字节的连续空间。
- 元素存储:数组中的元素在内存中是依次紧密排列且有序的。这意味着每个元素占用的内存地址是在前一个元素地址的基础上增加一个固定的偏移量,这个偏移量就是元素本身的大小。这种存储方式使得数组具有高效的随机访问能力。
- 长度固定:数组一旦初始化完成,其长度就是确定的,并且在程序的执行过程中不能修改。如果需要动态调整数组的大小,通常需要创建一个新的数组,并将原数组中的元素复制到新数组中。
- 快速访问:由于数组元素在内存中是连续存储的,我们可以直接通过索引(下标)获取指定位置的元素。这种访问方式非常高效,因为计算机可以直接根据数组的起始地址和元素的索引计算出元素的内存地址,从而快速读取或修改该元素的值。
2 数组的定义
2.1 静态数组的定义
在 C 语言中,静态数组是在编译时确定其大小的数组,其定义语法如下:
type arrayName[arraySize];
- type:表示数组中元素的数据类型,例如 int(整型)、float(浮点型)、char(字符型)等。不同的数据类型决定了数组元素在内存中所占用的空间大小。
- arrayName:是数组的名称,它遵循 C 语言的标识符命名规则。标识符不能以数字开头,不能包含除下划线以外的特殊字符,并且区分大小写。
- arraySize:是数组的大小,即数组中元素的数量。对于静态数组,这个大小必须在编译时已知,也就是在编写代码时就需要明确指定数组的大小。
以下是一些静态数组定义的例子:
int numbers[10]; // 定义一个包含 10 个整数的数组,可存储 10 个 int 类型的值
float grades[5]; // 定义一个包含 5 个浮点数的数组,能容纳 5 个 float 类型的值
char name[20]; // 定义一个包含 20 个字符的字符数组,可存储 19 个字符和一个字符串结束符 '\0'
2.2 可变长度数组的定义
在传统的 C 语言中,数组的长度通常是静态确定的,这意味着在编译时就必须知道数组的确切大小。然而,在 C99 标准之后,C 语言引入了可变长度数组(VLA,Variable - Length Array)的概念,允许在运行时确定数组的大小。
不过,需要特别注意的是,VLA 的支持情况存在差异。并不是所有编译器都默认开启对 VLA 的支持,特别是在严格遵循标准模式下(如使用 -std=c99 或 -std=gnu99 选项编译时),虽然 GCC 和 Clang 编译器支持 VLA,但出于代码的可移植性和稳定性考虑,在实际开发中最好避免使用 VLA。
可变长度数组的定义形式如下:
int n = 5; // 在运行时确定数组的大小
int array[n]; // 定义一个大小为 n 的整型数组
使用 VLA 的注意事项:
- 编译器支持:使用 VLA 需要编译器支持 C99 标准或更高版本。例如,在 GCC 编译器中,需要使用 -std=c99 或 -std=gnu99 选项来编译代码,以确保 VLA 特性能够正常使用。
- 作用域限制:VLA 只能在定义它的函数内有效,不能跨越函数调用。这意味着,如果在函数内部定义了一个 VLA,那么在其他函数中是无法直接访问这个 VLA 的。这种限制主要是由于 VLA 的内存分配是在函数栈帧中进行的,当函数执行完毕后,其栈帧会被释放,VLA 所占用的内存也会随之被回收。
3 数组的初始化
在 C 语言中,数组的初始化可以通过以下几种方式实现:
方式一:先定义数组,后逐个赋值
首先指定数组的元素个数和类型,定义一个数组,然后在后续的代码中逐个为数组元素赋值。
// 定义数组,数组名为 arr1,元素类型为 int,元素个数为 3 个
int arr1[3];// 定义完成后再给元素赋值
arr1[0] = 100;
arr1[1] = 200;
arr1[2] = 300;
方式二:定义数组时同时进行初始化
在定义数组的同时,直接通过初始化列表为数组元素赋值。
-
初始化的元素个数与定义的数组长度相等:所有的元素都会被赋予初始化列表中对应的值。
int arr2[3] = {4, 5, 6}; // arr2[0] = 4, arr2[1] = 5, arr2[2] = 6
- 初始化的元素个数少于定义的数组长度:未显式初始化的元素会被自动设置为默认值。
- 对于数值类型的数组,未初始化的元素会被设置为 0;
- 对于字符数组,未初始化的元素会被设置为空字符 \0。
int arr3[5] = {4, 5};
// arr3[0] = 4,arr3[1] = 5,arr3[2] = 0,arr3[3] = 0,arr3[4] = 0char arr4[5] = {'a', 'b'};
// arr4[0] = 'a',arr4[1] = 'b',arr4[2] = '\0',arr4[3] = '\0',arr4[4] = '\0'
- 初始化的元素个数多于定义的数组长度:编译器会给出警告或报错,因为多余的初始化值没有对应的位置存储。
int arr5[3] = {4, 5, 6, 7}; // 报错:初始化元素个数多于数组长度
方式三:省略数组大小,由编译器自动推断
如果在初始化列表中给出了所有元素的值,可以省略方括号中的大小,编译器会根据初始化列表中的元素个数自动推断出数组的大小。
int arr6[] = {7, 8, 9, 10}; // 编译器自动推断数组大小为 4
注意:
不可以对一个已经定义过的数组名进行重新赋值。在 C 语言中,数组名是一个指向数组首元素的常量指针,这意味着它的值(即数组的起始地址)在数组定义时就已经确定,并且在程序的整个生命周期中是不可更改的。
例如,以下代码是非法的:
int arr[5] = {1, 2, 3, 4, 5}; int anotherArr[5] = {6, 7, 8, 9, 10};arr = anotherArr; // 错误:不能对数组名进行赋值
在上面的代码中,arr 是一个数组名,不能直接赋值给另一个数组名 anotherArr。这种操作会导致编译错误,因为数组名是常量指针,不能改变其指向的地址。
4 数组元素访问
4.1 有效访问
在 C 语言中,可以通过 “数组名[下标]” 的语法来访问数组中的元素。下标是一个从 0 开始的整数,表示数组中元素的位置。
数组的下标范围是从 0 到数组长度减 1。例如,对于一个长度为 n 的数组,有效的下标范围是 0 到 n-1。通过这种方式,我们可以精确地定位到数组中的每一个元素,并对其进行读取或修改操作。
以下是一个示例代码,展示了如何有效访问数组元素:
#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[3] = 88;// 再次修改nums[2] = 66;// 注意:不可以直接对数组名进行赋值操作// 因为数组名是一个常量指针,指向数组的首元素,不能改变它的地址。// nums = {100, 200, 300, 400}; // 错误// nums[] = {100, 200, 300, 400}; // 错误// 读取并打印数组中每个元素的值printf("第一个元素的值:%d\n", nums[0]); // 输出第一个元素的值:20(10+10)printf("第二个元素的值:%d\n", nums[1]); // 输出第二个元素的值:40(20+20)printf("第三个元素的值:%d\n", nums[2]); // 输出第三个元素的值:66printf("第四个元素的值:%d\n", nums[3]); // 输出第四个元素的值:88return 0;
}
程序在 VS Code 中的运行结果如下所示:
4.2 越界访问
数组下标必须在指定的范围内使用,即从 0 到数组长度减 1。如果访问的下标超出了这个范围,就称为越界访问(out-of-bounds access)。
越界访问的危险性
越界访问是一种极其危险的操作,它可能导致以下问题:
- 未定义行为:越界访问会导致未定义行为(Undefined Behavior),这意味着程序的行为是不可预测的。程序可能会崩溃,也可能继续执行但产生错误的结果。
- 安全风险:越界访问可能会破坏其他数据或内存区域,从而引发安全漏洞。
- 难以调试:由于越界访问可能导致程序状态的不可预测改变,这使得调试变得更加困难。
以下是一个简单的示例代码,演示了越界访问的危险性:
#include <stdio.h>int main()
{// 定义一个包含 5 个整数的数组,并初始化这些元素为 10, 20, 30, 40, 50int nums[5] = {10, 20, 30, 40, 50};// 修改数组中元素的值nums[0] += 10;nums[1] += 20;nums[2] += 30;nums[3] = 88;nums[4] = 99;// 再次修改nums[2] = 66;// 越界访问,修改到了其它内存空间,属于危险行为nums[6] = 300;// 读取并打印数组中每个元素的值printf("第一个元素的值:%d\n", nums[0]); // 输出第一个元素的值:20(10+10)printf("第二个元素的值:%d\n", nums[1]); // 输出第二个元素的值:40(20+20)printf("第三个元素的值:%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;
}
程序在 VS Code 中的运行结果如下所示:
注意事项
C 语言在越界访问数组时通常不会直接报错(即不会像一些高级语言那样在运行时抛出异常或错误)。这是因为 C 语言是一种低级语言,它直接与硬件交互,提供了对内存的直接访问能力,但同时也要求程序员负责内存的管理和安全。
在 C 语言中,数组是通过指针进行访问的,数组名在表达式中会被转换成指向数组首元素的指针。当通过数组名加上索引来访问数组元素时,实际上是在进行指针运算,即根据索引值计算出目标元素的地址,然后访问该地址处的数据。如果索引超出了数组分配的内存范围,编译器并不会检查这一点,因为 C 语言的设计哲学之一就是 “相信程序员”。因此,编译器会生成直接访问该内存地址的代码,如果那个地址是可访问的(比如没有超出进程的地址空间),程序就会继续执行,但是访问到的数据可能不是预期的,这可能导致数据损坏、程序崩溃或安全漏洞。因此,程序员必须格外小心,确保数组访问始终在合法范围内。
VS Code 调试数组内存
对于上面这个越界访问的程序,为什么输出结果是这样的呢?
我们可以通过打断点来调试运行该程序,观察数组初始化完成后的内存空间,如下所示:
通过调试运行上面的程序,我们不仅能在程序运行过程中观察到数组元素值的变化,还可以深入探究程序在内存中的底层表现。当我们对程序进行调试时,实际上可以访问到与数组对应的二进制数据文件(在调试过程中可以查看内存快照等方式实现)。这些二进制数据以十六进制的形式呈现,它们精确地记录了数组在内存中的存储状态,每一个字节都蕴含着关键信息。
接下来,让我们一同深入分析这个二进制数据文件,揭开数组在内存中存储的神秘面纱,看看正常访问和越界访问在二进制层面究竟有着怎样的表现,二进制数据文件如下所示:
- 正常数组元素:
- nums[0] 对应的内存地址处的值是 14 00 00 00(小端字节序),对应十进制数据为 20(0x00000014)。
- nums[1] 对应的内存地址处的值是 28 00 00 00,对应十进制数据为 40(0x00000028)。
- nums[2] 对应的内存地址处的值是 42 00 00 00,对应十进制数据为 66(0x00000042)。
- nums[3] 对应的内存地址处的值是 58 00 00 00,对应十进制数据为 88(0x00000058)。
- nums[4] 对应的内存地址处的值是 63 00 00 00,对应十进制数据为 99(0x00000063)。
- 越界访问:
- nums[6] = 300:
- 300 的 16 进制表示是 0x0000012C(小端字节序:2C 01 00 00)。在内存中,可以看到 nums[6] 对应的内存位置被修改为 2C 01 00 00。
- 越界访问的输出:
- nums[-1] 和 nums[5] 是越界访问,它们的值是不确定的垃圾值。从内存中可以看到:
- nums[5] 对应的内存位置的值是 F8 01 00 00(小端字节序,转换为十进制是 504),但这不是预期的数组元素值。
- nums[-1] 对应的内存位置的值也是不确定的,从图中可以看到一些随机的字节值。
- nums[-1] 和 nums[5] 是越界访问,它们的值是不确定的垃圾值。从内存中可以看到:
- nums[6] = 300:
5 大端字节序与小端字节序
在计算机系统中,数据的存储方式有字节序之分,主要分为大端字节序(Big - Endian)和小端字节序(Little - Endian)。理解这两种字节序对于正确解读内存中的数据至关重要。
5.1 大端字节序
在大端字节序(Big - Endian)中,数据的高位字节存储在内存的低地址端,而低位字节存储在内存的高地址端。也就是说,从内存的低地址到高地址,数据的字节顺序与它们从高位到低位的顺序相同。这种存储方式就好像我们把一个多位数按照正常的书写顺序从左到右排列,左边是高位,右边是低位,在内存中也是按照这个顺序存储的。
5.2 小端字节序
小端字节序(Little - Endian)与大端字节序相反。在小端字节序中,数据的低位字节存储在内存的低地址端,而高位字节存储在内存的高地址端。这意味着,从内存的低地址到高地址,数据的字节顺序与它们从低位到高位的顺序相同。在大多数现代系统中,使用的是小端序格式,即最低字节在前。
假设有一个 32 位整数 0x12345678,其在大端存储和小端存储下的内存布局如下:
大端存储(Big-Endian):
内存地址:低地址端 -> 高地址端
存储内容:0x12 0x34 0x56 0x78小端存储(Little-Endian):
内存地址:低地址端 -> 高地址端
存储内容:0x78 0x56 0x34 0x12
5.3 对内存数据解读的影响
当我们在内存中看到一组字节数据时,其实际表示的数值依赖于系统的字节序。
例如,当内存空间显示为 2C 01 00 00(从低地址端到高地址端)时:
- 如果系统是大端字节序:这四个字节 2C 01 00 00 表示的是一个 32 位整数(假设是无符号整数)。在大端字节序中,数据的高位字节存储在内存的低地址端,低位字节存储在内存的高地址端。所以,2C 是最高有效字节,01 是次高有效字节,后面的两个 00 分别是次低有效字节和最低有效字节。大端序下这四个字节表示的十六进制数实际是 0x2C010000,转换为十进制就是 738263040。
- 如果系统是小端字节序:同样的四个字节 2C 01 00 00 将表示一个不同的整数。在小端字节序中,数据的低位字节存储在内存的低地址端,高位字节存储在内存的高地址端。所以,00 是最高有效字节(但因为是 0,所以不影响最终数值大小),另一个 00 是次高有效字节(同样不影响数值),01 是次低有效字节,2C 虽然写在低地址端,但在小端序中它是最低有效字节。小端序下这四个字节表示的十六进制数实际是 0x0000012C,转换为十进制就是 300。
6 数组长度计算
在 C 语言中,静态数组的长度(即元素个数)是在数组定义时明确指定且固定的,且在运行时无法直接获取其长度。不过,我们可以通过 sizeof 运算符间接计算出静态数组的长度。然而,当数据来自数据库等动态来源时,由于我们无法在编译时预知数据的实际长度,因此必须显式计算或获取数组(或数据集合)的长度,以确保后续操作(如遍历、处理)的正确性。具体的计算步骤如下:
- 使用 sizeof 运算符计算出整个数组的字节长度:通过 sizeof(array) 可以获取数组在内存中所占用的总字节数。
- 计算数组长度:由于数组成员是同一类型,每个元素占用的字节长度相等,因此用整个数组的字节长度除以单个元素的字节长度,即可得到数组的长度。常用的计算方式为 sizeof(array) / sizeof(array[0]);也可以使用 sizeof(array) / sizeof(数组的基本数据类型) 来计算。
以下是一个示例代码,展示了如何使用 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 是编译时运算符,所以相关变量的值是在编译时就确定的// 对于字面量和变量,sizeof 运算符可以直接作用于它们,括号是可选的,可以省略括号;// 而计算基本数据类型的大小时,必须使用括号将数据类型关键字包裹起来int arrByteLen1 = sizeof nums1; // 可以不加括号int arrByteLen2 = sizeof(nums2); // 加上括号更直观// 用整个数组的字节长度除以数组中单个元素(即 int 类型)的字节长度来计算数组长度// 这里使用强制类型转换 (int) 将 size_t 类型转换为 int 类型,这样可以避免编译器警告int arrLen1 = arrByteLen1 / (int)sizeof nums1[0];int arrLen2 = arrByteLen2 / (int)sizeof(nums1[0]); // 这里使用 nums1 的元素类型,因为都是 int 类型,不影响结果int arrLen3 = arrByteLen2 / (int)sizeof(nums1[1]);// 或者除以数组的基本数据类型int arrLen4 = arrByteLen2 / (int)sizeof(int);// 打印计算出的数组长度printf("数组 nums1 的长度:%d\n", arrLen1); // 输出:数组 nums1 的长度:7printf("数组 nums2 的长度:%d\n", arrLen2); // 输出:数组 nums2 的长度:10printf("数组 nums2 的长度:%d\n", arrLen3); // 输出:数组 nums2 的长度:10printf("数组 nums2 的长度:%d\n", arrLen4); // 输出:数组 nums2 的长度:10return 0;
}
程序在 VS Code 中的运行结果如下所示:
7 数组元素遍历
遍历数组是指按顺序访问数组中的每个元素,以便读取或修改它们。在编程中,一般使用循环结构对数组进行遍历。
7.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 开始,直到小于数组的长度(即最后一个元素的索引,n-1)printf("遍历数组中的元素:\n");for (int i = 0; i < len; i++){printf(" 第%d数组元素的值: %d \n", i + 1, arr[i]);}return 0;
}
程序在 VS Code 中的运行结果如下所示:
7.2 初始化各个元素
我们还可以通过遍历数组完成数组的初始化赋值。下面的案例展示了创建一个长度为 10 的数组,元素依次赋值为 100,200,300,…,1000,并按逆序输出每个元素。
#include <stdio.h>int main()
{// 声明一个整型数组 arr,大小为 10,初始时数组中的元素值未定义int arr[10];int len = sizeof(arr) / sizeof(arr[0]);// 遍历数组进行初始化赋值for (int i = 0; i < len; i++){arr[i] = (i + 1) * 100; // 将数组元素依次赋值为 100, 200, ..., 1000}// 通过遍历逆序输出数组元素// 从数组的最后一个元素开始,直到第一个元素// 注意逆序第一个下标不是 len 而是 len - 1printf("逆序输出数组元素:\n");for (int i = len - 1; i >= 0; i--){printf(" 第%d个元素为:%d\n", i + 1, arr[i]); // 输出:1000 900 800 700 600 500 400 300 200 100}return 0;
}
程序在 VS Code 中的运行结果如下所示:
7.3 计算元素之和与平均数
计算数组中所有元素的和以及平均数。
#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;// 使用 for 循环遍历数组中的每个元素// 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;
}
程序在 VS Code 中的运行结果如下所示:
7.4 获取最值元素
取出数组中值最大和最小的元素及其下标。
#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];// 定义变量存储最大值和最小值及其下标// 初始化最大值为数组的第一个元素,最小值同样为第一个元素,后面通过循环进行比较// 初始化最大值和最小值的下标为 0,后面通过循环进行更新int max = arr[0];int max_index = 0;int min = arr[0];int min_index = 0;// 使用 for 循环遍历数组中的每个元素for (int i = 0; i < len; i++){// 使用 if 语句检查并更新最大值及其下标// 如果当前元素大于当前最大值,则更新最大值和最大值的下标if (arr[i] > max){max = arr[i]; // 更新最大值max_index = i; // 更新最大值的下标}// 检查并更新最小值及其下标// 如果当前元素小于当前最小值,则更新最小值和最小值的下标if (arr[i] < min){min = arr[i]; // 更新最小值min_index = i; // 更新最小值的下标}}printf("最大的元素值:%d,下标:%d\n", max, max_index); // 输出:108, 7printf("最小的元素值:%d,下标:%d\n", min, min_index); // 输出:2, 1return 0;
}
程序在 VS Code 中的运行结果如下所示: