物协 C Gateway Course 6 数组
一、一维数组
1. 一维数组的创建和初始化
1.1 数组的创建
在C语言中,一维数组的创建需要指定数组的类型和大小。
int numbers[5]; // 创建一个包含5个整数的一维数组
这声明了一个名为numbers
的整型数组,其中包含5个元素。
1.2数组的初始化
数组的初始化是指在创建数组的同时给数组的内容一些合理初始值。以下是关于数组初始化的一些例子和注意事项:
1.2.1 数组大小和数值个数一致
int arr1[5] = {1, 2, 3, 4, 5};
初始化后各元素的值: 1, 2, 3, 4, 5
1.2.2 数组大小大于初始数值个数
int arr2[6] = {1, 2, 3};
初始化后各元素的值: 1, 2, 3, 0, 0, 0
1.2.3不指定数组大小
int arr3[] = {1, 2, 3, 4};
初始化后各元素的值: 1, 2, 3, 4
1.2.4不指定字符数组大小
char arr5[] = {'a', 'b', 'c'};
初始化后各元素的值: ‘a’, ‘b’, ‘c’
1.2.5字符数组存储字符串
char arr6[] = "abcdef";
初始化后各元素的值: ‘a’, ‘b’, ‘c’, ‘d’, ‘e’, ‘f’, ‘\0’
1.2.6字符数组的大小和字符串字符个数一致时
char arr6[6] = "abcdef";
注意: 这样的初始化是有问题的,因为无法正常读取字符串的结束标志(‘\0’),导致字符串的长度和内容不能得知。
1.2.7字符数组大小大于字符串中的字符数
char arr7[6] = "zxc";
初始化后各元素的值: ‘z’, ‘x’, ‘c’, ‘\0’, 0, 0
结论
- 数组是具有相同类型的集合,数组的大小由元素个数乘以单个元素的大小决定。
- 数组只能整体初始化,不能被整体赋值。只能使用循环从第一个逐个遍历赋值。
- 初始化时,数组的维度或元素个数可忽略,编译器会根据花括号中元素个数初始化数组元素的个数。
- 当花括号中用于初始化值的个数不足数组元素大小时,数组剩下的元素依次用0初始化。
- 字符型数组在计算机内部用对应的ASCII码值进行存储。
- 用双引号引起的字符串,如果不用数组保存,一般都被直接编译到字符常量区,并且不可被修改。
这些注意事项和例子有助于理解数组的初始化过程以及在不同情况下数组元素的赋值规则。
2.一维数组的使用
在C语言中,一维数组是通过下标引用操作符[]
来进行访问的。下面是一段演示一维数组使用的代码:
#include <stdio.h>
int main() {
int arr[10] = {0}; // 数组的不完全初始化
// 计算数组的元素个数
int sz = sizeof(arr) / sizeof(arr[0]);
// 对数组内容赋值,数组是使用下标来访问的,下标从0开始。
for (int i = 0; i < 10; i++) {
arr[i] = i;
}
// 输出数组的内容
for (int i = 0; i < 10; ++i) {
printf("%d ", arr[i]);
}
return 0;
}
注意点:
- 不完全初始化: 在上述代码中,数组
arr
进行了不完全初始化,剩余的元素被默认赋值为0。这在实际应用中是一个常见的做法。 - 计算数组的元素个数: 数组的大小可以通过计算得到,通常使用
sizeof(arr) / sizeof(arr[0])
这种方式。这样做的好处是,如果后续修改数组大小,不需要手动更新计数器。 - 使用下标访问数组: 数组是通过下标来访问的,下标从0开始。在循环中,使用循环变量
i
作为下标,对数组进行赋值和输出。 - 数组大小的建议计算方式: 建议使用
sizeof(arr) / sizeof(arr[0])
来计算数组的大小,以确保代码的可维护性和灵活性。
这段代码演示了一维数组的初始化、赋值和输出过程,以及一些常见的注意事项。通过使用循环和下标引用操作符,可以方便地对数组进行操作。
一维数组的输入
#include <stdio.h>
int main() {
int arr[5]; // 声明一个包含5个整数的一维数组
printf("Enter 5 integers:\n");
// 通过循环提示用户输入,并使用 scanf 将输入存储到数组中
for (int i = 0; i < 5; ++i) {
scanf("%d", &arr[i]);
}
printf("You entered: ");
// 循环输出用户输入的数组内容
for (int i = 0; i < 5; ++i) {
printf("%d ", arr[i]);
}
return 0;
}
-
数组越界检查: 在输入数组元素时,应确保不会越界。即保证输入的元素个数不超过数组的大小。
-
错误处理: 应该对输入的返回值进行检查,确保输入成功。
scanf
函数返回成功读取的项目数,可以用来判断是否成功读取了预期的输入。
3.一维数组在内存中的存储
通过观察下面的代码输出结果,可以了解一维数组在内存中的存储方式:
#include <stdio.h>
int main() {
int arr[10] = {0};
int size = sizeof(arr) / sizeof(arr[0]);
for (int i = 0; i < size; ++i) {
printf("&arr[%d] = %p\n", i, &arr[i]);
}
return 0;
}
输出结果:
&arr[0] = 0x7fff5037d4d0
&arr[1] = 0x7fff5037d4d4
&arr[2] = 0x7fff5037d4d8
&arr[3] = 0x7fff5037d4dc
&arr[4] = 0x7fff5037d4e0
&arr[5] = 0x7fff5037d4e4
&arr[6] = 0x7fff5037d4e8
&arr[7] = 0x7fff5037d4ec
&arr[8] = 0x7fff5037d4f0
&arr[9] = 0x7fff5037d4f4
结论:
-
数组在内存中是连续存放的: 通过观察输出结果,可以看到随着数组下标的增长,元素的地址也在有规律的递增。这表明数组在内存中是线性连续存放的。
-
任何元素的起始地址是开辟字节中最小的: 在C语言中,任何变量(基本变量、指针变量、结构体变量、数组变量)的空间都是整体开辟的,但任何元素的起始地址一定是开辟字节中最小的。在上述输出结果中,
&arr[0]
即为数组的起始地址。
这些观察结论对于理解一维数组在内存中的存储方式非常重要,特别是在涉及指针操作和内存管理时。
二、二维数组
1.二维数组的创建和初始化
1.1二维数组的创建
// 数组创建
int arr[3][4]; // [行数][列数]
char arr[][5];
double arr[2][4];
二维数组的创建时,行数可以忽略不写,而且所有维度的数组其第一个方括号的内容可忽略。
1.2二维数组的初始化
// 数组初始化
int arr[3][4] = {1, 2, 3, 4};
int arr[3][4] = {{1, 2}, {4, 5}};
int arr[][4] = {{2, 3}, {4, 5}};
注意:
- 花括号中的一个花括号代表一个一维数组的初始化。
- 当里面无花括号分组时,按照顺序从第一个开始逐个进行初始化。
- 未被显式赋值的元素将被自动初始化为0。
2.二维数组的使用
二维数组的使用通常通过双重循环嵌套进行索引操作。以下是一段演示二维数组使用的代码:
#include <stdio.h>
int main() {
int arr[3][4] = {0};
int i, j;
// 给二维数组赋值
for (i = 0; i < 3; i++) {
for (j = 0; j < 4; j++) {
arr[i][j] = i * 4 + j;
}
}
// 输出二维数组的内容
for (i = 0; i < 3; i++) {
for (j = 0; j < 4; j++) {
printf("%d ", arr[i][j]);
}
}
return 0;
}
输出结果:
0 1 2 3 4 5 6 7 8 9 10 11
二维数组的输入
#include <stdio.h>
int main() {
int arr[3][4]; // 声明一个3行4列的整数矩阵
printf("Enter 3x4 matrix:\n");
// 嵌套循环用于提示用户输入一个3x4的矩阵,并将输入存储到二维数组中
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 4; ++j) {
scanf("%d", &arr[i][j]);
}
}
printf("You entered:\n");
// 嵌套循环用于输出用户输入的矩阵内容
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 4; ++j) {
printf("%d ", arr[i][j]);
}
printf("\n"); // 换行,显示矩阵的每一行
}
return 0;
}
- 嵌套循环: 由于二维数组有两个维度,通常需要使用嵌套循环来逐个输入元素。
- 合理的提示信息: 提示用户输入时,应该提供清晰明了的信息,以确保用户了解需要输入的内容。
- 数组越界检查: 确保在输入二维数组时不会越界,即保证输入的行数和列数符合数组的实际大小。
- 错误处理: 对输入的返回值进行检查,确保输入成功。可以使用
scanf
的返回值来判断是否成功读取了预期的输入。
3.二维数组在内存中的存储
#include <stdio.h>
int main() {
int arr[3][4];
int i, j;
// 打印二维数组的每个元素的地址
for (i = 0; i < 3; i++) {
for (j = 0; j < 4; j++) {
printf("&arr[%d][%d] = %p\n", i, j, &arr[i][j]);
}
}
return 0;
}
输出结果:
&arr[0][0] = 0x7fffdcc5f630
&arr[0][1] = 0x7fffdcc5f634
&arr[0][2] = 0x7fffdcc5f638
&arr[0][3] = 0x7fffdcc5f63c
&arr[1][0] = 0x7fffdcc5f640
&arr[1][1] = 0x7fffdcc5f644
&arr[1][2] = 0x7fffdcc5f648
&arr[1][3] = 0x7fffdcc5f64c
&arr[2][0] = 0x7fffdcc5f650
&arr[2][1] = 0x7fffdcc5f654
&arr[2][2] = 0x7fffdcc5f658
&arr[2][3] = 0x7fffdcc5f65c
注意:
- 二维数组在内存中也是连续存储的,和一维数组一样,是线性连续且递增的。
- 二维数组本质上也是一维数组,只不过其内部元素是一维数组。每一行实际上是一个一维数组。
- 使用双重循环嵌套进行索引操作是一种常见的方式,通过行和列的循环可以方便地访问和操作二维数组的元素。
三、数组作为函数参数
当数组作为函数参数传递时,数组会发生退化,即被转换为指向数组首元素的指针。这一点在函数参数的声明和使用时需要特别注意。下面分别介绍一维数组和二维数组作为函数参数的情况。
1. 一维数组作为函数参数
#include <stdio.h>
void printArray(int arr[]) {
// 在函数中,arr实际上是一个指向数组首元素的指针
printf("Size of arr in function: %zu\n", sizeof(arr));
}
int main() {
int array[5] = {1, 2, 3, 4, 5};
// 在调用函数时,传递数组名作为参数
printArray(array);
// 在主函数中,数组大小为整个数组的大小
printf("Size of array in main: %zu\n", sizeof(array));
return 0;
}
注意点:
- 在函数中,一维数组形参会被转换为指向数组首元素的指针。
sizeof(arr)
在函数中输出的是指针的大小,而在主函数中输出的是整个数组的大小。
2. 二维数组作为函数参数
#include <stdio.h>
void printMatrix(int arr[][3], int rows) {
// 在函数中,arr实际上是一个指向包含3个整数的一维数组的指针
printf("Size of arr in function: %zu\n", sizeof(arr[0]));
}
int main() {
int matrix[2][3] = {{1, 2, 3}, {4, 5, 6}};
// 在调用函数时,传递二维数组名和行数作为参数
printMatrix(matrix, 2);
// 在主函数中,二维数组大小为整个数组的大小
printf("Size of matrix in main: %zu\n", sizeof(matrix));
return 0;
}
注意点:
- 二维数组作为函数参数时,至少要指定第二维的大小。
- 在函数中,二维数组形参会被转换为指向包含指定大小的一维数组的指针。
sizeof(arr[0])
在函数中输出的是一维数组的大小。
四、数组指针和指针数组
1. 指针数组
指针数组是一个数组,其元素是指针类型。下面是一个简单的例子:
#include <stdio.h>
int main() {
int *p[4]; // 声明一个包含4个指针的数组
int arr1[3] = {1, 2, 3};
int arr2[4] = {2, 4, 6, 8};
int arr3[5] = {0};
int arr4[2] = {2, 2};
p[0] = arr1;
p[1] = arr2;
p[2] = arr3;
p[3] = arr4;
printf("%d\n", *(p[0] + 1)); // 输出 arr1 的第二个元素
printf("%d\n", *(p[1] + 1)); // 输出 arr2 的第二个元素
printf("%d\n", *(p[2] + 1)); // 输出 arr3 的第二个元素
printf("%d\n", *(p[3] + 1)); // 输出 arr4 的第二个元素
return 0;
}
注意点:
- 指针数组中的每个元素是一个指针,可以指向不同类型的数组。
2. 数组指针
数组指针是一个指针,指向数组。下面是一个简单的例子:
#include <stdio.h>
int main() {
int matrix[3][4] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0};
int (*arr)[4] = matrix;
for (int i = 0; i
< 3; i++) {
for (int j = 0; j < 4; j++) {
printf("arr[%d][%d]=%d ", i, j, arr[i][j]);
}
printf("\n");
}
return 0;
}
注意点:
- 数组指针指向一个数组,需要指定数组的第二维大小。
int (*arr)[4]
表示一个指向包含4个整数的一维数组的指针。
这些概念和代码示例应该有助于理解数组作为函数参数、数组指针和指针数组的使用方式。