一、一维数组
1. 一维数组的创建方式
程序清单1
#include <stdio.h> int main() { int arr1[10] = { 1,2,3,4,5 }; int arr2[] = { 1,2,3,4,5 }; int arr3[10]; return 0; }
数组 arr1 在定义时,长度给了 10,但初始化未完全。
数组 arr2 在定义时,未明确数组大小,底层根据初始化后提供默认大小。
数组 arr3 只被定义,未初始化,所以底层放的都是随机数字。
程序清单2
下面的三个数组呈现了字符数组的区别。
这里需要注意 sizeof 和 strlen 的区别,sizeof 求的是整个数组内所有元素占内存的大小( 包括 ’ \0 ’),strlen 求的是字符串长度( ’ \0 ’ 之前 )。也就是说,前者求的是数组元素的大小,后者求的是字符串的长度。
#include <stdio.h> #include <string.h> int main() { char arr4[] = "abcd"; char arr5[] = { 'a','b','c','d' }; char arr6[10] = { 'a','b','c','d' }; printf("%d %d\n", sizeof(arr4), strlen(arr4)); return 0; } // 输出结果:5 4
2. 一维数组在内存中的存储方式
#include <stdio.h> int main() { int arr[] = { 1,2,3,4,5,6,7,8,9,10 }; int size = sizeof(arr) / sizeof(arr[0]); for (int i = 0; i < size; i++) { printf("&arr[%d] => %p\n", i, &arr[i]); } return 0; }
输出结果:
从上面的十六进制的地址来看,我们可以得出结论:
① 一维数组在内存中是连续存放的。
② 随着数组下标的增长,地址由低到高变化。
③ 地址之间的差值,即数组元素类型的大小。( 例如:数组存放的元素是整型,那么每个元素的地址之间就相差 4. )
3. 计算一维数组的元素个数
在 C语言 中,sizeof 可以用来计算某个变量的所占内存的字节大小,所以利用【整个数组所占的内存大小】/ 【数组内某个元素所占的内存大小】,就能够得出数组长度。
#include <stdio.h> int main() { int arr[] = { 1,2,3,4,5,6,7,8,9,10 }; printf("%d\n", sizeof(arr)); // 40 printf("%d\n", sizeof(arr[0])); // 4 int size = sizeof(arr) / sizeof(arr[0]); // 求数组长度的方法 printf("%d\n", size); // 10 return 0; }
4. 一维数组打印
#include <stdio.h> // 方法一 void print(int arr[], int size) { for (int i = 0; i < size; i++) { printf("%d ", arr[i]); } } // 方法二 void print2(int* arr, int size) { for (int i = 0; i < size; i++) { printf("%d ", *(arr + i)); } } int main() { int arr[] = { 1,2,3,4,5,6,7,8,9,10 }; int size = sizeof(arr) / sizeof(arr[0]); print2(arr, size); return 0; }
arr 为数组名,即首元素地址 &arr[0],其本质上是一个指针。所以我们可以写成方法二的整型指针方式来接收,每次访问数组的元素时,就往后跳一个元素。即跳跃四个字节。
arr[i] <==> *(arr+i)
二、二维数组
1. 二维数组的创建方式
#include <
#include <stdio.h> int main() { int arr1[3][5] = { 0 }; int arr2[3][5] = { 1,2,3,4,5,6 }; int arr3[][5] = {1,2,3,4,5,6}; int arr4[3][5] = { {1,2}, {3,4}, {5,6} }; //int arr[3][] = { {1,2}, {3,4}, {5,6} }; // error return 0; }
注意事项:
① 二维数组在创建时,行可以省略,列不可省略。
② 如果提前知道二维数组中存储什么元素,推荐上面的 arr4 (直接初始化);如果提前不确定二维数组的元素,推荐上面的 arr1. (初始化第一行第一列的元素,后面的自动初始化为 0.)
2. 计算二维数组的行和列
和计算一维数组的大小思想相同,这里依旧先采用 sizeof 计算内存所占大小,之后分别计算行和列。
#include <stdio.h> int main() { int arr[3][5] = { {1,2}, {3,4}, {5,6} }; // 整个二维数组的大小 / 第一行一维数组大小 int row = sizeof(arr) / sizeof(arr[0]); // 60/20 = 3 // 第一行一维数组大小 / 第一行第一个数组元素的大小 int column = sizeof(arr[0]) / sizeof(arr[0][0]); // 20/4 = 5 return 0; }
3. 二维数组在内存中的存储方式
#include <stdio.h> int main() { int arr[3][5] = { {1,2}, {3,4}, {5,6} }; for (int i = 0; i < 3; i++) { for (int j = 0; j < 5; j++) { printf("&arr[%d][%d] = %p\n", i, j, &arr[i][j]); } printf("\n"); } return 0; }
输出结果:
从上面的十六进制地址的输出结果来看,我们可以得出结论:
二维数组在内存中是连续存放的,即二维数组实际的内存是连续存放的,和我们想象中的几行几列不一样。
4. 二维数组打印
#include <stdio.h> // 方法一 void print(int arr[3][5], int row, int column) { for (int i = 0; i < row; i++) { for (int j = 0; j < column; j++) { printf("%d ", arr[i][j]); } printf("\n"); } } // 方法二 void print2(int (*arr)[5], int row, int column) { for (int i = 0; i < row; i++) { for (int j = 0; j < column; j++) { printf("%d ", *(*(arr+i) + j )); } printf("\n"); } } int main() { int arr[3][5] = { {1,2,3,4,5}, {2,3,4,5,6}, {3,4,5,6,7} }; int row = sizeof(arr) / sizeof(arr[0]); // 行 int column = sizeof(arr[0]) / sizeof(arr[0][0]); // 列 print2(arr, row, column); return 0; }
arr 为数组名,本质上描述的是第一行一维数组的地址。此时 arr + i,表示每次跳过一行。所以当我们利用指针的方式作为形参时,这个指针为数组指针。此时数组指针指向数量为 5个元素的 数组。
arr[i][j] <==> *(arr+i)[j] <==> *(*(arr+i)+j) // 先找行,再找此行的列
三、数组名的含义
结论
对于一维数组来说,数组名就是首元素的地址。
对于二维数组来说,数组名就是第一行数组的地址。
但有两个例外:
① sizeof(数组名),此时数组名表示整个数组,计算的是整个数组占用内存的大小。
② &数组名,此时数组名表示整个数组,取出的是整个数组的地址。
程序清单
#include <stdio.h> int main() { int arr[] = {1,2,3,4,5,6,7,8,9,10}; printf("%p\n", arr); printf("%p\n\n", arr + 1); // 往后跳 4 个字节 printf("%p\n", &arr[0]); printf("%p\n\n", &arr[0] + 1); // 往后跳 4 个字节 printf("%p\n", &arr); printf("%p\n\n", &arr + 1); // 往后跳 40个字节 printf("%d\n", sizeof(arr)); printf("%d\n", sizeof(arr[0])); //printf("%d", sizeof(&arr); // error return 0; }
输出结果:
注意事项:
从上面的四组数据来看,数组名在不同的场景下起到不同的作用。