跳转至

第一次作业标准答案

返回第二次培训讲义 · 下载全部源码

源码采用UTF-8编码。各题目录包含完整中文注释,编译命令为gcc -std=c11 -Wall -Wextra -finput-charset=UTF-8 -fexec-charset=UTF-8 main.c -o program.exe。第五题还需要同目录的max.h。

第1题

main.c

/* 本题采用基础写法:每个有效语句前均有中文说明。
 * scanf输入须在对应整数类型范围内;计算器结果也须在int范围内。 */
/* 引入标准输入输出头文件,才能使用printf和scanf。 */
#include <stdio.h>
/* 程序从main函数开始执行;void表示不接收参数,int表示返回整数退出状态。 */
int main(void)
{
    /* 声明保存输入值的short整数;本题按16位short讲解。 */
    short int number;
    /* 声明无符号16位变量,保存转换后的数值,便于进行位运算。 */
    unsigned short bits;
    /* 声明两个无符号字节变量,分别保存高8位和低8位。 */
    unsigned char high, low;
    /* 显示输入提示并换行,让用户知道需要输入short范围内的整数。 */
    printf("请输入一个short整数:\n");
    /* %hd读取short,&number给出存储地址;返回值不是1说明没有成功读入一个整数。 */
    if (scanf("%hd", &number) != 1) {
        /* 提示输入未成功;\n表示换行。 */
        printf("输入错误。\n");
        /* 结束整个程序并返回1,表示本次运行因输入错误而失败。 */
        return 1;
    }
    /* 将有符号输入转换为无符号数,负数也可用确定的低16位数值参与右移。 */
    bits = (unsigned short)number;
    /* 0xFF的低8位是1、高位是0;按位与后仅保留原数低8位。 */
    low = bits & 0xFF;
    /* 先右移8位把原高字节移到低位,再与0xFF提取这8位。 */
    high = (bits >> 8) & 0xFF;
    /* 输出高字节;%02X表示大写十六进制,至少两位,不足补0。 */
    printf("高字节:0x%02X\n", high);
    /* 用相同格式输出低字节,方便和高字节对照。 */
    printf("低字节:0x%02X\n", low);
    /* 小端低地址存低字节,所以先输出low,再输出high;这里只模拟顺序。 */
    printf("小端序(低地址到高地址):0x%02X 0x%02X\n", low, high);
    /* 大端低地址存高字节,所以先输出high,再输出low。 */
    printf("大端序(低地址到高地址):0x%02X 0x%02X\n", high, low);
    /* 结束整个程序并返回0,表示正常完成。 */
    return 0;
}

第2题

main.c

/* 本题采用基础写法:每个有效语句前均有中文说明。
 * scanf输入须在对应整数类型范围内;计算器结果也须在int范围内。 */
/* 引入标准输入输出头文件,才能使用printf和scanf。 */
#include <stdio.h>
/* 程序从main函数开始执行;void表示不接收参数,int表示返回整数退出状态。 */
int main(void)
{
    /* number表示当前候选数,divisor表示用来试除的因数。 */
    int number, divisor;
    /* 声明已输出素数的数量并从0开始,避免使用未初始化的值。 */
    int count = 0;
    /* 从100开始;不超过200时继续;每轮加1,逐一检查整个区间。 */
    for (number = 100; number <= 200; ++number) {
        /* 每检查一个新数都重新假设它是素数;1表示是,0表示不是。 */
        int is_prime = 1;
        /* 从2试除到平方根;合数至少有一个因数不超过平方根,因此无需继续试除。 */
        for (divisor = 2; divisor * divisor <= number; ++divisor) {
            /* %计算余数;余数为0说明divisor能整除number,因此number是合数。 */
            if (number % divisor == 0) {
                /* 把当前候选数标记为非素数,后面不会输出它。 */
                is_prime = 0;
                /* 已经找到因数,无需继续试除;只退出最近的内层for,不退出外层枚举。 */
                break;
            }
        }
        /* 标志非0说明没有找到因数,当前数是素数,进入输出分支。 */
        if (is_prime) {
            /* 输出这个素数本身,随后再决定用空格还是换行分隔。 */
            printf("%d", number);
            /* 已输出素数的计数增加1;合数不会增加这个计数。 */
            ++count;
            /* 计数除以5余数为0说明刚好输出了5个、10个等,此时应换行。 */
            if (count % 5 == 0) {
                /* 当前行已经输出完毕,换到下一行。 */
                printf("\n");
            /* 前面的判断不成立时,执行这个分支。 */
            } else {
                /* 输出一个空格,用于分隔数字或形成图形内部的空心区域。 */
                printf(" ");
            }
        }
    }
    /* 循环结束后,如果最后一行不足5个,则补上一个换行。 */
    if (count % 5 != 0) {
        /* 当前行已经输出完毕,换到下一行。 */
        printf("\n");
    }
    /* 结束整个程序并返回0,表示正常完成。 */
    return 0;
}

