news 2026/8/24 13:46:39

学 C 语言数组别再混淆:下标、初始化、二维行优先存储详解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
学 C 语言数组别再混淆:下标、初始化、二维行优先存储详解

数组概述

为什么需要数组?

用一块连续的内存空间,一次性管理多个相同类型的数据。

什么是数组

定义:数组是相同类型,有序数据的集合。

数组的特征

  • 数组中的每个数据称为元素(也就是匿名的变量空间),所有元素类型相同。

  • 数组中的元素存放在连续的内存空间中(char player_name[6])申请在内存中开辟6块连续的基于char类型的变量空间。

衍生概念:下标(索引)
  • 下标或者索引代表了数组中元素距离第一个元素的偏移量(本质是每个元素首地址距离第一个元素首地址的偏移量。)

下标 = (每个元素首地址 - 第一个元素首地址)/数组元素类型大小

  • 数组的下标是从0开始:

    数组的最大下标 = 数组的元素个数(数组容量或大小)-1

  • int a:在内存中开辟一个空间,该空间的大小是4个字节

  • int a1,a2,a3,a4,a5;:在内存中开辟连续的5块内存,每一块空间的大小是4个字节,通过变量名访问

  • int arr[5];在内存中开辟连续的5块内存,每一块空间的大小是4个字节,通过数组下标访问(因为数组中的元素都是匿名变量)

一维数组

数组的定义

语法:

数据类型 数组名[数组容量];

注意:数据类型又被称作类型说明符,数组容量又被称作数组元素个数或者数组的大小/长度

说明:

  • 数组的数据类型由数组中的元素来决定。也就是元素是什么类型,数组就是什么类型,同一个数组中,所有元素的类型都是一致的

  • 数组名也是标识符,即数据类型为数组的变量名。命名规范与变量相同,区别是变量一般用单数、数组用复数。

  • 数组容量还可以叫常量表达式,其值必须是整数。关于数组容量的类型:

    • C89标准:只支持常量和符号常量,不支持变量

      #define SIZE 5 // 符号常量,使用无参宏定义 int length = 5; // 变量 int arr[5]; // 正确,支持常量(字面量) int arr2[SIZE]; // 正确,支持符号常量 int arr3[length]; // 错误,C89不支持变量
    • C99标准:引入变长数组(VLA: MSVC(微软编译器)对C99的VLA支持有限,GCC/Clong等编译器支持较好),就是可以使用变量,数组在运行的时候决定大小,举例:

      int length = 5; // 创建变量,赋值5 int arr[length]; // C99标准,GCC/Clong支持较好,此时数组大小为5 length = 10; //

类型:代表了数组中元素的类型,数组的空间大小 = 数组中所有空间元素之和

容量:数组中能存多少个元素,数组容量一定是一个整型

深入理解:

  1. 定义一个数组,相当于申请了一个可以容纳指定元素个数的内存单元,所申请的内存是连续的。

  2. 定义一个数组,相当于定义了多个匿名变量,这些变量因为匿名,所以需要通过数组名[下标]来访问。

范例:

// 定义一个数组 int arr[10]; // 定义一个存储10个int类型元素的数组

数组元素的访问

原则:数组中的元素不能一次性访问所有,只能一个一个访问。

语法:

  • 取值:

    [数据类型 变量名] = 数组名[下标];
  • 赋值:

    数组名[下标] = 值;

举例:

// 所有举例都是基于局部作用域 ​ // 定义一个存储10个元素的int数组 int arr[10]; ​ // 给数组的第一个元素赋值 arr[0] = 88; ​ // int a = arr[0]; // a = 88

案例:一维数组的遍历

  • 需求:利用循环给数组元素a[0]~a[9]赋值0~9,并且要逆序输出。

  • 代码:

    /************************************************************************* > File Name: demo04.c > Author: *** > Description: 一维数组的遍历 > Created Time: 2026年08月08日 星期六 21时29分37秒 ************************************************************************/ ​ #include <stdio.h> ​ int main(int argc,char *argv[]) { // 创建一个数组,用来存储0~9 int arr[10]; ​ // 计算数组的大小 int len = sizeof(arr) / sizeof(arr[0]); ​ // 定义循环变量,同时是数组的下标,从0开始 int i; ​ // 通过for循环给数组元素赋值 for (i = 0; i < len; i++) arr[i] = i; ​ // 逆序输出数组中的元素 for (i = len - 1; i >= 0; i--) printf("%-4d", arr[i]); ​ printf("\n"); ​ return 0; }

    运行结果:

    an-vm:/mnt/hgfs/code/20260806$ gcc demo04.c an-vm:/mnt/hgfs/code/20260806$ ./a.out 9 8 7 6 5 4 3 2 1 0

