第一次作业标准答案
源码采用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