C语言 数组
文章目录
目录
文章目录
一、数组的概念
1.1 数组的特点
二、一维数组的声明与初始化
2.1 一维数组的声明
2.2 一维数组的初始化
三、 一维数组的基本用法
3.1 一维数组的下标
3.3 一维数组的遍历与输入输出
四、sizeof计算数组元素个数
五、二维数组的声明与初始化
5.1 二维数组的声明
5.2 二维数组的初始化
六、二维数组的基本用法
6.1 二维数组的下标
6.3 二维数组的遍历与输入输出
七、一二数组在内存中的存储
7.1 一维数组在内存中的存储
7.2 二维数组在内存中的存储
7.3 数组与指针的关系
八、C99中的变长数组
8.1 C99标准之前
8.2 C99标准
8.2.1 变长数组的声明
8.2.2 变长数组的使用示例
8.2.3 变长数组的限制
8.2.4 变长数组在内存中的存储
一、数组的概念
数组是相同数据类型元素的集合,这些元素在内存中连续存储,每个元素可以通过索引(下标)来单独访问。
1.1 数组的特点
1. 所有数组元素具有相同的数据类型。
2. 数组元素在内存中连续存放。
3. 通过下标进行访问数组元素。
4. 数组大小在创建时确定,不能动态改变(除变长数组外)。
二、一维数组的声明与初始化
2.1 一维数组的声明
⼀维数组声明的基本语法如下:
#include <stdio.h>
int main() {type arr_name[常量值]; //type 指定的是数组中存放数据的类型,//可以是:char 、 short 、 int 、 float 等,也可以⾃定义的类型//arr_name 指的是数组名的名字,这个名字根据实际情况,起的有意义就⾏。//[] 中的常量值是⽤来指定数组的⼤⼩的,这个数组的⼤⼩是根据实际的需求指定就⾏。return 0;
}
存放在数组的值被称为数组的元素,数组在创建的时候可以指定数组的⼤⼩和数组的元素类型。
在C语言中,一维数组的声明如下图:
#include <stdio.h>
int main() {// 语法:数据类型 数组名[数组长度];int numbers[10]; // 声明长度为10的整型数组float scores[20]; // 声明长度为20的浮点型数组 char name[50]; // 声明长度为50的字符数组double prices[5]; // 声明长度为5的双精度浮点数组return 0;
}
需要注意的是:数组长度必须是整型常量表达式(C99前)并且数组长度应大于0。
2.2 一维数组的初始化
有时候,数组在创建的时候,我们需要给定⼀些初始值值,这种就称为初始化的。
那数组如何初始化呢?数组的初始化⼀般使⽤⼤括号,将数据放在⼤括号中。
#include <stdio.h>
int main() {// 1. 完全初始化int arr1[5] = { 1, 2, 3, 4, 5 };// 2. 部分初始化,未初始化的元素自动为0int arr2[5] = { 1, 2 }; // {1, 2, 0, 0, 0}// 3. 不指定长度,编译器自动计算int arr3[] = { 1, 2, 3, 4, 5 }; // 自动推断长度为5// 4. 全部初始化为0int arr4[5] = { 0 }; // {0, 0, 0, 0, 0}// 5. 字符数组的特殊初始化char str1[] = "Hello"; // 自动包含'\0'char str2[6] = { 'H', 'e', 'l', 'l', 'o', '\0' };return 0;
}
三、 一维数组的基本用法
3.1 一维数组的下标
C语⾔规定数组是有下标的,下标是从0开始的,到数组长度-1结束,下标就相当于数组元素的编号,如下图:

