本文件是"纯代码 + 逐行注释",每个程序都能直接编译运行。
用法:先照着敲一遍,再合上书默写一遍;卡住的地方回课程主体对应小节。
一、知识点代码
c
// ============ 1. 一维数组:定义、初始化、引用 ============
#include <stdio.h> // 标准输入输出
int main() // 主函数,程序入口
{
int a[10]; // 定义 10 个元素,合法下标 0~9
int b[5] = {1, 2, 3}; // 前 3 个赋初值,后 2 个自动为 0
int c[] = {9, 8, 7}; // 全部赋初值,长度自动定为 3
int i; // 循环变量
for (i = 0; i <= 9; i++) // 遍历全部合法下标
a[i] = i * i; // 第 i 号柜子放 i 的平方
printf("a: "); // 提示文字
for (i = 0; i <= 9; i++) // 再遍历一遍
printf("%d ", a[i]); // 输出每个元素
printf("\n"); // 换行
printf("b[0]=%d b[3]=%d\n", b[0], b[3]); // 验证没写到的元素是 0
printf("count=%d\n", (int)(sizeof(c) / sizeof(c[0]))); // 元素个数 = 总字节 / 单个字节
return 0; // 正常结束
}c
// ============ 2. 二维数组:定义、初始化、行列引用、转置 ============
#include <stdio.h>
int main()
{
int a[2][3] = {{1, 2, 3}, {4, 5, 6}}; // 2 行 3 列,分行初始化
int b[3][2]; // 转置后是 3 行 2 列
int i, j; // 行下标、列下标
for (i = 0; i <= 1; i++) // 行 0~1
for (j = 0; j <= 2; j++) // 列 0~2
b[j][i] = a[i][j]; // 行列对调,这就是转置
for (i = 0; i <= 2; i++) // 输出 b 的 3 行
{
for (j = 0; j <= 1; j++) // 每行 2 列
printf("%3d", b[i][j]); // 每个数占 3 格
printf("\n"); // 一行结束换行
}
return 0;
}c
// ============ 3. 字符数组:定义、初始化、逐字符输出 ============
#include <stdio.h>
int main()
{
char c[10] = {'I',' ','a','m',' ','h','a','p','p','y'}; // 10 个字符,空格也算
int i; // 循环变量
for (i = 0; i < 10; i++) // 下标 0~9
printf("%c", c[i]); // %c 输出一个字符
printf("\n"); // 换行
return 0;
}c
// ============ 4. 字符串结束标志 '\0' ============
#include <stdio.h>
#include <string.h> // 用了 strlen,必须包含
int main()
{
char a[] = "China"; // 字符串常量初始化,末尾自动加 '\0'
char b[] = {'C','h','i','n','a'}; // 逐字符初始化,没有 '\0'
printf("sizeof(a)=%d strlen(a)=%d\n", (int)sizeof(a), (int)strlen(a)); // 6 和 5
printf("sizeof(b)=%d\n", (int)sizeof(b)); // 5
return 0;
}c
// ============ 5. 字符数组的输入输出:scanf %s、gets、puts ============
#include <stdio.h>
int main()
{
char s1[20], s2[20]; // 两个字符数组
printf("input a word: "); // 提示
scanf("%s", s1); // 读一个词,遇空格停;数组名不加 &
printf("scanf got: %s\n", s1); // 输出刚才读到的词
printf("input a line: "); // 提示
getchar(); // 吃掉 scanf 留在缓冲区里的回车
gets(s2); // 读一整行,可以含空格
puts(s2); // 输出整行并自动换行
return 0;
}c
// ============ 6. 字符串处理函数:strlen / strcpy / strcat / strcmp / strncpy ============
#include <stdio.h>
#include <string.h> // 字符串函数都在这
int main()
{
char s1[40] = "Hello"; // 留足空间,后面要接 "World"
char s2[20] = "World"; // 被连接的串
char s3[40]; // 空的接收者
printf("strlen: %d\n", (int)strlen(s1)); // 5,不含 '\0'
strcpy(s3, s1); // 把 s1 复制进 s3
printf("strcpy: %s\n", s3); // Hello
strcat(s1, s2); // 把 s2 接到 s1 后面
printf("strcat: %s\n", s1); // HelloWorld
printf("strcmp: %d\n", strcmp("abc", "abd")); // 'c'-'d' 为负
strncpy(s3, "China", 3); // 只复制前 3 个字符
s3[3] = '\0'; // strncpy 不保证结尾,手工补 '\0'
printf("strncpy: %s\n", s3); // Chi
return 0;
}c
// ============ 7. strlwr / strupr:大小写转换(VS 专有)============
#include <stdio.h>
#include <string.h>
int main()
{
char s[20] = "Hello World"; // 混有大小写
printf("%s\n", strlwr(s)); // 全转小写:hello world
printf("%s\n", strupr(s)); // 全转大写:HELLO WORLD
return 0;
}c
// ============ 8. 冒泡排序:n 个数比 n-1 趟,第 j 趟比 n-j 次 ============
#include <stdio.h>
int main()
{
int a[10]; // 10 个柜子
int i, j, t; // i 内层、j 外层、t 交换用
printf("input 10 numbers:\n"); // 提示
for (i = 0; i < 10; i++) // 读 10 个数
scanf("%d", &a[i]); // 元素要加 &
for (j = 0; j < 9; j++) // 外层:9 趟
for (i = 0; i < 9 - j; i++) // 内层:第 j 趟比 9-j 次
if (a[i] > a[i + 1]) // 前面比后面大就交换(升序)
{
t = a[i]; // 三只杯子:先倒进空杯
a[i] = a[i + 1]; // 后面的数往前挪
a[i + 1] = t; // 空杯里的数放后面
}
for (i = 0; i < 10; i++) // 输出结果
printf("%d ", a[i]); // 每个数后跟空格
printf("\n"); // 换行
return 0;
}二、课后题 · 代码题
c
// ============ 习题 1:筛选法求 100 之内的素数 ============
#include <stdio.h>
int main()
{
int a[101], i, j; // 用下标当数值本身
for (i = 2; i <= 100; i++) // 2~100 全部入柜
a[i] = i; // 编号即数值
for (i = 2; i * i <= 100; i++) // 只需筛到 10
if (a[i] != 0) // 还在,说明 i 是素数
for (j = i + i; j <= 100; j += i) // 从两倍开始
a[j] = 0; // 倍数全部划掉
for (i = 2; i <= 100; i++) // 扫一遍
if (a[i] != 0) printf("%d ", a[i]); // 非 0 的就是素数
printf("\n"); // 换行
return 0;
}c
// ============ 习题 2:选择法对 10 个整数排序 ============
#include <stdio.h>
int main()
{
int a[10], i, j, k, t; // k 记住最小元素的下标
for (i = 0; i < 10; i++) // 读入 10 个数
scanf("%d", &a[i]); // 元素加 &
for (i = 0; i < 9; i++) // 共 9 趟
{
k = i; // 先假定本趟第一个最小
for (j = i + 1; j < 10; j++) // 在后面找更小的
if (a[j] < a[k]) k = j; // 找到就更新 k
if (k != i) // 最小值不在本位置才换
{
t = a[i]; a[i] = a[k]; a[k] = t; // 一次交换到位
}
}
for (i = 0; i < 10; i++) // 输出
printf("%d ", a[i]); // 空格分隔
printf("\n"); // 换行
return 0;
}c
// ============ 习题 3:3×3 矩阵主对角线元素之和 ============
#include <stdio.h>
int main()
{
int a[3][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}; // 3 行 3 列
int i, sum = 0; // sum 记得从 0 开始
for (i = 0; i < 3; i++) // 主对角线只有 3 个元素
sum = sum + a[i][i]; // 行列下标相同
printf("sum=%d\n", sum); // 输出 15
return 0;
}c
// ============ 习题 4:有序数组中插入一个数 ============
#include <stdio.h>
int main()
{
int a[11] = {1, 4, 6, 9, 13, 16, 19, 28, 40, 100}; // 留一个空位
int i, x; // x 是待插入的数
scanf("%d", &x); // 读入 x
for (i = 9; i >= 0 && a[i] > x; i--) // 从后往前找位置
a[i + 1] = a[i]; // 比 x 大的往后挪一格
a[i + 1] = x; // 空出来的位置放 x
for (i = 0; i < 11; i++) // 输出 11 个数
printf("%d ", a[i]); // 空格分隔
printf("\n"); // 换行
return 0;
}c
// ============ 习题 5:数组的值按逆序重新存放 ============
#include <stdio.h>
int main()
{
int a[5] = {8, 6, 5, 4, 1}, i, t; // 5 个数
for (i = 0; i < 5 / 2; i++) // 只换前一半
{
t = a[i]; // 暂存左边的数
a[i] = a[4 - i]; // 右边的数搬到左边
a[4 - i] = t; // 暂存的数放到右边
}
for (i = 0; i < 5; i++) // 输出
printf("%d ", a[i]); // 空格分隔
printf("\n"); // 换行
return 0;
}c
// ============ 习题 6:杨辉三角形(10 行)============
#include <stdio.h>
int main()
{
int a[10][10], i, j; // 10×10 够用
for (i = 0; i < 10; i++) // 先处理两条边
{
a[i][0] = 1; // 每行第一个是 1
a[i][i] = 1; // 每行最后一个是 1
}
for (i = 2; i < 10; i++) // 从第 3 行开始算中间
for (j = 1; j < i; j++) // 中间的列 1~i-1
a[i][j] = a[i - 1][j - 1] + a[i - 1][j]; // 上方两数之和
for (i = 0; i < 10; i++) // 输出 10 行
{
for (j = 0; j <= i; j++) // 第 i 行有 i+1 个数
printf("%5d", a[i][j]); // 每个占 5 格
printf("\n"); // 换行
}
return 0;
}c
// ============ 习题 7:奇数阶魔方阵 ============
#include <stdio.h>
int main()
{
int a[15][15] = {0}; // 全部初始化为 0
int n, i, j, k, row, col; // row/col 是当前要填的格子
scanf("%d", &n); // 读入阶数,必须是奇数
row = 0; col = n / 2; // 1 放在第一行中间
for (k = 1; k <= n * n; k++) // 依次填 1~n*n
{
a[row][col] = k; // 放入当前数
if (k % n == 0) row = row + 1; // 放满 n 的倍数就往下一行
else
{
row = row - 1; if (row < 0) row = n - 1; // 上移,越界绕到最后一行
col = col + 1; if (col > n - 1) col = 0; // 右移,越界绕到第一列
}
}
for (i = 0; i < n; i++) // 输出 n 行
{
for (j = 0; j < n; j++) // 每行 n 列
printf("%5d", a[i][j]); // 每格占 5 位
printf("\n"); // 换行
}
return 0;
}c
// ============ 习题 8:找二维数组中的鞍点 ============
#include <stdio.h>
int main()
{
int a[3][4] = {{4, 6, 8, 2}, {1, 11, 10, 3}, {2, 9, 12, 13}}; // 3 行 4 列
int i, j, k, maxj, max, flag, found = 0; // maxj 记本行最大值的列号
for (i = 0; i < 3; i++) // 逐行处理
{
max = a[i][0]; maxj = 0; // 假定本行第一个最大
for (j = 1; j < 4; j++) // 扫描本行
if (a[i][j] > max) // 找到更大的
{
max = a[i][j]; maxj = j; // 更新最大值和列号
}
flag = 1; // 先假定是鞍点
for (k = 0; k < 3; k++) // 在本列上检查
if (a[k][maxj] < max) // 有更小的,一票否决
{
flag = 0; break; // 不是鞍点,跳出
}
if (flag) // 确认是鞍点
{
printf("a[%d][%d]=%d\n", i, maxj, max); // 输出位置和值
found = 1; // 记录找到过
}
}
if (!found) printf("no saddle point\n"); // 一个都没有
return 0;
}c
// ============ 习题 9:折半查找(降序数组)============
#include <stdio.h>
int main()
{
int a[15] = {100, 96, 90, 85, 80, 75, 70, 65, 60, 55, 50, 45, 40, 35, 30}; // 降序
int x, low, high, mid; // 查找区间 [low, high]
scanf("%d", &x); // 要查找的数
low = 0; high = 14; // 初始区间是全部
while (low <= high) // 区间还有元素
{
mid = (low + high) / 2; // 取中间
if (a[mid] == x) break; // 找到了
else if (a[mid] > x) low = mid + 1; // 降序:中间偏大,往右找
else high = mid - 1; // 中间偏小,往左找
}
if (low <= high) printf("index=%d\n", mid + 1); // 第几个(从 1 数)
else printf("not found\n"); // 没找到
return 0;
}c
// ============ 习题 10:统计 3 行 80 字符 ============
#include <stdio.h>
int main()
{
char text[3][80]; // 3 行,每行 80 个字符
int i, j, upper = 0, lower = 0, digit = 0, space = 0, other = 0;
for (i = 0; i < 3; i++) // 读 3 行
gets(text[i]); // 一次读一整行
for (i = 0; i < 3; i++) // 逐行扫描
for (j = 0; text[i][j] != '\0'; j++) // 以 '\0' 判断行尾
{
char c = text[i][j]; // 当前字符
if (c >= 'A' && c <= 'Z') upper++; // 大写字母
else if (c >= 'a' && c <= 'z') lower++; // 小写字母
else if (c >= '0' && c <= '9') digit++; // 数字
else if (c == ' ') space++; // 空格
else other++; // 其他
}
printf("upper=%d lower=%d digit=%d space=%d other=%d\n", upper, lower, digit, space, other);
return 0;
}c
// ============ 习题 11:输出平行四边形图案 ============
#include <stdio.h>
int main()
{
int i, j; // i 行号,j 列号
for (i = 0; i < 5; i++) // 5 行
{
for (j = 0; j < i; j++) // 第 i 行先打 i 个空格
printf(" "); // 一个空格
for (j = 0; j < 5; j++) // 再打 5 组
printf("* "); // 星号 + 空格
printf("\n"); // 换行
}
return 0;
}c
// ============ 习题 12:电文译码(A→Z,B→Y……)============
#include <stdio.h>
int main()
{
char c[100]; // 存放电文
int i; // 循环变量
gets(c); // 读一整行
printf("cipher: %s\n", c); // 先输出密码
for (i = 0; c[i] != '\0'; i++) // 逐个字符处理
{
if (c[i] >= 'A' && c[i] <= 'Z') // 大写字母
c[i] = 'A' + 'Z' - c[i]; // A↔Z、B↔Y,自逆变换
else if (c[i] >= 'a' && c[i] <= 'z') // 小写字母
c[i] = 'a' + 'z' - c[i]; // 同样自逆
}
printf("plain: %s\n", c); // 输出原文
return 0;
}c
// ============ 习题 13:连接两个字符串(不用 strcat)============
#include <stdio.h>
int main()
{
char s1[100], s2[50]; // s1 要够大
int i = 0, j = 0; // i 走 s1,j 走 s2
gets(s1); // 读第一个串
gets(s2); // 读第二个串
while (s1[i] != '\0') i++; // 找到 s1 的结尾
while (s2[j] != '\0') // 逐个搬 s2
{
s1[i] = s2[j]; // 搬一个字符
i++; j++; // 两个下标都前进
}
s1[i] = '\0'; // 补上结束标志
printf("%s\n", s1); // 输出连接后的串
return 0;
}c
// ============ 习题 14:比较两个字符串(不用 strcmp)============
#include <stdio.h>
int main()
{
char s1[100], s2[100]; // 两个待比较的串
int i = 0; // 从第 0 个字符开始
gets(s1); // 读 s1
gets(s2); // 读 s2
while (s1[i] != '\0' && s1[i] == s2[i]) // 相同的字符一直往后走
i++; // 下标前进
printf("%d\n", s1[i] - s2[i]); // 第一个不同字符的 ASCII 差
return 0;
}c
// ============ 习题 15:复制字符串(不用 strcpy)============
#include <stdio.h>
int main()
{
char s1[100], s2[100]; // s2 是源,s1 是目标
int i; // 下标
gets(s2); // 读源串
for (i = 0; ; i++) // 条件留空,靠 break 退出
{
s1[i] = s2[i]; // 先复制一个字符
if (s2[i] == '\0') break; // 复制到 '\0' 就收工
}
printf("%s\n", s1); // 输出复制结果
return 0;
}三、我出的题 · 随课练习
🔮 猜输出
c
// 猜输出 1
#include <stdio.h>
int main()
{
int a[5] = {1, 2, 3}; // 只给前 3 个元素初值
int i; // 循环变量
for (i = 0; i < 5; i++) // 输出全部 5 个
printf("%d ", a[i]); // 每个数后跟空格
printf("\n"); // 换行
return 0;
}c
// 猜输出 2
#include <stdio.h>
#include <string.h>
int main()
{
char s[] = "abc"; // 字符串常量初始化
printf("%d %d\n", (int)strlen(s), (int)sizeof(s)); // 一个测长度,一个测字节
return 0;
}c
// 猜输出 3
#include <stdio.h>
int main()
{
int a[3][4] = {{1, 2}, {5}, {9, 10, 11}}; // 分行部分初始化
printf("%d %d %d\n", a[0][2], a[1][3], a[2][0]); // 输出三个指定元素
return 0;
}c
// 猜输出 4
#include <stdio.h>
int main()
{
char a[] = "China"; // 用字符串常量初始化
char b[] = {'C', 'h', 'i', 'n', 'a'}; // 用字符列表初始化
printf("%d %d\n", (int)sizeof(a), (int)sizeof(b)); // 两个数组各占多少字节
return 0;
}🛠 变式题
c
// 变式 1:把冒泡排序改成降序 —— 只改下面的比较符号
for (j = 0; j < 9; j++) // 共 9 趟
for (i = 0; i < 9 - j; i++) // 每趟 9-j 次
if (a[i] ______ a[i + 1]) // 在这里填一个比较运算符
{
t = a[i]; a[i] = a[i + 1]; a[i + 1] = t; // 交换
}c
// 变式 2:求 3×4 矩阵每一行的最大值 —— 在外面再套一层"逐行"的循环
for (i = 0; i < 3; i++) // 逐行
{
max = a[i][0]; // 假定本行第一个最大
for (j = 1; j < 4; j++) // 扫描本行
if (______) max = a[i][j]; // 补全:什么时候更新 max
printf("row %d max = %d\n", i, max); // 输出本行最大值
}c
// 变式 3:不用 strlen,自己写 my_strlen
int my_strlen(char s[]) // 返回字符串长度
{
int i = 0; // 计数器
while (______) // 补全:没到结尾就继续
i++; // 计数器加 1
return i; // 返回长度
}c
// 变式 4:3×3 矩阵原地转置 —— 只遍历上三角
for (i = 0; i < 3; i++) // 行
for (j = ______; j < 3; j++) // 补全:j 从哪里开始?
{
t = a[i][j]; // 暂存 a[i][j]
a[i][j] = a[j][i]; // 对称位置互换
a[j][i] = t; // 放回
}🐛 改错题
c
// ❌ 错误示例(故意)
#include <stdio.h>
int main()
{
int a[10], i; // 10 个元素,下标 0~9
for (i = 1; i <= 10; i++) // 错:从 1 到 10
a[i] = i; // 错:a[10] 越界,a[0] 没赋值
return 0;
}
// 错在哪:下标从 0 开始,最大是 9。
// 怎么改:for (i = 0; i <= 9; i++) a[i] = i;c
// ❌ 错误示例(故意)
#include <stdio.h>
int main()
{
char s[10]; // 字符数组
scanf("%s", &s); // 错:数组名本身就是地址,不能加 &
printf("%s\n", s); // 输出
return 0;
}
// 错在哪:&s 是"数组的地址",类型不对;数组名 s 已经是首元素地址。
// 怎么改:scanf("%s", s);c
// ❌ 错误示例(故意)
#include <stdio.h>
int main()
{
char s1[10], s2[10] = "China"; // s2 已初始化
s1 = s2; // 错:数组名是地址常量,不能整体赋值
printf("%s\n", s1); // 输出
return 0;
}
// 错在哪:s1 = s2 想复制字符串,但数组名不能被赋值。
// 怎么改:char s1[10]; strcpy(s1, s2); 并加上 #include <string.h>四、加练题
(暂无)