CC语言通关
第06课 · 要写代码
教材对照
第06课配套 · 纯代码

第06课 · 要写代码

一维/二维数组、字符数组、字符串结束标志、字符串处理函数

34 段代码每行都有注释边敲边想
教材依据第6章《利用数组处理批量数据》书页 p.139–166
本课覆盖:教材第6章正文 + 书末习题(p.165–166)。标「教材 p.X」= 教材段落总结;「例题」= 教材例题;「习题」= 书末题目;「本课补充」= 非教材原文。
在全书对照表中查看 →

本文件是"纯代码 + 逐行注释",每个程序都能直接编译运行。
用法:先照着敲一遍,再合上书默写一遍;卡住的地方回课程主体对应小节。

一、知识点代码

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>

四、加练题

(暂无)