数组的初始化

说明:所谓的初始化,就是定义数组的时候,用指定的数据给对应的元素赋值

语法:

数据类型 数组名[数组容量] = {...};

注意事项:

  • 数组可以部分初始化:也就是可以给数组中的前几个元素初始化,未初始化的元素系统将自动初始化,初始值是0

  • 数组根据初始化的元素自动分配大小:

    // 由初始化的元素来决定数组的容量 int arr[]; // 错误,未初始化,必须指定 int arr[] = {11,12,13,14,15}; // 正确,推荐,根据初始化的

排序算法:冒泡排序

排序思想(向前冒泡)

  • 对一组数列一次只排好一个元素,针对n个元素,最差情况需要n-1次就可以排好;最好情况需要0次(只需要1轮遍历,无交换操作)

  • 每次排序假定第一个元素是最大或者最小的。然后相邻的两个元素进行比较,遇到较大或者较小的元素进行交换,访问完数组的最后一个元素,就拍好了一个数。

  • 在余下的元素中,再次应用第2步操作,直到只剩下一个数。

排序推理

例如:将5,4,3,2,1冒泡排序为1,2,3,4,5

排序演示

第⓪轮:5,4,3,2,1 → 4,3,2,1,5比较4次 = 数组长度5 - 轮数0 - 1

第①轮:4,3,2,1,5→ 3,2,1,4,5比较3次 = 数组长度5 - 轮数1 - 1

第②轮:3,2,1,4,5→ 2,1,3,4,5比较2次 = 数组长度5 - 轮数2 - 1

第③轮:2,1,3,4,5→ 1,2,3,4,5比较1次 = 数组长度5 - 轮数3 - 1

总结

  • 案例涉及到5个数列的排序,排序了4轮,得到:轮数 = 数组长度 - 1,我们可以通过外层的for循环实现轮数的遍历。

  • 案例涉及到每一轮中数列的排序次数,得到:次数 = 数组长度 - 轮数 [- 1],我们都可以通过一个内层的for循环实现每一轮的次数的遍历。

  • 每一次比较过程中,两个数涉及到位置的交换,比如 a = 3, b = 4, 交换a,b的数据变为 a = 4, b = 3,应该如何实现:

    • 引入一个临时变量temp,将a的值赋值给temp, int temp = a;

    • 将b的值赋值给a, a = b;

    • 将temp的值赋值给b,b = temp;

代码实现:

/************************************************************************* > File Name: demo03.c > Author: 李昕怡 > Description: 冒泡排序 > Created Time: 2026年08月09日 星期日 13时01分17秒 ************************************************************************/ ​ #include <stdio.h> ​ // 定义符号常量,用来控制是升序还是降序,0-升序/非0-降序,默人为升序 #define SORT_DESC 0 /** * 冒泡排序(向前冒泡) */ void test1() { // 创建一个数组,用来存放冒泡排序用的数列 int arr[10]; // 定义循环变量i和j,分别控制轮数和次数 int i, j; // 定义一个临时变量,用来存储交换的值 int temp; ​ // 获取数组的大小 int len = sizeof(arr) / sizeof(arr[0]); ​ printf("请输入%d个整数\n",len); // 通过for循环,为数组元素赋值 for (i = 0;i < len; i++) scanf("%d", &arr[i]); ​ printf("\n排序前:"); for (i = 0; i < len; i++) printf("%-4d", arr[i]); printf("\n"); ​ // 外层循环:实现轮数的遍历:轮数 = 数组容量 - 1 for (i = 0; i < len - 1; i++) { // 记录一个标志位 int flag = 0; // 内层循环:实现每一轮比较次数的遍历:次数 = 数组长度 - 轮数 - 1 for (j = 0; j < len - i - 1; j++) { // 降序排序 if(SORT_DESC) { if(arr[j] < arr[j+1]) { temp = arr[j]; arr[j] = arr[j+1]; arr[j+1] = temp; flag = 1; } } // 升序排序 else { if (arr[j] > arr [j+1]) { temp = arr[j]; arr[j] = arr[j+1]; arr[j+1] = temp; flag = 1; } } } ​ if(!flag) break; ​ } // 排序完后,再次遍历 printf("\n排序后:"); for (i = 0; i < len; i++) printf("%-4d", arr[i]); printf("\n"); } ​ int main(int argc,char *argv[]) { test1(); return 0; }

运行结果:

yueqian@yueqian-vm:/mnt/hgfs/code/20260807$ gcc demo03.c yueqian@yueqian-vm:/mnt/hgfs/code/20260807$ ./a.out 请输入10个整数 1 5 3 7 3 9 6 11 54 2 ​ 排序前:1 5 3 7 3 9 6 11 54 2 ​ 排序后:1 2 3 3 5 6 7 9 11 54

