🎯 本章你会学到:数组的内存布局、越界为什么危险、C 的字符串为什么要靠 '\0' 收尾。建议边读边敲,每节练习先自己做,再展开答案对照。
数组是一段连续的内存。这一章的两个重点:一是数组下标不做边界检查,二是 C 没有字符串类型,只有“以 \0 结尾的字符数组”。
数组:连续内存 + 下标越界不报错
数组求最大值与平均值
#include <stdio.h>
#define MAXN 100
int main(void) {
int n = 0;
if (scanf("%d", &n) != 1 || n <= 0 || n > MAXN) {
return 1; /* 先校验规模,再开数组 */
}
int a[MAXN];
for (int i = 0; i < n; ++i) {
if (scanf("%d", &a[i]) != 1) {
return 1;
}
}
int maxv = a[0];
long long sum = 0;
for (int i = 0; i < n; ++i) {
if (a[i] > maxv) {
maxv = a[i];
}
sum += a[i];
}
printf("max=%d\n", maxv);
printf("avg=%.2f\n", (double)sum / n);
return 0;
}输入
5
3 9 4 9 2运行结果
max=9
avg=5.40
输入 5 和 3 9 4 9 2,输出 max=9 与 avg=5.40。注意 sum 用 long long:数组很大时 int 会溢出。
字符串:字符数组 + 结尾的 '\0'
字符串长度与复制
#include <stdio.h>
#include <string.h>
int main(void) {
char buf[64];
if (scanf("%63s", buf) != 1) { /* 63:给结尾的 '\0' 留一个位置 */
return 1;
}
char copy[64];
strcpy(copy, buf); /* 复制(目标数组必须够大) */
printf("len=%zu\n", strlen(copy)); /* 长度不含 '\0' */
printf("first=%c\n", copy[0]);
return 0;
}输入
hello运行结果
len=5
first=h
输入 hello 输出 len=5 与 first=h。strlen 是一个字符一个字符数到 \0 为止,所以它不保存长度、每次调用都是 O(n)。
| 函数 | 作用 | 注意 |
|---|---|---|
| strlen(s) | 求长度(不含 '\0') | O(n) |
| strcpy(dst, src) | 复制字符串 | dst 必须够大,否则缓冲区溢出 |
| strcmp(a, b) | 比较(返回负/0/正) | 不能用 == 比较字符串 |
| strcat(dst, src) | 拼接 | 同样要保证 dst 够大 |
| fgets(buf, n, stdin) | 读一整行(含空格) | 会保留换行符,需要自己删 |
⚠️ 易错点 数组越界不报错:
int a[5]; a[5] = 1; 编译通过、多数时候也能跑,但它改的是别人的内存,属于未定义行为。⚠️ 易错点 用
%s 读入一定要限宽(%63s)。不限宽时,输入比缓冲区长就会溢出——这是 C 程序最经典的安全漏洞。⚠️ 易错点 把数组传给函数后,
sizeof 拿到的是指针大小(8),不是数组长度。长度必须另外传进去。✍️ 本节练习
5.1必做数组反转输出
读入 n 和 n 个整数,按相反顺序输出,数字之间用空格分隔,末尾换行。
输入 第一行 n;第二行 n 个整数。
输出 一行:反转后的序列。
样例输入
5
1 2 3 4 5样例输出
5 4 3 2 1💡 提示 从 n - 1 倒着循环,注意最后一个数字后面不要再输出空格。
✅ 查看参考答案与解析
#include <stdio.h>
#define MAXN 1000
int main(void) {
int n = 0;
if (scanf("%d", &n) != 1 || n <= 0 || n > MAXN) {
return 1;
}
int a[MAXN];
for (int i = 0; i < n; ++i) {
if (scanf("%d", &a[i]) != 1) {
return 1;
}
}
for (int i = n - 1; i >= 0; --i) {
printf("%d%c", a[i], i == 0 ? '\n' : ' ');
}
return 0;
}解析 用三元运算符决定“输出空格还是换行”,可以避免末尾多一个空格——在线评测系统对格式很敏感。
5.2挑战统计一行的长度和单词数
用 fgets 读入一整行(可能含空格),输出这行的字符数(不含换行)和单词数(以空格分隔)。
输入 一行文字(长度 ≤ 250)。
输出 两行:len=字符数、words=单词数。
样例输入
hello world样例输出
len=11
words=2💡 提示 先删掉行尾的换行符,再用一个 in_word 标志遍历统计。
✅ 查看参考答案与解析
#include <stdio.h>
#include <string.h>
int main(void) {
char line[256];
if (fgets(line, sizeof line, stdin) == NULL) {
return 0;
}
size_t len = strlen(line);
while (len > 0 && (line[len - 1] == '\n' || line[len - 1] == '\r')) {
line[--len] = '\0'; /* 去掉行尾换行 */
}
int words = 0, in_word = 0;
for (size_t i = 0; i < len; ++i) {
if (line[i] != ' ' && !in_word) {
++words;
in_word = 1;
} else if (line[i] == ' ') {
in_word = 0;
}
}
printf("len=%zu\n", len);
printf("words=%d\n", words);
return 0;
}解析 fgets 会把换行也读进来,所以长度要先减掉它;in_word 标志保证连续空格不会被重复计数。