数组

数组

  • 数组是相同数据类型变量的有序集合
    • 数组作为整体需要一个合法的命名(数组名)
    • 数组中的变量没有独立命名,只有在数组中的编号
    • 数组中变量的个数是固定不变的(数组大小固定)

数组的内存布局

  • 数组在计算机底层是一片连续的内存,用于存储数组元素
  • 数组的大小字节数可以用 sizeof 获取(单位:字节)

计算数组的大小

  • type Name[] = {V0, V1, V2, …, Vn,} // 共 n 个元素
  • sizeof(Name) / sizeof(Name[0]); // 计算结果为 n

注意

  • 数组名只能当作左值使用(可以看作是常量)
  • 只能使用整形常量对数组大小进行定义
    • 某些编译器可能支持,是因为编译器本身作了优化,c语言本身是不支持的
  • 只能使用整形值作为下标访问数组元素
1
2
int size = 8;
int arr[size]; // Error

数组的类型

1
2
3
4
5
6
7
8
// 这是两个不同类型的数组
int a[10] = {0}; // 类型为:int[10]
int b[5] = {1}; // 类型为:int[5]
int i = 0;

for(i = 0; i < 5; i++){
a[i] = b[i];
}

上述操作中,按理说是不符合类型的赋值规则的,因为不同类型之间进行输入传递是有可能出现问题的;

在上述赋值操作中,仅仅改变了 a 数组中的前五个变量,这与赋值操作的定义相违背(赋值操作指的是改变所保存的内容,这个改变是完全的改变);

因此从严格意义上来说是有问题的,但并没有影响程序的运行;

二维数组

  • 二维数组能且仅能让编译器自动确定第一维的大小
  • 第二维大小必须显示给定,即:数组元素的类型必须正确合法
  • 第一维大小自动确定的方法:(初始值个数 除以 第二维大小)向上取整
1
2
3
int a[][3] = {1, 2, 3, 4, 5};
// 第一维确定方法:4 / 3 = 2
int s1 = sizeof(a) / sizeof(a[0]) // 24 / 12 = 2

当你声明一个二维数组时,如 int arr[M][N];,这里:

  • M 是外层数组的大小,即这个二维数组包含多少个一维数组。
  • N 是内层数组(即每个一维数组)的大小,也就是每个一维数组中有多少个元素。

为什么需要指定第二维的大小?

  1. 内存分配:编译器需要知道如何为二维数组分配足够的连续内存空间。通过指定 N,编译器可以计算出整个二维数组需要多少字节的内存(M * N * sizeof(int) 对于 int 类型的二维数组)。如果 N 没有被指定,编译器就无法确定每个内部数组的确切大小,进而无法计算整个二维数组所需的总内存量。
  2. 索引计算:在 C 语言中,二维数组的索引是通过 arr[i][j] 这样的形式来访问的。当你知道 N 的值时,编译器可以很容易地计算出 j 索引对应的元素在内存中的偏移量(即 j 乘以每个内部数组的大小,即 N * sizeof(int))。如果 N 没有被指定,这种索引计算就无法进行。
  3. 类型检查:在编译时,指定 N 还允许编译器对数组索引进行类型检查,确保索引不会超出数组的边界。