数组与字符串(Arrays & Strings)
数组用于存储一组相同类型的元素,字符串是 C 语言中以 \0 结尾的字符数组。它们是批量数据处理的基础工具。
1. 数组——同类型数据的集合
1.1 为什么需要数组
假设要存储 100 个学生的成绩,不用数组的结果是:
int s0, s1, s2, s3, ..., s99; // 声明 100 个独立变量——不现实
用数组只需一行:
int scores[100]; // 100 个连续的 int 空间
1.2 声明与初始化
int arr[5]; // 未初始化(元素为垃圾值)
int arr2[5] = {10, 20, 30, 40, 50}; // 全部指定
int arr3[5] = {1, 2, 3}; // 部分指定,剩余自动补 0 → {1,2,3,0,0}
int arr4[] = {1, 2, 3, 4}; // 自动推断大小为 4
数组在内存中是连续存放的:
arr2: ┌────┬────┬────┬────┬────┐
│ 10 │ 20 │ 30 │ 40 │ 50 │
└────┴────┴────┴────┴────┘
索引: 0 1 2 3 4
地址: 低地址 → → → → → → → → 高地址
1.3 访问元素
int arr[5] = {10, 20, 30, 40, 50};
arr[0] = 100; // 修改第 1 个元素
int x = arr[2]; // 读取第 3 个元素 → 30
printf("%d\n", arr[4]); // 输出最后一个元素 → 50
- 索引从 0 开始,到 n-1 结束(n 为元素个数)。
- 索引超出范围(如
arr[5]或arr[-1])是未定义行为,编译器不报错但运行时会出问题。
1.4 获取数组长度
int arr[] = {1, 2, 3, 4, 5};
int length = sizeof(arr) / sizeof(arr[0]); // 总字节数 / 单个元素字节数 = 元素个数
printf("数组长度:%d\n", length); // 5
⚠️ 这种方法仅在数组定义的同一作用域内有效。将数组传给函数后,
sizeof返回的是指针大小而非数组大小(详见"函数"篇)。
2. 多维数组
int matrix[3][4] = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};
printf("%d\n", matrix[1][2]); // 第 2 行第 3 列 → 7
内存布局(C 语言采用行优先存储):
逻辑视图: 内存中的实际排列(按行存放):
┌─────────────┐ ┌──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┐
│ 1 2 3 4│ │1 │2 │3 │4 │5 │6 │7 │8 │9 │10│11│12│
│ 5 6 7 8│ └──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┘
│ 9 10 11 12│ 第0行 第1行 第2行
└─────────────┘
// 遍历二维数组
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 4; j++) {
printf("%3d ", matrix[i][j]);
}
printf("\n");
}
3. 字符串——以 \0 结尾的字符数组
C 语言没有独立的字符串类型。字符串本质上是末尾带有 \0(空字符,ASCII 值 0)的 char 数组。
3.1 声明与初始化
char str1[6] = {'H', 'e', 'l', 'l', 'o', '\0'}; // 手动逐字符
char str2[6] = "Hello"; // 等价写法(编译器自动加 \0)
char str3[] = "Hello"; // 自动推断大小为 6(5 字符 + \0)
内存布局:
str3: ┌───┬───┬───┬───┬───┬───┐
│ H │ e │ l │ l │ o │\0 │
└───┴───┴───┴───┴───┴───┘
索引: 0 1 2 3 4 5
\0 是字符串结束的标志。库函数(如 printf、strlen)靠它来确定字符串边界。如果缺少 \0,函数会一直向后读取直到碰巧遇到一个 0 字节,导致错误。
3.2 使用 printf 和 scanf
char name[50];
printf("请输入姓名:");
scanf("%49s", name); // 限制最多读 49 字符,保留 1 位给 \0
printf("你好,%s\n", name);
⚠️
scanf("%s")遇到空格就会停止读取。如需读取含空格的整行,用fgets:c char line[100]; fgets(line, sizeof(line), stdin); // 安全读取一行
4. 常用字符串函数(<string.h>)
#include <string.h>
char s1[50] = "Hello";
char s2[50] = "World";
char s3[50];
4.1 获取长度
size_t len = strlen(s1); // 5(不包含 \0)
4.2 复制
strcpy(s3, s1); // 将 s1 复制到 s3,包括 \0
strncpy(s3, s1, 3); // 最多复制 3 个字符,需手动补 \0
s3[3] = '\0';
⚠️
strcpy不检查目标缓冲区大小,如果源字符串更长会导致缓冲区溢出。优先使用strncpy或snprintf。
4.3 拼接
strcat(s1, " "); // s1 = "Hello "(确保 s1 有足够空间)
strcat(s1, s2); // s1 = "Hello World"
4.4 比较
if (strcmp(s1, s2) == 0) { printf("相等\n"); }
if (strcmp(s1, s2) < 0) { printf("s1 < s2\n"); }
if (strcmp(s1, s2) > 0) { printf("s1 > s2\n"); }
⚠️ 不能用
==比较字符串。s1 == s2比较的是两个数组的地址而非内容。必须用strcmp。
5. 字符处理函数(<ctype.h>)
#include <ctype.h>
char ch = 'A';
isalpha(ch); // 是否为字母 → 真
isdigit(ch); // 是否为数字 → 假
isspace(ch); // 是否为空白字符 → 假
islower(ch); // 是否为小写 → 假
isupper(ch); // 是否为大写 → 真
toupper('a'); // 转大写 → 'A'
tolower('Z'); // 转小写 → 'z'
6. 数组与字符串常见操作
6.1 反转数组
void reverse(int arr[], int n)
{
for (int i = 0; i < n / 2; i++) {
int temp = arr[i];
arr[i] = arr[n - 1 - i];
arr[n - 1 - i] = temp;
}
}
6.2 字符串转数字
#include <stdlib.h>
int a = atoi("42"); // 字符串 → int
double b = atof("3.14"); // 字符串 → double
long c = atol("1000"); // 字符串 → long
7. 总结
| 要点 | 说明 |
|---|---|
| 数组索引从 0 开始 | arr[0] 是第一个元素,arr[n-1] 是最后一个 |
| 不能越界访问 | 编译器不检查,运行时可能崩溃或产生错误结果 |
sizeof 获取数组大小 |
仅在数组定义的作用域内有效 |
字符串以 \0 结尾 |
分配空间时需额外留 1 字节 |
用 strcmp 比较字符串 |
== 只比较地址 |
优先用 strncpy/fgets |
带长度限制的版本更安全 |
| 二维数组行优先存储 | 内层索引变化快,与外层循环对应 |
作者: Qing | 修改日期: 2026-07-18