而在C语⾔中数组的访问提供了⼀个操作符 [ ] ,这个操作符叫:下标引⽤操作符。
有了下标访问操作符,我们就可以轻松的访问到数组的元素了,⽐如我们访问下标为0的元素,我们就可以使⽤ arr[7] ,想要访问下标是2的元素,就可以使⽤ arr[3] 。
而想修改数组里的元素,我们也只需要通过访问它的下标进行修改,比如想修改数组元素2,我们可以使用 arr[1] = 25 进行访问修改,如下代码:
#include <stdio.h>
int main() {int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };// 访问元素printf("第一个元素: arr[0] = %d\n", arr[0]); // 1printf("第三个元素: arr[2] = %d\n", arr[2]); // 3printf("最后一个元素: arr[4] = %d\n", arr[4]); // 5// 修改元素arr[1] = 25;printf("修改后的arr[1] = %d\n", arr[1]); // 25return 0;
}
3.3 一维数组的遍历与输入输出
接下来,如果想要访问整个数组的内容,那怎么办呢?
只要我们产⽣数组所有元素的下标就可以了,那我们使⽤for循环产⽣0~9的下标,接下来使⽤下标访问就⾏了,这就是数组的遍历了, 如下代码:
#include <stdio.h>
int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };int i = 0;for (i = 0; i < 10; i++){printf("%d ", arr[i]);}return 0;
}
明⽩了数组的访问,当然我们也根据需求,⾃⼰给数组输⼊想要的数据,如下:
#include <stdio.h>
int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };int i = 0;for (i = 0; i < 10; i++){scanf("%d", &arr[i]);}for (i = 0; i < 10; i++){printf("%d ", arr[i]);}return 0;
}
四、sizeof计算数组元素个数
在遍历数组的时候,我们经常想知道数组的元素个数,那C语⾔中有办法使⽤程序计算数组元素个数吗?
答案是有的,可以使⽤sizeof关键字。
sizeof 在C语⾔是⼀个关键字,可以计算类型或者变量⼤⼩的
sizeof也可以计算数组的⼤⼩。
如图:
#include <stdio.h>
int main() {int numbers[] = { 2, 4, 6, 8, 10, 12, 14 };// 使用sizeof计算数组长度int length = sizeof(numbers) / sizeof(numbers[0]);printf("数组长度: %d\n", length); // 输出: 7printf("数组总大小: %zu 字节\n", sizeof(numbers)); //28 字节printf("单个元素大小: %zu 字节\n", sizeof(numbers[0])); //4 字节return 0;
}
五、二维数组的声明与初始化
前⾯学习的数组被称为⼀维数组,数组的元素都是内置类型的,如果我们把⼀维数组做为数组的元 素,这时候就是⼆维数组,⼆维数组作为数组元素的数组被称为三维数组,⼆维数组以上的数组统称为多维数组。

5.1 二维数组的声明
二维数组的声明与一维数组的声明差不多,如图:
#include <stdio.h>
int main() {// 语法:数据类型 数组名[行数][列数];int matrix[3][4]; // 3行4列的整型矩阵float grades[5][3]; // 5行3列的浮点数数组char board[8][8]; // 8x8的字符棋盘return 0;
}
5.2 二维数组的初始化
在创建变量或者数组的时候,给定⼀些初始值,被称为初始化。
那⼆维数组如何初始化呢?像⼀维数组⼀样,也是使⽤⼤括号初始化的。
⼆维数组初始化代码如下,有五种方法:
#include <stdio.h>
int main() {// 1. 按行分组初始化int matrix1[3][4] = {{1, 2, 3, 4},{5, 6, 7, 8},{9, 10, 11, 12}};// 2. 连续初始化int matrix2[3][4] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 };// 3. 部分初始化int matrix3[3][4] = {{1, 2}, // 第一行:1, 2, 0, 0{5, 6, 7}, // 第二行:5, 6, 7, 0{9} // 第三行:9, 0, 0, 0};// 4. 省略第一维大小的初始化,初始化时省略⾏,但是不能省略列int matrix4[][4] = {{1, 2, 3, 4},{5, 6, 7, 8},{9, 10, 11, 12}}; // 编译器自动推断行数为3// 5. 全部初始化为0int matrix5[3][4] = { 0 };return 0;
}
需要注意的是:初始化时可以省略⾏,但是不能省略列。
六、二维数组的基本用法
6.1 二维数组的下标
⼆维数组访问也是使⽤下标的形式的,⼆维数组是有⾏和列的,只要锁定了⾏和列就能唯⼀锁定 数组中的⼀个元素。
C语⾔规定,⼆维数组的⾏是从0开始的,列也是从0开始的,如下所⽰:
#include <stdio.h>
int main() {int arr[3][4] = {{1, 2, 3, 4},{1, 2, 3, 4},{1, 2, 3, 4}};return 0;
}

假如我们想要访问三行四列的数组元素,我们只需要
#include <stdio.h>
int main() {int arr[3][4] = {{1, 2, 3, 4},{1, 2, 3, 4},{1, 2, 3, 4}};printf("%d\n", arr[2][3]); //4return 0;
}
6.3 二维数组的遍历与输入输出
我们只要能够按照⼀定的规律产⽣所有的⾏和列的数字就⾏;以上⼀段代码中的arr数组为例,⾏ 的选择范围是0~2,列的取值范围是0~4,所以我们可以借助循环实现⽣成所有的下标。
#include <stdio.h>
int main()
{int arr[3][5] = { 1,2,3,4,5, 2,3,4,5,6, 3,4,5,6,7 };int i = 0;//遍历⾏//输⼊for (i = 0; i < 3; i++) //产⽣⾏号{int j = 0;for (j = 0; j < 5; j++) //产⽣列号{scanf("%d", &arr[i][j]); //输⼊数据}}//输出for (i = 0; i < 3; i++) // 产⽣⾏号{int j = 0;for (j = 0; j < 5; j++) //产⽣列号{printf("%d ", arr[i][j]); //输出数据}printf("\n");}
}
输入输出结果:

