数组
1. 数组声明和初始化
1.1 数组声明
数组声明的一般形式是:元素类型 数组名[元素个数]。数组对象在内存中连续存放,元素下标从 0 开始。元素个数 必须是能在编译期确定的常量表达式(变长数组除外),并且不能为负数或零。
int a[4]; // 4 个 int 对象
double table[8]; // 8 个 double 对象
char name[32]; // 32 个 char 对象2
3
运行结果:该代码块主要用于语法或结构说明,单独运行通常无终端输出。
如果在声明点能看到初始化器,也可以省略长度,让编译器按初始化器自动推导:
int a[] = {1, 2, 3}; // 长度为 3
char s[] = "mdr"; // 长度为 4,包含 '\0'2
运行结果:该代码块主要用于语法或结构说明,单独运行通常无终端输出。
在项目代码中,声明长度时建议使用具名常量或 enum 常量,而不是裸字面量,这样在需求变更时更容易维护。
1.2 数组初始化
字符串字面量初始化字符数组:可以把字符串字面量放在花括号里,但更多情况下省略。
如果数组的大小未知,那么字符串字面量里面的每个字符(包括空终止字符)用来初始化数组的各个元素;如果数组的大小已知,允许数组大小比字符串字面量的大小少 1,此时数组里无空终止字符。
cchar arr1[] = "mdr"; char arr2[] = { "mdr" }; // arr1 和 arr2 为 { 'm', 'd', 'r', '\0' } char arr3[3] = "mdr"; // arr3 为 { 'm', 'd', 'r' }1
2
3
4
5
6花括号环绕的初始化器列表初始化数组
每个初始化器前面可以带一个形如
[ 下标 ] =的指派符,指定这个初始化器初始化该数组的第 下标 个元素。如果一个初始化器无指派符,如果它是第一个初始化器则初始化下标为零的元素,否则初始化前一个初始化器初始化的元素的后一个元素。如果数组的大小未知,那么初始化器的最大下标会确定数组大小(也就是足够装得下数组里面的元素),同时在初始化过后,数组从不完整类型变成完整类型。
cint arr1[3] = { 1, 2, 3 }; int arr2[] = { [2] = 5 }; // { 0, 0, 5 } int arr3[] = { [2] = 5, 6, 7} // { 0, 0, 5, 6, 7 } int arr4[] = { 1, 2, 3, [2] = 5 }; // { 1, 2, 5 }1
2
3
4用嵌套初始化器初始化数组的数组:
cint arr[3][2] = { {1, 2}, {3, 4}, {5, 6} }; int arr[3][2] = { {1}, {3, 4}, {[1] = 6} }; // { { 1, 0 }, { 3, 4 }, { 0, 6 } }1
2如果嵌套的初始化器外围没有花括号,就会用尽可能多的初始化器依次初始化子数组的各个元素。
cint arr1[3][2] = { 1, {2}, {3, 4} }; // { { 1, 0 }, { 2, 0 }, { 3, 4 } } int arr2[3][2] = { 1, 2, 3, 4 }; // { { 1, 2 }, { 3, 4 }, { 0, 0 } }1
2指派符可以嵌套:
cint arr1[3][2] = { [2][1] = 3 }; // { { 0, 0 }, { 0, 0 }, { 0, 3 } }1
浅触动态内存分配
当数组长度需要在运行时决定时,常见做法是使用动态分配并通过指针访问。此时虽然语法上看起来像“数组”,本质上是“指向首元素的指针 + 长度约束”。如果缺少长度信息,边界检查就无法保证。
#include <stdio.h>
#include <stdlib.h>
int main(void) {
size_t n = 5;
int *arr = malloc(n * sizeof *arr);
if (arr == NULL) {
return 1;
}
for (size_t i = 0; i < n; ++i) {
arr[i] = (int)(i * i);
}
for (size_t i = 0; i < n; ++i) {
printf("%d ", arr[i]);
}
printf("\n");
free(arr);
return 0;
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
可能的输出(示例):
<输出与输入或平台相关,请以实际运行为准>
这种写法和静态数组最大的差别是生命周期管理:静态数组由作用域自动管理,动态分配对象需要显式 free。在真实项目中,推荐把“指针 + 长度”封装成结构体接口,避免散落的边界错误。
习题
10701 [1.3] 输入两个整数 m 和 n(均小于 100),代表矩阵的行数和列数。接着输入两个 m x n 的整数矩阵 A 和 B。计算它们的和矩阵 C = A + B,并输出矩阵 C。
10702 [M4.4**] 输入
,通过割圆术计算圆周率精确到小数点后 位的值。10703 [3.4] 设计程序,模拟“生命游戏”:
- 世界由
个方格组成,每个方格有两种状态:要么有生命,要么无生命; - 每个方格周围的 8 个方格(边 5 个,角 3 个)称为它的“邻居”;
- 在每代演化中,如果某方块有生命且其邻居中的 2 个或 3 个有生命,则该方块在下一代中保持有生命;如果该方块无生命且其邻居中的 3 个有生命,则该方块在下一代中变为有生命;其余情况则保持不变。
首先输入世界大小
;再输入 个数,作为第 0 代时的每个方格的状态;最后输入 作为演化的代数:输出第 代的的演化结果。- 世界由
给定一个正整数 n,计算并输出 n 的阶乘(n!)的值。
10705 [2.2] 输入维数 n,再输入两个 n 维向量在某直角坐标系内的坐标,输出它们的夹角。