第3题

main.c

/* 本题采用基础写法:每个有效语句前均有中文说明。
 * scanf输入须在对应整数类型范围内;计算器结果也须在int范围内。 */
/* 引入标准输入输出头文件,才能使用printf和scanf。 */
#include <stdio.h>
/* 程序从main函数开始执行;void表示不接收参数,int表示返回整数退出状态。 */
int main(void)
{
    /* choice保存菜单编号,a和b保存参与运算的两个整数。 */
    int choice, a, b;
    /* 先执行一次菜单主体,再在末尾检查循环条件;菜单至少显示一次。 */
    do {
        /* 显示功能编号和选择提示;开头换行用于分隔前一轮输出。 */
        printf("\n1.加法 2.减法 3.乘法 4.除法 5.退出\n请选择:\n");
        /* 读取一个int菜单编号;返回值不是1说明读取失败,不能使用choice继续判断。 */
        if (scanf("%d", &choice) != 1) {
            /* 提示输入未成功;\n表示换行。 */
            printf("输入错误。\n");
            /* 结束整个程序并返回1,表示本次运行因输入错误而失败。 */
            return 1;
        }
        /* 编号5代表退出;必须在输入操作数之前处理,避免退出时还要求输入两数。 */
        if (choice == 5) {
            /* 告诉用户已选择退出。 */
            printf("程序退出。\n");
            /* 退出最近的do-while循环,程序走到最后的return正常结束。 */
            break;
        }
        /* ||表示或;小于1或大于4就是非法运算编号,5已在前面单独处理。 */
        if (choice < 1 || choice > 4) {
            /* 提示菜单编号无效,下一轮重新显示菜单。 */
            printf("输入错误,请重新选择\n");
            /* 跳过本轮余下的输入和计算,先检查do-while条件,再进入下一轮菜单。 */
            continue;
        }
        /* 只有选择1到4后才提示输入操作数。 */
        printf("请输入两个整数(空格分隔):\n");
        /* 分别向a、b读入整数;成功项数必须是2,否则不进行运算。 */
        if (scanf("%d %d", &a, &b) != 2) {
            /* 提示输入未成功;\n表示换行。 */
            printf("输入错误。\n");
            /* 结束整个程序并返回1,表示本次运行因输入错误而失败。 */
            return 1;
        }
        /* 按choice的整数值跳到匹配的case运算分支。 */
        switch (choice) {
        /* 菜单编号1对应加法;从此处执行直到break。 */
        case 1:
            /* 计算a与b的和并按整数格式输出。 */
            printf("结果:%d\n", a + b);
            /* 退出当前switch,防止继续执行下一case;外层菜单循环仍会继续。 */
            break;
        /* 菜单编号2对应减法,注意操作数顺序是a减b。 */
        case 2:
            /* 计算a减b并输出;交换两数会改变结果。 */
            printf("结果:%d\n", a - b);
            /* 退出当前switch,防止继续执行下一case;外层菜单循环仍会继续。 */
            break;
        /* 菜单编号3对应乘法。 */
        case 3:
            /* 计算a与b的乘积并输出。 */
            printf("结果:%d\n", a * b);
            /* 退出当前switch,防止继续执行下一case;外层菜单循环仍会继续。 */
            break;
        /* 菜单编号4对应除法,计算前还要检查除数。 */
        case 4:
            /* b是除数;除数为0不能执行整数除法,否则会产生异常。 */
            if (b == 0) {
                /* 说明不能进行本次除法,随后返回菜单。 */
                printf("除数不能为0。\n");
            /* 前面的判断不成立时,执行这个分支。 */
            } else {
                /* 执行整数除法并输出,舍弃小数部分,向0截断;例如-17/8为-2。 */
                printf("结果:%d\n", a / b);
            }
            /* 退出当前switch,防止继续执行下一case;外层菜单循环仍会继续。 */
            break;
        }
    /* 菜单不是5时重复执行;末尾分号是do-while语法的一部分。 */
    } while (choice != 5);
    /* 结束整个程序并返回0,表示正常完成。 */
    return 0;
}

第4题

main.c

/* 本题采用基础写法:每个有效语句前均有中文说明。
 * scanf输入须在对应整数类型范围内;计算器结果也须在int范围内。 */