七、一二数组在内存中的存储
7.1 一维数组在内存中的存储
一维数组在内存中是连续存储的。例如,声明一个整型数组并且依次打印数组元素的地址:
#include <stdio.h>int main() {int arr[5] = {1, 2, 3, 4, 5};for (int i = 0; i < 5; i++) {printf("arr[%d] 的地址是 %p\n", i, &arr[i]);}return 0;
}
这将在内存中分配连续的空间,足以存储5个整数。每个整数占用的字节数取决于系统(通常是4字节)。因此,如果数组的起始地址是base,那么第i个元素的地址就是base + i * sizeof(int)。
输出结果:

从输出的结果我们分析,数组随着下标的增⻓,地址是由⼩到⼤变化的,并且我们发现每两个相邻的 元素之间相差4(因为⼀个整型是4个字节)。所以我们得出结论:数组在内存中是连续存放的,且相邻元素地址之差为 sizeof(int)。
7.2 二维数组在内存中的存储
二维数组在内存中也是连续存储的。例如,声明一个整型二维数组并且依次打印数组元素的地址:
#include <stdio.h>int main() {int arr[3][4] = {{1, 2, 3, 4},{5, 6, 7, 8},{9, 10, 11, 12}};for (int i = 0; i < 3; i++) {for (int j = 0; j < 4; j++) {printf("arr[%d][%d] 的地址是 %p\n", i, j, &arr[i][j]);}}return 0;
}
输出结果:

从输出的结果来看,每⼀⾏内部的每个元素都是相邻的,地址之间相差4个字节,跨⾏位置处的两个元素(如:arr[0][3]和arr[1][0])之间也是差4个字节,所以⼆维数组中的每个元素都是连续存放的。
得出结论:二维数组元素的地址是连续的,每行内部连续,行与行之间也是连续的。
7.3 数组与指针的关系
这里简略讲一下:
数组名在大多数情况下会被转换为指向其首元素的指针。例如,一维数组爱arr,arr等同于&arr[0]。对于二维数组,arr等同于&arr[0][0],而arr[i]是一个一维数组,它会被转换为指向该行首元素的指针,即&arr[i][0]。
八、C99中的变长数组
8.1 C99标准之前
在C99标准之前,C语⾔在创建数组的时候,数组⼤⼩的指定只能使⽤常量、常量表达式,或者如果我们初始化数据的话,可以省略数组大小。
如下代码:
#include <stdio.h>int main() {int arr1[10];int arr2[3 + 5];int arr3[] = { 1,2,3 };return 0;
}
这样的语法限制,让我们创建数组就不够灵活,有时候数组⼤了浪费空间,有时候数组⼜⼩了不够⽤的。
8.2 C99标准
在C99标准中,引入了变长数组。变长数组的长度可以在运行时确定,而不是在编译时。
8.2.1 变长数组的声明
int n;
printf("请输入数组长度:");
scanf("%d", &n);
int vla[n];
注意,变长数组的长度一旦确定,在数组的生命周期内就不会改变。
8.2.2 变长数组的使用示例
需要注意的是:在VS2022上,虽然⽀持⼤部分C99的语法,但是却没有⽀持C99中的变⻓数组,没法测试;下⾯是在gcc编译器上测试,可以看⼀下。
#include <stdio.h>int main() {int rows, cols;printf("请输入行数和列数:");scanf("%d %d", &rows, &cols);int vla[rows][cols];// 初始化变长数组for (int i = 0; i < rows; i++) {for (int j = 0; j < cols; j++) {vla[i][j] = i * cols + j;}}// 打印变长数组for (int i = 0; i < rows; i++) {for (int j = 0; j < cols; j++) {printf("%d ", vla[i][j]);}printf("\n");}return 0;
}
8.2.3 变长数组的限制
1. 变长数组不能使用静态存储期(即不能是全局变量或使用static关键字)。
2. 变长数组不能初始化,也就是说,在声明时不能使用初始化列表。
3. 变长数组的长度必须是正整数。
8.2.4 变长数组在内存中的存储
变长数组在内存中的存储方式与普通数组相同,都是连续的。但是,由于长度在运行时确定,所以内存分配也是在运行时进行,通常分配在栈上。这意味着如果数组长度很大,可能会导致栈溢出。
