数组的基础概念
-
定义与声明
(图片来源网络,侵删)- 语法:
数据类型 数组名[数组长度]; - 说明: 数组在内存中是连续存储的,数组的索引从 0 开始。
- 示例:
int numbers[5]; // 定义一个可以存放5个整数的数组 char name[10]; // 定义一个可以存放10个字符的数组 float scores[30]; // 定义一个可以存放30个浮点数的数组
- 语法:
-
初始化
- 完全初始化: 在声明时为数组的所有元素赋值。
int a[5] = {1, 2, 3, 4, 5}; // 如果省略长度,编译器会自动根据初始化列表的个数确定数组长度 int b[] = {10, 20, 30}; // b的长度为3 - 部分初始化: 只为前几个元素赋值,其余元素自动初始化为0。
int c[5] = {1, 2}; // c[0]=1, c[1]=2, c[2]=0, c[3]=0, c[4]=0 - 全部初始化为0:
int d[5] = {0}; // 所有元素都被初始化为0
- 完全初始化: 在声明时为数组的所有元素赋值。
数组的常见操作
遍历数组
这是最基本也是最重要的操作,即访问数组中的每一个元素。
-
使用
for循环 (最常用)int arr[] = {10, 20, 30, 40, 50}; int length = sizeof(arr) / sizeof(arr[0]); // 计算数组长度 printf("使用 for 循环遍历:\n"); for (int i = 0; i < length; i++) { printf("arr[%d] = %d\n", i, arr[i]); } -
使用
while循环
(图片来源网络,侵删)int j = 0; printf("\n使用 while 循环遍历:\n"); while (j < length) { printf("arr[%d] = %d\n", j, arr[j]); j++; }
访问与修改元素
通过索引(下标)可以直接访问或修改数组中的元素。
int scores[3] = {88, 92, 75};
// 访问元素
printf("第一个学生的分数是: %d\n", scores[0]); // 输出 88
// 修改元素
scores[2] = 85; // 将第三个学生的分数从75改为85
printf("修改后的第三个学生的分数是: %d\n", scores[2]); // 输出 85
插入元素
插入元素比其他操作复杂,因为数组长度固定,通常需要创建一个新数组或移动现有元素。
-
在指定位置插入一个元素 (移动后续元素)
#include <stdio.h> int main() { int arr[10] = {1, 2, 4, 5, 6}; // 假设数组已使用5个位置 int length = 5; int pos = 2; // 在索引2的位置插入 int value = 3; // 要插入的值 // 1. 检查数组是否已满和位置是否合法 if (length >= 10 || pos < 0 || pos > length) { printf("插入失败!\n"); return 1; } // 2. 从后向前移动元素,为新元素腾出空间 for (int i = length; i > pos; i--) { arr[i] = arr[i - 1]; } // 3. 插入新元素 arr[pos] = value; // 4. 更新数组长度 length++; // 打印结果 printf("插入后的数组: "); for (int i = 0; i < length; i++) { printf("%d ", arr[i]); } printf("\n"); return 0; } // 输出: 插入后的数组: 1 2 3 4 5 6
删除元素
删除元素也需要移动元素来填补空缺。
-
删除指定位置的元素
#include <stdio.h> int main() { int arr[10] = {1, 2, 3, 4, 5, 6}; // 假设数组已使用6个位置 int length = 6; int pos = 2; // 删除索引2的元素 // 1. 检查位置是否合法 if (pos < 0 || pos >= length) { printf("删除失败!\n"); return 1; } // 2. 从前向后移动元素,覆盖被删除的元素 for (int i = pos; i < length - 1; i++) { arr[i] = arr[i + 1]; } // 3. 更新数组长度 length--; // 打印结果 printf("删除后的数组: "); for (int i = 0; i < length; i++) { printf("%d ", arr[i]); } printf("\n"); return 0; } // 输出: 删除后的数组: 1 2 4 5 6
查找元素
-
顺序查找 (适用于无序数组) 从头到尾依次查找,直到找到目标元素或遍历完整个数组。
int arr[] = {15, 8, 25, 36, 42}; int target = 25; int index = -1; // 假设-1表示未找到 for (int i = 0; i < 5; i++) { if (arr[i] == target) { index = i; break; // 找到后立即退出循环 } } if (index != -1) { printf("元素 %d 的索引是: %d\n", target, index); } else { printf("未找到元素 %d\n", target); } -
二分查找 (适用于有序数组) 查找效率更高,但要求数组必须是有序的(升序或降序)。
#include <stdio.h> int binarySearch(int arr[], int size, int target) { int left = 0; int right = size - 1; while (left <= right) { int mid = left + (right - left) / 2; // 防止溢出 if (arr[mid] == target) { return mid; } else if (arr[mid] < target) { left = mid + 1; // 在右半部分查找 } else { right = mid - 1; // 在左半部分查找 } } return -1; // 未找到 } int main() { int arr[] = {2, 5, 8, 12, 16, 23, 38, 56}; int size = sizeof(arr) / sizeof(arr[0]); int target = 16; int result = binarySearch(arr, size, target); if (result != -1) { printf("元素 %d 的索引是: %d\n", target, result); } else { printf("未找到元素 %d\n", target); } return 0; }
排序数组
排序是数组操作中的核心内容。
-
冒泡排序 (简单易懂,但效率较低)
#include <stdio.h> void bubbleSort(int arr[], int size) { for (int i = 0; i < size - 1; i++) { // 每一轮内层循环,都会将最大的元素“冒泡”到最后 for (int j = 0; j < size - 1 - i; j++) { if (arr[j] > arr[j + 1]) { // 交换两个元素 int temp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = temp; } } } } int main() { int arr[] = {64, 34, 25, 12, 22, 11, 90}; int size = sizeof(arr) / sizeof(arr[0]); bubbleSort(arr, size); printf("冒泡排序后的数组: "); for (int i = 0; i < size; i++) { printf("%d ", arr[i]); } printf("\n"); return 0; } -
选择排序 (思路简单) 每次从未排序的部分找到最小(或最大)的元素,放到已排序部分的末尾。
多维数组
最常见的多维数组是二维数组,可以看作是“数组的数组”。
-
定义与初始化
// 定义一个3行4列的整型二维数组 int matrix[3][4]; // 初始化 int matrix2[3][4] = { {1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12} }; // 部分初始化 int matrix3[3][4] = {{1}, {5, 6}}; // 第一行 {1,0,0,0}, 第二行 {5,6,0,0}, 第三行 {0,0,0,0} -
访问与遍历 使用双重循环进行遍历,外层循环控制行,内层循环控制列。
int matrix[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("%d ", matrix[i][j]); } printf("\n"); // 每行结束后换行 }
数组作为函数参数
将数组传递给函数时,传递的是数组首元素的地址,而不是整个数组的副本,这被称为“地址传递”或“引用传递”。
-
形参的写法
void func(int arr[])void func(int arr[10])// 方括号中的数字通常被忽略void func(int *arr)// 指针写法,与数组写法等价
-
关键点:传递数组长度 由于函数无法知道传入数组的实际长度,所以必须额外传递一个表示数组大小的参数。
#include <stdio.h> // 函数声明:接受一个整型数组和它的大小 void printArray(int arr[], int size); int main() { int myNumbers[] = {10, 20, 30, 40, 50}; int size = sizeof(myNumbers) / sizeof(myNumbers[0]); printArray(myNumbers, size); // 传递数组名和长度 return 0; } // 函数定义 void printArray(int arr[], int size) { printf("函数接收到的数组: "); for (int i = 0; i < size; i++) { printf("%d ", arr[i]); } printf("\n"); } -
数组作为返回值 C语言不允许直接返回一个完整的数组,你可以返回一个指向数组首元素的指针。
#include <stdio.h> // 返回一个整型指针 int* getArray() { static int arr[5] = {1, 2, 3, 4, 5}; // 必须是静态变量或全局变量,否则函数返回后内存会被释放 return arr; } int main() { int *ptr; ptr = getArray(); printf("从函数返回的数组: "); for (int i = 0; i < 5; i++) { printf("%d ", *(ptr + i)); // 使用指针算术访问 } printf("\n"); return 0; }
字符数组与字符串
在C语言中,字符串通常被实现为字符数组,一个关键的区别是,字符串必须以一个空字符 定义与初始化 常用字符串函数 (需要包含 数组越界
访问数组索引范围之外的元素是未定义行为,可能导致程序崩溃或数据损坏。 忘记计算数组长度
在函数中处理数组时,一定要记得传递数组长度,或者使用 混淆数组和指针
虽然数组名在多数情况下会“退化”为指向首元素的指针,但它们本质不同。 字符串忘记 动态内存分配与数组
对于长度在运行时才能确定的数组,应使用动态内存分配:'\0'
// 方式1: 定义字符数组
char str1[6] = "Hello"; // 需要额外一个位置给'\0'
// 方式2: 初始化时省略长度
char str2[] = "World"; // 编译器自动分配6个字节 (W,o,r,l,d,\0)
// 方式3: 逐个字符初始化
char str3[] = {'H', 'i', '\0'}; // 必须手动添加'\0'
<string.h> 头文件)
strcpy(dest, src): 复制字符串 src 到 dest。strcat(dest, src): 将字符串 src 拼接到 dest 的末尾。strcmp(str1, str2): 比较两个字符串,返回0(相等)、正数(str1>str2)、负数(str1<str2)。strlen(str): 返回字符串的长度(不包括 '\0')。strchr(str, c): 查找字符 c 在字符串 str 中首次出现的位置。strstr(str1, str2): 查找字符串 str2 在字符串 str1 中首次出现的位置。#include <stdio.h>
#include <string.h>
int main() {
char str1[50] = "Hello, ";
char str2[] = "C Language!";
// 复制
strcpy(str1, "Hello, "); // 确保str1足够大
// 拼接
strcat(str1, str2);
printf("拼接后的字符串: %s\n", str1); // 输出: Hello, C Language!
// 比较
char a[] = "apple";
char b[] = "banana";
int cmp = strcmp(a, b);
if (cmp < 0) {
printf("a < b\n");
}
// 计算长度
printf("str1 的长度是: %zu\n", strlen(str1)); // 使用 %zu 打印 size_t 类型
return 0;
}
注意事项与常见错误
int arr[5] = {0};
arr[5] = 10; // 错误!索引5超出了范围 [0, 4]
sizeof 在函数外部计算好再传递。
sizeof(arr) 在函数内部得到的是指针的大小(通常是4或8字节),而在外部得到的是整个数组的大小。'\0'
如果字符数组没有以 '\0' 使用 printf("%s", str) 或 strlen() 等函数时,会继续读取内存中的后续数据,直到遇到一个 '\0',这会导致不可预测的错误。
int *arr = (int*)malloc(n * sizeof(int)); // 分配free(arr); // 释放
动态分配的数组在 [] 中使用的方式与静态数组完全相同,但记得在不需要时 free 掉,避免内存泄漏。