二维数组

定义

二维数组本质上是数组的数组,即外层数组的每个元素都是一个一维数组,由行和列两部分构成,属于多维数组。

可以按照先行后列的方式解读。

二维数组可被视作一个特殊的一维数组,也就是说,当一个数组的每一个元素都是一维数组的时候,那么这个数组就是二维数组。

语法

数据类型 数组名[行容量][列容量];

行容量:外层数组的数组容量,也就是二维数组中一维数组的个数(也就是二维数组中有几个一维数组)

列容量:内层数组的数组容量,也就是一维数组中元素的个数(见一维数组)

说明

  • 二维数组在初始化的时候,可以省略行容量,系统会通过初始化后的数据自动推断行容量

  • 二维数组和一维数组一样,可以部分初始化,未初始化部分的元素使用0填充

  • 二维数组在初始化的时候,不能省略列容量,否则编译报错

初始化

特殊写法:

  • 下标可以是整型表达式,如arr[2-1][2*2-1]等价于arr[1][3]

  • 下标可以是已经有值的变量或者数组的元素。如arr[2*x-1][b[3][1]][]中最终需要的是一个>=0且小于数组对应维度容量的整数。

  • 数组元素可以出现在表达式,如b[1][2] = a[2][3]/2

  • 演示:

    数组:arr列-0列-1列-2举例说明
    行-0111213arr[0][1]数组arr的0行1列对应的元素12
    行-1212223arr[1][2]数组arr的1行2列对应的元素23

    注意:使用数组元素的下标应在已定义数组的大小范围内;应注意区别定义数组大小和引用数组元素的区别。

内存存储

C语言中二维数组按行优先存储:先连续存储第0行的所有列元素,再存储第1行的所有列元素,以此类推;反映在下标上,行下标(第一维)变化慢,列下标(第二维)变化快(例如arr[2][3],先填满arr[0][0]arr[0][1]arr[0][2],再到arr[1][0]arr[1][1]arr[1][2])。

应用场合

主要是应用对行列有要求的情况,比如说我们现在要存储西安粤嵌所有的在班学生的成绩。

还有就是字符数组的应用,比如用数组存储学生的姓名。char names[50][20] = {"张三","李四","王五"};

  1. 存放一个班的所有学生的成绩,使用一维数组。举例:double scores[50] = {...};

  2. 存放五个班的所有学生的成绩,使用二维数组。举例:double scores[5][50] = {...};

  3. 存放6个校区的所有学生的成绩,使用三维数组。举例:double scores[6][5][50] = {{{}},{{}}}

案例1:二维数组的遍历

  • 分析:

    • 二维数组本质上属于行列式,遍历的时候需要借助于嵌套的for循环,外层for负责行的遍历,内层的for负责列的遍历。

  • 取值:

    [数据类型 变量名 =] 数组名[行下标][列下标];
  • 赋值:

    数组名[行下标][列下标] = 值;
  • 行和列的容量计算:

    // 计算行的大小 int row_len = sizeof(数组名) / sizeof(数组名[行下标0]); // 二维数组中,一维数组的个数 // 计算列的大小 int col_len = sizeof(数组名[行下标0]) / sizeof(数组名[行下标0][列下标0]);
  • 代码:

    /************************************************************************* > File Name: demo04.c > Author: 李昕怡 > Description: 二维数组的遍历 > Created Time: 2026年08月16日 星期日 12时58分21秒 ************************************************************************/ ​ #include <stdio.h> ​ int main(int argc,char *argv[]) { // 创建一个二维数组,用来存储测试数据 int arr[][3] = {{11},{22,33},{44,55,66}}; ​ // 计算行容量和列容量 int row_len = sizeof(arr) / sizeof(arr[0]); int col_len = sizeof(arr[0]) / sizeof(arr[0][0]); ​ // 外循环:遍历行 for (int i = 0; i < row_len; i++) { // 内循环:遍历列 for (int j = 0; j < col_len; j++) { printf("%-4d", arr[i][j]); } printf("\n"); } printf("\n"); ​ return 0; }
  • 运行结果:

    yueqian@yueqian-vm:/mnt/hgfs/code/20260807$ gcc demo04.c yueqian@yueqian-vm:/mnt/hgfs/code/20260807$ ./a.out 11 0 0 22 33 0 44 55 66

