第二次作业标准答案
源码统一采用 UTF-8 编码,每题独立为一个多文件小工程(main.c + 函数实现 .c + .h)。进入题目目录编译,例如第一题:
gcc -std=c11 -Wall -Wextra -Wpedantic -Werror main.c sum_array.c -o sum_array.exe
其余各题把 sum_array.c 换成对应题的实现文件即可。
第1题:递归实现数组元素求和
main.c
#include <stdio.h>
#include "sum_array.h"
// 限制数组最大长度,避免输入过长导致数组越界。
#define MAX_ARRAY_SIZE 1000
int main(void)
{
int arr[MAX_ARRAY_SIZE];
int n;
// 输入格式:先输入数组长度 n,再输入 n 个整数。
if (scanf("%d", &n) != 1 || n < 1 || n > MAX_ARRAY_SIZE) {
fprintf(stderr, "输入错误:数组长度必须在 1 到 %d 之间。\n", MAX_ARRAY_SIZE);
return 1;
}
// 每次读取一个整数,并通过下标 arr[i] 存入对应位置。
for (int i = 0; i < n; ++i) {
if (scanf("%d", &arr[i]) != 1) {
fprintf(stderr, "输入错误:请按要求输入整数。\n");
return 1;
}
}
// 将数组和有效长度传给递归函数,在主函数中输出最终结果。
printf("%d\n", sumArray(arr, n));
return 0;
}
sum_array.c
#include "sum_array.h"
int sumArray(const int arr[], int n)
{
// 递归必须有明确的结束条件。
// 当 n <= 0 时,没有需要累加的元素,因此元素和为 0。
if (n <= 0) {
return 0;
}
// 将问题拆成两部分:最后一个元素 arr[n - 1],以及前 n - 1 个元素的和。
// 每次递归都会让 n 减小 1,最终到达 n == 0 的递归出口。
return arr[n - 1] + sumArray(arr, n - 1);
}
sum_array.h
#ifndef SUM_ARRAY_H
#define SUM_ARRAY_H
// 递归计算整型数组中前 n 个元素的和。
// arr 指向数组首元素,n 表示参与求和的元素个数。
// 使用 const 表明函数只读取数组,不会修改数组中的数据。
int sumArray(const int arr[], int n);
#endif // SUM_ARRAY_H
第2题:学生成绩统计与最高平均分查找
main.c
#include <stdio.h>
#include "score_stats.h"
int main(void)
{
float score[STUDENT_COUNT][COURSE_COUNT];
// 按顺序输入 4 名学生的成绩,每名学生依次输入 3 门课程成绩。
// 输入示例:80 90 85 76 88 92 91 90 89 70 75 80
for (int student_idx = 0; student_idx < STUDENT_COUNT; ++student_idx) {
for (int course_idx = 0; course_idx < COURSE_COUNT; ++course_idx) {
if (scanf("%f", &score[student_idx][course_idx]) != 1) {
fprintf(stderr, "输入错误:请按要求输入数字成绩。\n");
return 1;
}
}
}
// 学生编号从 1 开始,而数组下标从 0 开始,因此输出时加 1。
for (int student_idx = 0; student_idx < STUDENT_COUNT; ++student_idx) {
printf("学生 %d:%.1f\n",
student_idx + 1, calcAverage(score, student_idx));
}
// findTopStudent 返回的是数组下标,输出学生编号时同样加 1。
// 再调用 calcAverage 得到该学生的平均分。
int top_idx = findTopStudent(score);
printf("最高平均分:学生 %d,%.1f\n",
top_idx + 1, calcAverage(score, top_idx));
return 0;
}
score_stats.c
#include "score_stats.h"
float calcAverage(float score[STUDENT_COUNT][COURSE_COUNT], int student_idx)
{
float total = 0.0f;
// 先检查学生下标,避免访问二维数组中不存在的行。
// 题目规定正常调用时下标在 0 到 3 之间;这里额外处理非法参数。
if (student_idx < 0 || student_idx >= STUDENT_COUNT) {
return 0.0f;
}
// 固定 student_idx 这一行,遍历这一行的 3 门课程成绩。
// 使用二维数组下标访问元素,不使用指针算术。
for (int course_idx = 0; course_idx < COURSE_COUNT; ++course_idx) {
total += score[student_idx][course_idx];
}
// 转换为平均分并返回;本函数只计算,不负责输入或输出。
return total / COURSE_COUNT;
}
int findTopStudent(float score[STUDENT_COUNT][COURSE_COUNT])
{
// 先把第 0 名学生设为当前最高分学生,保证返回的是有效下标。
int top_idx = 0;
float top_average = calcAverage(score, top_idx);
// 从第 1 名学生开始比较,并调用 calcAverage 统一计算每人的平均分。
for (int student_idx = 1; student_idx < STUDENT_COUNT; ++student_idx) {
float average = calcAverage(score, student_idx);
// 只有发现严格更高的平均分才更新。
// 如果平均分相同,不更新下标,因此会保留先出现的较小下标。
if (average > top_average) {
top_average = average;
top_idx = student_idx;
}
}
return top_idx;
}
score_stats.h
#ifndef SCORE_STATS_H
#define SCORE_STATS_H
// 本题固定为 4 名学生、每名学生 3 门课程。
// 将数量定义为常量,避免在多个文件中重复写数字。
#define STUDENT_COUNT 4
#define COURSE_COUNT 3
// 计算下标为 student_idx 的学生的平均分。
// student_idx 使用从 0 开始的数组下标,有效范围是 0 到 STUDENT_COUNT - 1。
float calcAverage(float score[STUDENT_COUNT][COURSE_COUNT], int student_idx);
// 查找平均分最高的学生,返回其数组下标。
// 如果最高平均分并列,返回下标较小的学生。
int findTopStudent(float score[STUDENT_COUNT][COURSE_COUNT]);
#endif // SCORE_STATS_H
第3题:结构体实现复数乘法运算
main.c
#include <stdio.h>
#include "complex.h"
int main(void)
{
Complex c1;
Complex c2;
Complex result;
// 依次输入两个复数的实部和虚部系数,例如:3 2 1 4。
if (scanf("%d %d %d %d", &c1.real, &c1.im, &c2.real, &c2.im) != 4) {
fprintf(stderr, "输入错误:请依次输入两个复数的实部和虚部整数。\n");
return 1;
}
// 调用独立函数计算乘积。
result = complexMult(c1, c2);
// %+d 会在正数前显示 +,负数前显示 -,从而正确呈现实部与虚部。
// 例如结果会输出为 -5+14i 或 3-2i 的形式。
printf("%d%+di\n", result.real, result.im);
return 0;
}
complex.c
#include "complex.h"
Complex complexMult(Complex c1, Complex c2)
{
Complex result;
// 根据 (a + bi)(c + di) = (ac - bd) + (ad + bc)i 计算。
// 实部为两个实部相乘,再减去两个虚部系数的乘积。
result.real = c1.real * c2.real - c1.im * c2.im;
// 虚部系数为“第一个实部 × 第二个虚部”加上“第一个虚部 × 第二个实部”。
result.im = c1.real * c2.im + c1.im * c2.real;
// 结构体可以作为函数返回值,把完整的复数结果返回给调用者。
return result;
}
complex.h
#ifndef COMPLEX_H
#define COMPLEX_H
// 用结构体表示复数:real 是实部,im 是虚部的系数。
// 例如 3 + 2i 表示为 real = 3、im = 2。
typedef struct {
int real;
int im;
} Complex;
// 接收两个 Complex 结构体,返回它们相乘得到的新复数。
// 参数按值传递,函数不会直接修改调用方的两个复数。
Complex complexMult(Complex c1, Complex c2);
#endif // COMPLEX_H
第4题:枚举类型实现小球颜色组合枚举
main.c
#include "color_combinations.h"
int main(void)
{
// 组合枚举和输出由函数完成,主函数只负责调用。
printColorCombinations();
return 0;
}
color_combinations.c
#include <stdio.h>
#include "color_combinations.h"
// 将枚举值转换为对应的中文名称。
// 使用 switch 分支逐个匹配枚举值,避免使用字符串数组循环输出组合。
static const char *colorName(enum Color color)
{
switch (color) {
case Red:
return "红色";
case Yellow:
return "黄色";
case White:
return "白色";
default:
return "未知颜色";
}
}
void printColorCombinations(void)
{
// 外层循环依次选择第一种颜色:红、黄、白。
for (enum Color first = Red; first <= White;
first = (enum Color)(first + 1)) {
// 第二种颜色从 first + 1 开始,保证两种颜色不相同。
// 这种遍历方式也避免了红黄与黄红被重复输出。
for (enum Color second = (enum Color)(first + 1);
second <= White;
second = (enum Color)(second + 1)) {
printf("%s - %s\n", colorName(first), colorName(second));
}
}
}
color_combinations.h
#ifndef COLOR_COMBINATIONS_H
#define COLOR_COMBINATIONS_H
// 枚举值默认从 0 开始递增,分别对应红、黄、白三种颜色。
// 这里使用枚举类型表示颜色,而不是用整数或字符串数组代替。
enum Color {
Red,
Yellow,
White
};
// 遍历枚举值并输出所有不重复的两两不同颜色组合。
void printColorCombinations(void);
#endif // COLOR_COMBINATIONS_H
第5题:人员信息管理系统(结构体嵌套共用体)
main.c
#include <stdio.h>
#include "person.h"
int main(void)
{
// 初始化 3 名人员,数组中的每个元素都是一个 Person 结构体。
// 使用指定成员初始化,可以直观看到每个人的身份以及对应的共用体成员。
Person people[3] = {
{ .name = "张三", .id = 1001, .role = 's', .info.score = 92.5f },
{ .name = "李老师", .id = 2001, .role = 't', .info.subject = "C语言基础" },
{ .name = "王五", .id = 1002, .role = 's', .info.score = 88.0f }
};
// 将结构体数组中的每个人依次传给 printPerson。
for (int i = 0; i < 3; ++i) {
printPerson(people[i]);
}
// personCount 由 printPerson 更新,主函数只负责在最后输出统计结果。
printf("人员总数量:%d\n", personCount);
return 0;
}
person.c
#include <stdio.h>
#include "person.h"
// 全局变量只在一个 .c 文件中定义一次,初始值为 0。
int personCount = 0;
void printPerson(Person p)
{
// 共用体成员共享内存,必须先检查 role,再读取对应的有效成员。
// 学生读取 info.score,教师读取 info.subject,不能混用这两个成员。
if (p.role == 's') {
printf("学生:姓名=%s,编号=%d,分数=%.1f\n",
p.name, p.id, p.info.score);
} else if (p.role == 't') {
printf("教师:姓名=%s,编号=%d,授课科目=%s\n",
p.name, p.id, p.info.subject);
} else {
// 对未知身份标记作出提示,但仍然计入本次函数调用次数。
printf("未知身份:姓名=%s,编号=%d,标记=%c\n",
p.name, p.id, p.role);
}
// 每调用一次 printPerson,已输出人员计数加 1。
++personCount;
}
person.h
#ifndef PERSON_H
#define PERSON_H
// 姓名与授课科目的字符数组容量,包含字符串结尾的 '\0' 空字符。
#define NAME_LENGTH 32
#define SUBJECT_LENGTH 32
// 共用体的所有成员共享同一块内存。
// 一个人只能根据身份使用其中一个成员:学生使用 score,教师使用 subject。
typedef union {
float score;
char subject[SUBJECT_LENGTH];
} ScoreInfo;
// Person 将基础身份信息与 ScoreInfo 组合起来。
// role == 's' 表示学生,role == 't' 表示教师。
typedef struct {
char name[NAME_LENGTH];
int id;
char role;
ScoreInfo info;
} Person;
// 全局计数器在 person.c 中定义,在其他源文件中通过 extern 声明访问。
// 每调用一次 printPerson,无论身份类型如何,计数器都会增加 1。
extern int personCount;
// 根据人员身份输出对应信息,并将已输出人员数 personCount 加 1。
void printPerson(Person p);
#endif // PERSON_H