/* 引入标准输入输出头文件,才能使用printf和scanf。 */
#include <stdio.h>
/* 程序从main函数开始执行;void表示不接收参数,int表示返回整数退出状态。 */
int main(void)
{
    /* n保存总行数,row表示当前行,column表示当前列。 */
    int n, row, column;
    /* 提示用户输入题目规定范围内的行数。 */
    printf("请输入行数n(3到20):\n");
    /* 读入失败、n小于3、n大于20三者任一成立都属于非法输入;||为短路或。 */
    if (scanf("%d", &n) != 1 || n < 3 || n > 20) {
        /* 说明输入无效及允许的范围。 */
        printf("输入错误:n必须在3到20之间。\n");
        /* 结束整个程序并返回1,表示本次运行因输入错误而失败。 */
        return 1;
    }
    /* 从第1行到第n行,每完成一行row加1,共打印n行。 */
    for (row = 1; row <= n; row++) {
        /* 第row行有row列,列号从1递增到row,形成直角三角形轮廓。 */
        for (column = 1; column <= row; column++) {
            /* 第一列是左边、当前行末列是斜边、最后一行是底边;在任一边界上就输出星号。 */
            if (column == 1 || column == row || row == n) {
                /* 当前位置属于边界,输出一个星号且暂不换行。 */
                printf("*");
            /* 前面的判断不成立时,执行这个分支。 */
            } else {
                /* 输出一个空格,用于分隔数字或形成图形内部的空心区域。 */
                printf(" ");
            }
        }
        /* 当前行已经输出完毕,换到下一行。 */
        printf("\n");
    }
    /* 结束整个程序并返回0,表示正常完成。 */
    return 0;
}

第5题

main.c

/* 本题采用基础写法:每个有效语句前均有中文说明。
 * scanf输入须在对应整数类型范围内;计算器结果也须在int范围内。 */
/* 引入标准输入输出头文件,才能使用printf和scanf。 */
#include <stdio.h>
/* 引入本题自定义头文件,获得MAX宏和DEBUG开关。 */
#include "max.h"
/* 程序从main函数开始执行;void表示不接收参数,int表示返回整数退出状态。 */
int main(void)
{
    /* a和b保存输入的两数,maximum保存宏比较后的最大值。 */
    int a, b, maximum;
    /* 读取两个整数;只有成功项数为2,才能安全地比较a、b。 */
    if (scanf("%d %d", &a, &b) != 2) {
        /* 提示输入未成功;\n表示换行。 */
        printf("输入错误。\n");
        /* 结束整个程序并返回1,表示本次运行因输入错误而失败。 */
        return 1;
    }
    /* 展开并计算MAX宏;a>b时结果为a,否则为b,再保存到maximum。 */
    maximum = MAX(a, b);
/* 检查DEBUG是否已定义;已定义才编译下面的调试输出,不是运行时判断。 */
#ifdef DEBUG
    /* 调试模式显示两数当前值,帮助核对输入。 */
    printf("调试信息:a=%d,b=%d\n", a, b);
    /* 判断a是否严格大于b,成立时宏会选择a。 */
    if (a > b) {
        /* 说明大于分支为何选择a。 */
        printf("a大于b,选择a。\n");
    /* 只有a>b不成立时才检查a<b;成立说明b更大。 */
    } else if (a < b) {
        /* 说明小于分支为何选择b。 */
        printf("a小于b,选择b。\n");
    /* 前面的判断不成立时,执行这个分支。 */
    } else {
        /* 大于和小于都不成立,说明两数相等;取任何一个都正确。 */
        printf("a等于b,两者均为最大值。\n");
    }
/* 结束最近一个条件编译区域。 */
#endif
    /* 把maximum按十进制整数输出;无论是否开启调试,这一行都要执行。 */
    printf("最大值:%d\n", maximum);
    /* 结束整个程序并返回0,表示正常完成。 */
    return 0;
}

max.h

/* 本题采用基础写法:每个有效语句前均有中文说明。
 * scanf输入须在对应整数类型范围内;计算器结果也须在int范围内。 */
/* 如果还没有定义MAX_H才处理下面内容,避免头文件重复包含。 */
#ifndef MAX_H
/* 定义头文件保护标记,下次包含时会跳过已经处理的内容。 */
#define MAX_H
/* 定义带参数宏:a>b时取a,否则取b;参数和整体括号保护优先级。 */
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
/* 定义调试开关;注释掉此行后,#ifdef DEBUG中的代码不再参与编译。 */
#define DEBUG
/* 结束最近一个条件编译区域。 */
#endif