案例2:矩阵的转置

  • 分析:

    • 所谓矩阵的转置:就是原本的行变列,列变行

  • 代码:

    /************************************************************************* > File Name: demo05.c > Author: 李昕怡 > Description: 二维数组:矩阵的转置 > Created Time: 2026年08月16日 星期日 13时10分58秒 ************************************************************************/ ​ #include <stdio.h> ​ #define ROW 2 #define COL 3 ​ int main(int argc,char *argv[]) { // 定义循环变量 int i, j; ​ // 准备两个数组,用来存储转置前后的数据 int arr_before[ROW][COL] = {11,12,13,21,22,23}; int arr_after[COL][ROW] = {0}; ​ // 计算数组的大小 int arr_before_row = sizeof(arr_before) / sizeof(arr_before[0]); int arr_before_col = sizeof(arr_before[0]) / sizeof(arr_before[0][0]); ​ int arr_after_row = sizeof(arr_after) / sizeof(arr_after[0]); int arr_after_col = sizeof(arr_after[0]) / sizeof(arr_after[0][0]); ​ // 使用双重for循环遍历二维数组 printf("转置前:\n"); // 遍历行 for (i = 0; i < arr_before_row; i++) { // 遍历列 for (j = 0; j < arr_before_col; j++) { // 打印转置前的数据 printf("%-4d", arr_before[i][j]); // 转置:行便列,列变行 arr_after[j][i] = arr_before[i][j]; } printf("\n"); } ​ printf("转置后:\n"); // 遍历行 for (i = 0; i < arr_after_row; i++) { // 遍历列 for (j = 0; j < arr_after_col; j++) { // 打印转置前的数据 printf("%-4d", arr_after[i][j]); } printf("\n"); } ​ return 0; }
  • 运行结果:

    yueqian@yueqian-vm:/mnt/hgfs/code/20260807$ gcc demo05.c yueqian@yueqian-vm:/mnt/hgfs/code/20260807$ ./a.out 转置前: 11 12 13 21 22 23 转置后: 11 21 12 22 13 23

字符数组

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/24 13:46:34

2026/08/16 AI学习笔记

一 . Ollama学习笔记1. ollama概述Ollama 可以理解成“本地版的 OpenAI API”&#xff0c;可以直接调用&#xff0c;不需要通过互联网。没有部署的:你的电脑│▼ Spring AI│▼ 互联网│▼ DeepSeek / OpenAI / 豆包│▼ 返回结果部署ollama的:你的电脑│▼ Spring AI│▼ Olla…

作者头像 李华
网站建设 2026/8/24 13:45:40

双脉冲开关测试中考虑电路寄生影响的波形分析

参考文献&#xff1a;Performance Comparison of Standalone 4H-SiC JFETs and Cascode Devices at Cryogenic Temperature 在双脉冲测试中&#xff0c;器件开关波形受到功率回路寄生电感、续流二极管结电容、器件非线性结电容、封装键合线和内部栅极网络的共同影响。开通过程中…

作者头像 李华
网站建设 2026/8/24 13:45:38

鱼眼相机标定核心参数解析,可定制开发各种视觉测量算法

鱼眼相机标定旨在确定其独特的内部参数和畸变模型&#xff0c;以校正图像并实现精确的几何测量。其核心在于使用更复杂的模型来描述大视场角镜头引入的严重径向畸变。 一、鱼眼相机模型与标定参数 鱼眼相机模型通常使用等距投影模型&#xff0c;其标定参数与普通透视相机有显…

作者头像 李华
网站建设 2026/8/24 13:43:08

如何向 AI 清楚描述一个编程需求

文章目录一、为什么“帮我写代码”通常不够清楚二、描述需求的第一个要素&#xff1a;背景背景通常包含什么为什么背景很重要背景不要写得过多三、描述需求的第二个要素&#xff1a;目标不够具体的目标更清楚的目标目标最好包含“做什么”和“不做什么”四、描述需求的第三个要…

作者头像 李华
网站建设 2026/8/24 13:39:03

Arteris session 1 — NoC Concept and Architecture- 总结

引言 随着现代片上系统(SoC)的复杂度不断提升,传统的总线互连架构在带宽、延迟和可扩展性方面面临严峻挑战。片上网络(Network on Chip, NoC)作为一种分布式、可扩展的互连解决方案,已成为高性能SoC设计的核心。本文基于Arteris的培训资料,系统梳理NoC的核心概念、架构…

作者头像 李华
网站建设 2026/8/24 13:37:58

Android多渠道打包全攻略:从原理到Walle/VasDolly实战避坑

1. 项目背景与核心价值如果你在Android开发团队里待过&#xff0c;或者自己发布过App&#xff0c;大概率都听过“多渠道打包”这个词。听起来挺高大上&#xff0c;但说白了&#xff0c;就是给同一个App包&#xff0c;打上不同的“身份标签”&#xff0c;然后分发给不同的应用商…

作者头像 李华