高级语言程序设计课程第五次个人作业

这个作业属于哪个课程:https://edu.cnblogs.com/campus/fzu/2026gjyycxsj

这个作业要求在哪里:https://edu.cnblogs.com/campus/fzu/2026gjyycxsj/homework/16751

学号:010100223

姓名:武子腾


高级语言程序设计课程第五次个人作业

本次作业主要练习:getchar/putchar 字符输入输出、文件重定向、scanf 混合输入、函数与多源代码文件编译、变量地址等。


一、设计题

题1:getchar 和 putchar 联合使用,遇到空格符结束

题目:设计一个 getchar 和 putchar 联合使用的程序,使得程序遇到空格符时结束。

代码:

#include <stdio.h>

int main(void)
{
    char ch;

    printf("请输入一串字符(遇到空格结束):\n");
    while ((ch = getchar()) != ' ')      /* 遇到空格符就结束 */
        putchar(ch);                     /* 用 putchar 逐个回显 */
    printf("\n已遇到空格,程序结束。\n");

    return 0;
}

运行结果(输入 Hello World 2026):

请输入一串字符(遇到空格结束):
Hello
已遇到空格,程序结束。

说明:程序逐个读入字符并用 putchar 回显,一读到空格(' ')就退出循环,所以只输出了第一个单词 "Hello"。


题2:in.txt 和 out.txt 文件重定向,小写转大写

题目:建立一个 in.txt 和 out.txt,分别使用这两个文件进行重定位(重定向)代替键盘输入和屏幕输出。in.txt 存放一些字符(小写字符、大写字符、数字字符),程序将这些小写字符转换成大写字符,然后全部输出到 out.txt。

代码:

#include <stdio.h>
#include <ctype.h>

int main(void)
{
    int ch;

    while ((ch = getchar()) != EOF)
        putchar(toupper(ch));            /* 小写转大写,其余原样输出 */

    return 0;
}

in.txt 原始内容:

HelloWorld123abcXYZ456

运行命令:

design2_redirect.exe < in.txt > out.txt

out.txt 输出内容(小写已转大写):

HELLOWORLD123ABCXYZ456

说明:程序本身和普通程序一样用 getchar/putchar 读写标准输入输出,但通过命令行 < 和 > 把标准输入重定向到 in.txt、把标准输出重定向到 out.txt,实现了"文件代替键盘和屏幕"。


题3:四则运算("a运算b"形式)

题目:从键盘上输入数据并进行加、减、乘、除四则运算(以"a运算b"的形式输入),判断输入的数据是否可以进行计算,若能计算,则输出计算结果,保留小数点后 2 位,否则显示"错误输入"。另外,碰到除数为 0 时能提示 "division by zero"。

代码:

#include <stdio.h>

int main(void)
{
    double a, b, result;
    char op;

    printf("请输入运算(形如 a运算b,例如 3.5+2 或 8/0):\n");
    if (scanf("%lf %c %lf", &a, &op, &b) != 3)
    {
        printf("错误输入\n");
        return 0;
    }

    switch (op)
    {
        case '+': result = a + b; break;
        case '-': result = a - b; break;
        case '*': result = a * b; break;
        case '/':
            if (b == 0)
            {
                printf("division by zero\n");
                return 0;
            }
            result = a / b;
            break;
        default:
            printf("错误输入\n");
            return 0;
    }

    printf("计算结果:%.2f\n", result);   /* 保留小数点后 2 位 */
    return 0;
}

运行结果(三种情况):

=== 测试1:正常加法 3.5+2 ===
请输入运算(形如 a运算b,例如 3.5+2 或 8/0):
计算结果:5.50

=== 测试2:除数为 0:8/0 ===
请输入运算(形如 a运算b,例如 3.5+2 或 8/0):
division by zero

=== 测试3:非法运算符 3.5@2 ===
请输入运算(形如 a运算b,例如 3.5+2 或 8/0):
错误输入

说明:用 scanf("%lf %c %lf", ...) 按"数 运算符 数"读取,运算符用 switch 分支处理;除数为 0 时提前返回并提示 division by zero,非法运算符走 default 提示"错误输入"。结果用 %.2f 保留两位小数。


题4:混合读入字符、数值

题目:先读入一个字符,再读入一个整数,最后又读入一个字符;然后分别输出它们。输入格式为「n 个空格和/或回车,一个字符,m 个空格和/或回车,一个整数,k 个空格和/或回车,一个字符」;输出格式为「字符整数字符」。

代码:

#include <stdio.h>

int main(void)
{
    char c1, c2;
    int num;

    printf("请输入:字符 整数 字符(可用任意个空格/回车分隔):\n");
    /* scanf 中 %c 前的空格会跳过任意前导空白(空格/回车) */
    scanf(" %c %d %c", &c1, &num, &c2);

    printf("%c%d%c\n", c1, num, c2);     /* 输出格式:字符 整数 字符 */

    return 0;
}

运行结果(输入 a 123 b,字符间夹了多个空格):

请输入:字符 整数 字符(可用任意个空格/回车分隔):
a123b

说明:scanf 里 %d 会自动跳过前导空白,而 %c 不会,所以在两个 %c 前各加一个空格(" %c"),让它也跳过任意个空格或回车,从而能正确处理"任意个空白分隔"的输入。


二、第8章 8.11 编程练习(第3~7题)

第3题:统计大小写字母个数

题目:编写一个程序,在遇到 EOF 之前,把输入作为字符流读取。程序要报告输入中的大写字母和小写字母的个数。

代码:

#include <stdio.h>
#include <ctype.h>

int main(void)
{
    int lower = 0, upper = 0;
    char ch;

    printf("请输入文本(按 Ctrl+Z 结束输入):\n");
    while ((ch = getchar()) != EOF)
    {
        if (islower(ch))               /* 小写字母 */
            lower++;
        else if (isupper(ch))          /* 大写字母 */
            upper++;
    }

    printf("大写字母:%d 个,小写字母:%d 个\n", upper, lower);

    return 0;
}

运行结果(输入 HelloWorld123):

请输入文本(按 Ctrl+Z 结束输入):
大写字母:2 个,小写字母:8 个

第4题:统计平均每个单词的字母数

题目:编写一个程序,在遇到 EOF 之前,把输入作为字符流读取。程序要报告平均每个单词的字母数(不要把空白字符统计为单词的字母)。

代码:

#include <stdio.h>
#include <ctype.h>

int main(void)
{
    char ch;
    int inword = 0;          /* 是否在单词中:1 是 0 否 */
    int words = 0;           /* 单词数 */
    int letters = 0;         /* 字母数(不含空白和标点) */

    printf("请输入英文文本(按 Ctrl+Z 结束输入):\n");
    while ((ch = getchar()) != EOF)
    {
        if (!isspace(ch) && !ispunct(ch))    /* 是字母 */
        {
            letters++;
            if (!inword)                    /* 新单词开始 */
            {
                inword = 1;
                words++;
            }
        }
        else if (isspace(ch) || ispunct(ch)) /* 空白或标点 */
            inword = 0;                      /* 单词结束 */
    }

    printf("共 %d 个单词,%d 个字母\n", words, letters);
    if (words > 0)
        printf("平均每个单词 %.2f 个字母\n",
               (double)letters / (double)words);

    return 0;
}

运行结果(输入 Hello world, I love C):

请输入英文文本(按 Ctrl+Z 结束输入):
共 5 个单词,16 个字母
平均每个单词 3.20 个字母

说明:用一个标志变量 inword 记录"是否处于单词内部",从非字母切换到字母时说明一个新单词开始(words++),遇到空白或标点则离开单词,从而正确统计单词数。


第5题:猜数字(二分法)

题目:修改程序,让用户心里想一个 1~100 的整数,程序用"二分法"来猜。用户用 y(猜对)/s(猜小)/b(猜大)回答。

代码:

#include <stdio.h>
#define LOW 1
#define HIGH 100

int main(void)
{
    char response;
    int guess;
    int low = LOW, high = HIGH;

    guess = (low + high) / 2;
    printf("心里想一个 %d~%d 的整数,我来猜。\n", LOW, HIGH);
    printf("猜大了回 b,猜小了回 s,猜对了回 y。\n");
    printf("是这个数吗?%d\n", guess);

    while ((response = getchar()) != 'y')
    {
        if (response == 's')                 /* 猜小了,提高下限 */
        {
            low = guess;
            guess = (guess + high) / 2;
        }
        else if (response == 'b')            /* 猜大了,降低上限 */
        {
            high = guess;
            guess = (guess + low) / 2;
        }
        else
            printf("我只能理解 y、s、b 三个字母。\n");

        while (getchar() != '\n')            /* 跳过本行剩余输入 */
            continue;

        printf("那么,是 %d 吗?\n", guess);
    }
    printf("我就知道我能猜中!\n");

    return 0;
}

运行结果(心里想的数是 75,先回 s 再回 y):

心里想一个 1~100 的整数,我来猜。
猜大了回 b,猜小了回 s,猜对了回 y。
是这个数吗?50
那么,是 75 吗?
我就知道我能猜中!

说明:二分法每次把范围缩小一半——先猜中点 50,用户回 s(猜小了),下限提到 50,再猜 (50+100)/2 = 75 命中。最多 log₂(100)≈7 次就能猜中任意数。


第6题:get_first() 函数

题目:设计一个 get_first() 函数,它返回读取到的第一个非空白字符,并丢弃输入行中的剩余字符。

代码:

#include <stdio.h>
#include <ctype.h>

char get_first(void);                        /* 函数原型 */

int main(void)
{
    char ch;

    printf("请输入字符(按 Ctrl+Z 结束):\n");
    while ((ch = get_first()) != EOF)
    {
        printf("读到的第一个非空白字符是:%c\n", ch);
    }

    return 0;
}

/* 返回第一个非空白字符,并丢弃本行剩余内容 */
char get_first(void)
{
    int ch, c;

    while (isspace(ch = getchar()))          /* 跳过空白,取第一个非空白 */
        continue;
    if (ch == EOF)                           /* 文件末尾直接返回 EOF */
        return EOF;

    while ((c = getchar()) != '\n' && c != EOF)   /* 丢弃本行剩余 */
        continue;

    return ch;
}

运行结果(输入 abc):

请输入字符(按 Ctrl+Z 结束):
读到的第一个非空白字符是:a

说明:第一个循环跳过所有空白直到读到非空白字符;第二个循环把本行剩余字符(直到换行或文件末尾)全部丢弃。


第7题:工资计算程序(带菜单)

题目:修改练习 8 的工资程序,添加一个菜单,让用户选择工资等级(a/b/c/d 对应 8.75/9.33/10.00/11.20 美元/小时),用 get_choice() 显示菜单并读取选择、get_first() 读取第一个非空白字符。

代码:

#include <stdio.h>
#include <ctype.h>
#define BASE1 8.75
#define BASE2 9.33
#define BASE3 10.00
#define BASE4 11.20
#define TIME 40         /* 超过 40 小时为加班 */
#define MUL 1.5         /* 加班按 1.5 倍 */
#define RATE1 0.15      /* 前 300 美元税率 */
#define RATE2 0.20      /* 300~450 美元税率 */
#define RATE3 0.25      /* 超 450 美元税率 */
#define BREAK1 300
#define BREAK2 450

char get_choice(void);
char get_first(void);
void calculate(double base);

int main(void)
{
    double base = 0;
    char choice;

    while ((choice = get_choice()) != 'q')
    {
        switch (choice)
        {
            case 'a': base = BASE1; break;
            case 'b': base = BASE2; break;
            case 'c': base = BASE3; break;
            case 'd': base = BASE4; break;
            default:
                printf("请回答 a、b、c、d 或 q。\n");
                continue;
        }
        calculate(base);
    }
    printf("再见!\n");

    return 0;
}

/* 显示菜单并读取选择 */
char get_choice(void)
{
    char ch;

    printf("----------------------------------\n");
    printf("请选择工资等级:\n");
    printf("a) $8.75/小时    b) $9.33/小时\n");
    printf("c) $10.00/小时   d) $11.20/小时\n");
    printf("q) 退出\n");
    printf("----------------------------------\n");

    ch = get_first();
    return ch;
}

/* 返回第一个非空白字符,丢弃本行剩余 */
char get_first(void)
{
    int ch, c;

    while (isspace(ch = getchar()))
        continue;
    if (ch == EOF)
        return EOF;
    while ((c = getchar()) != '\n' && c != EOF)
        continue;
    return ch;
}

/* 计算并输出工资 */
void calculate(double base)
{
    double hour, gross, tax;

    printf("请输入本周工作小时数:");
    scanf("%lf", &hour);
    while (getchar() != '\n')
        continue;

    if (hour <= TIME)
        gross = hour * base;
    else
        gross = TIME * base + (hour - TIME) * MUL * base;

    if (gross <= BREAK1)
        tax = gross * RATE1;
    else if (gross <= BREAK2)
        tax = BREAK1 * RATE1 + (gross - BREAK1) * RATE2;
    else
        tax = BREAK1 * RATE1 + (BREAK2 - BREAK1) * RATE2
            + (gross - BREAK2) * RATE3;

    printf("总收入 $%.2f,税金 $%.2f,净收入 $%.2f\n",
           gross, tax, gross - tax);
}

运行结果(选 a,工作 45 小时,然后 q 退出):

----------------------------------
请选择工资等级:
a) $8.75/小时    b) $9.33/小时
c) $10.00/小时   d) $11.20/小时
q) 退出
----------------------------------
请输入本周工作小时数:总收入 $415.62,税金 $68.12,净收入 $347.50
----------------------------------
请选择工资等级:
a) $8.75/小时    b) $9.33/小时
c) $10.00/小时   d) $11.20/小时
q) 退出
----------------------------------
再见!

验证:总收入 = 40×8.75 + 5×8.75×1.5 = 350 + 65.625 = 415.625;税金 = 300×0.15 + 115.625×0.2 = 45 + 23.125 = 68.125;净收入 = 415.625 − 68.125 = 347.5,与输出一致。


三、第9章 9.11 编程练习(第1~4、8、9、11题)

第1题:min() 返回较小值

题目:设计一个函数 min(x, y),返回两个 double 类型数值中较小的那个,并写一个简单的驱动程序测试。

代码:

#include <stdio.h>

double min(double x, double y);          /* 函数原型 */

int main(void)
{
    double a, b;

    printf("请输入两个数:");
    scanf("%lf %lf", &a, &b);
    printf("较小的数是:%f\n", min(a, b));

    return 0;
}

/* 返回两个 double 中的较小值 */
double min(double x, double y)
{
    return (x < y ? x : y);
}

运行结果(输入 3.5 2.5):

请输入两个数:较小的数是:2.500000

第2题:chline() 打印字符

题目:设计一个函数 chline(ch, i, j),在 i 行中打印字符 ch,每行打印 j 次。

代码:

#include <stdio.h>

void chline(char ch, int i, int j);      /* 函数原型 */

int main(void)
{
    char ch;
    int row, col;

    printf("请输入要打印的字符:");
    ch = getchar();
    while (getchar() != '\n')
        continue;
    printf("请输入行数和每行个数:");
    scanf("%d %d", &row, &col);

    chline(ch, row, col);

    return 0;
}

/* 在 i 行打印字符 ch,每行 j 次 */
void chline(char ch, int i, int j)
{
    int r, c;
    for (r = 1; r <= i; r++)
    {
        for (c = 1; c <= j; c++)
            putchar(ch);
        putchar('\n');
    }
}

运行结果(输入字符 #、行数 3、每行 4 个):

请输入要打印的字符:请输入行数和每行个数:####
####
####

第3题:打印字符(三个参数)

题目:编写一个函数,接受 3 个参数:一个字符和两个整数。字符参数是待打印的字符,第 1 个整数指定一行中打印字符的次数,第 2 个整数指定打印的行数。

代码:

#include <stdio.h>

void myprint(char ch, int cols, int rows);   /* 函数原型 */

int main(void)
{
    printf("打印 5 行,每行 10 个 '*':\n");
    myprint('*', 10, 5);

    printf("\n打印 3 行,每行 6 个 '#':\n");
    myprint('#', 6, 3);

    return 0;
}

/* 字符 ch 为待打印字符,cols 为每行个数,rows 为行数 */
void myprint(char ch, int cols, int rows)
{
    int i, j;
    for (i = 0; i < rows; i++)
    {
        for (j = 0; j < cols; j++)
            putchar(ch);
        putchar('\n');
    }
}

运行结果:

打印 5 行,每行 10 个 '*':
**********
**********
**********
**********
**********

打印 3 行,每行 6 个 '#':
######
######
######

第4题:调和平均数

题目:编写一个函数,把两个 double 类型的数作为参数,返回它们的调和平均数。两个数的调和平均数 = 两数倒数的平均值的倒数,即 2/(1/x + 1/y)。

代码:

#include <stdio.h>

double har_mean(double x, double y);     /* 函数原型 */

int main(void)
{
    double a, b;

    printf("请输入两个数:");
    scanf("%lf %lf", &a, &b);
    printf("它们的调和平均数是:%f\n", har_mean(a, b));

    return 0;
}

/* 调和平均数 = 两数倒数平均值的倒数 = 2xy/(x+y) */
double har_mean(double x, double y)
{
    return 1.0 / ((1.0 / x + 1.0 / y) / 2.0);
}

运行结果(输入 2 6):

请输入两个数:它们的调和平均数是:3.000000

说明:2 和 6 的调和平均数 = 2/(1/2 + 1/6) = 2/(2/3) = 3,与输出一致。注意:网上有些参考实现写成了 1/(1/x + 1/y),少算了一次"取倒数后再取平均",结果会变成 1.5,是错的,这里按教材步骤 1.0 / ((1.0/x + 1.0/y) / 2.0) 实现才是正确的调和平均数。


第8题:power() 函数(循环实现)

题目:编写一个程序,用函数 power(n, p) 返回 n 的 p 次幂。如果 p 为 0,返回 1;如果 n 为 0 且 p 为 0,程序提示"未定义"并按 1 处理;如果 p 为负,用倒数计算。

代码:

#include <stdio.h>

double power(double n, int p);           /* 函数原型 */

int main(void)
{
    double x, xpow;
    int exp;

    printf("请输入底数和整数指数(输入 q 退出):\n");
    while (scanf("%lf %d", &x, &exp) == 2)
    {
        xpow = power(x, exp);
        printf("%.3g 的 %d 次幂是 %g\n", x, exp, xpow);
        printf("请输入下一对底数和指数(q 退出):\n");
    }
    printf("再见。\n");

    return 0;
}

/* 循环实现 n 的 p 次幂,处理 0^0、负指数、0 的负指数 */
double power(double n, int p)
{
    double pow = 1.0;
    int i;

    if (p == 0)
    {
        if (n == 0)
            printf("0 的 0 次幂未定义,这里按 1 处理\n");
        return 1.0;
    }
    else if (p > 0)
    {
        for (i = 0; i < p; i++)
            pow *= n;
    }
    else                                 /* p < 0 */
    {
        if (n == 0)
            return 0.0;                  /* 0 的负次幂无意义 */
        for (i = 0; i < -p; i++)
            pow /= n;
    }

    return pow;
}

运行结果(输入 2 3、5 -2、0 0 后 q 退出):

请输入底数和整数指数(输入 q 退出):
2 的 3 次幂是 8
请输入下一对底数和指数(q 退出):
5 的 -2 次幂是 0.04
请输入下一对底数和指数(q 退出):
0 的 0 次幂未定义,这里按 1 处理
0 的 0 次幂是 1
请输入下一对底数和指数(q 退出):
再见。

第9题:power() 函数(递归实现)

题目:把第 8 题的 power() 函数改用递归实现。

代码:

#include <stdio.h>

double power(double n, int p);           /* 函数原型 */

int main(void)
{
    double x, xpow;
    int exp;

    printf("请输入底数和整数指数(输入 q 退出):\n");
    while (scanf("%lf %d", &x, &exp) == 2)
    {
        xpow = power(x, exp);
        printf("%.3g 的 %d 次幂是 %g\n", x, exp, xpow);
        printf("请输入下一对底数和指数(q 退出):\n");
    }
    printf("再见。\n");

    return 0;
}

/* 递归实现 n 的 p 次幂 */
double power(double n, int p)
{
    if (p > 0)
        return n * power(n, p - 1);
    else if (p < 0)
    {
        if (n == 0)
            return 0.0;
        return (1.0 / n) * power(n, p + 1);
    }
    else                                 /* p == 0 */
        return 1.0;
}

运行结果(输入 2 3 后 q 退出):

请输入底数和整数指数(输入 q 退出):
2 的 3 次幂是 8
请输入下一对底数和指数(q 退出):
再见。

说明:递归实现的核心是 power(n, p) = n * power(n, p-1),p 逐层减到 0 时返回 1 作为递归出口;负指数则用 (1/n) * power(n, p+1) 把指数往 0 逼近。


第11题:Fibonacci 数列

题目:编写一个程序,让用户输入正整数 n,程序显示斐波那契数列的前 n 项(1, 1, 2, 3, 5, 8, 13, ...)。

代码:

#include <stdio.h>

unsigned long Fibonacci(unsigned n);     /* 函数原型 */

int main(void)
{
    unsigned int num;
    int count;

    printf("请输入斐波那契数列的项数(q 退出):");
    while (scanf("%u", &num) == 1)
    {
        for (count = 1; count <= num; count++)
            printf("%lu ", Fibonacci(count));
        printf("\n请输入下一项数(q 退出):");
    }
    printf("再见。\n");

    return 0;
}

/* 返回斐波那契数列第 n 项(1,1,2,3,5,8,...) */
unsigned long Fibonacci(unsigned n)
{
    int count;
    unsigned long n1 = 1, n2 = 1, temp;

    if (n <= 2)
        return 1;

    for (count = 3; count <= n; count++)
    {
        temp = n1 + n2;
        n1 = n2;
        n2 = temp;
    }
    return n2;
}

运行结果(输入 10 后 q 退出):

请输入斐波那契数列的项数(q 退出):1 1 2 3 5 8 13 21 34 55 
请输入下一项数(q 退出):再见。

说明:斐波那契数列从第三项起每一项等于前两项之和,用两个变量 n1、n2 滚动推进即可,不需要数组,空间复杂度 O(1)。


四、多源代码文件程序的编译

题目:设计一个多源代码文件程序的编译。first.c 包含主函数和一些头文件(second.h),second.c 是一些自定义的函数功能实现,second.h 是一些常量声明和 second.c 中自定义函数的函数原型声明。

second.h(常量声明 + 函数原型):

#ifndef SECOND_H
#define SECOND_H

#define PI 3.14159               /* 常量声明 */

double circle_area(double r);     /* 函数原型声明 */
double circle_circum(double r);   /* 函数原型声明 */

#endif

second.c(函数实现):

#include "second.h"

/* 计算圆面积 */
double circle_area(double r)
{
    return PI * r * r;
}

/* 计算圆周长 */
double circle_circum(double r)
{
    return 2 * PI * r;
}

first.c(主函数):

#include <stdio.h>
#include "second.h"

int main(void)
{
    double r;

    printf("请输入圆的半径:");
    scanf("%lf", &r);

    printf("圆的面积 = %.2f\n", circle_area(r));
    printf("圆的周长 = %.2f\n", circle_circum(r));

    return 0;
}

编译命令(把两个 .c 文件一起编译):

gcc first.c second.c -o first.exe

运行结果(输入半径 5):

请输入圆的半径:圆的面积 = 78.54
圆的周长 = 31.42

说明:多文件程序把「函数声明(.h)」「函数实现(.c)」「主函数(另一个 .c)」分开,编译时把所有 .c 一起交给编译器链接。second.h 用 #ifndef ... #endif 防止头文件被重复包含。


五、变量地址

题目:编写一个程序,在该程序中输出主函数内定义的变量的地址,以及函数中定义的同名变量的地址。

代码:

#include <stdio.h>

void func(void);                     /* 函数原型 */

int main(void)
{
    int x = 10;                      /* 主函数内的变量 x */

    printf("主函数中变量 x 的值 = %d,地址 = %p\n", x, &x);
    func();

    return 0;
}

void func(void)
{
    int x = 20;                      /* 函数内同名的变量 x */

    printf("函数中变量 x 的值 = %d,地址 = %p\n", x, &x);
    /* 两个 x 虽然同名,但地址不同,是各自独立的变量 */
}

运行结果:

主函数中变量 x 的值 = 10,地址 = 000000543d3ff95c
函数中变量 x 的值 = 20,地址 = 000000543d3ff91c

说明:两个 x 虽然同名,但分别属于主函数和 func 函数的局部变量,地址不同(...f95c 与 ...f91c),占用各自独立的存储空间,互不影响。


六、编写运行过程中遇到的问题与解决

  1. get_first() 函数在文件末尾(EOF)死循环:最初参考实现里丢弃剩余字符的循环写成 while (getchar() != '\n') continue;,当输入不是以换行结尾(比如管道输入结束、直接遇到 EOF)时,getchar() 一直返回 EOF(-1),而 EOF 不等于 '\n',导致死循环、程序卡住不退出。解决:把循环条件改成 while ((c = getchar()) != '\n' && c != EOF),并在跳过空白后判断 if (ch == EOF) return EOF;,正确处理文件结束。

  2. 调和平均数公式写错:网上参考实现把调和平均数写成了 1/(1/x + 1/y),漏掉了"先取倒数再取平均"里的"平均"(除以 2),结果会变成正确值的一半。解决:按教材定义 1.0 / ((1.0/x + 1.0/y) / 2.0) 实现,并用 2 和 6 验证(正确结果是 3 而不是 1.5)。

  3. isspace 未声明警告:第 8-7 题用到了 isspace() 但忘了包含 <ctype.h>,编译时出现 implicit declaration of function 'isspace' 警告。解决:在文件头部加上 #include <ctype.h> 后警告消除。

  4. scanf 里 %c 不会跳过空白:题 4 混合读入时,%d 会自动跳过前导空格和回车,但 %c 不会,如果直接写 scanf("%c%d%c"),中间的整数后面残留的空白会被最后一个 %c 吃掉。解决:在两个 %c 前各加一个空格(" %c"),让 %c 也跳过任意前导空白。

  5. 多文件编译时只编一个文件报"未定义引用":第一次只编译 first.c 时,链接器报 undefined reference to circle_area。解决:把 first.c 和 second.c 一起编译(gcc first.c second.c -o first.exe),让链接器能找到函数实现。


七、总结与思考

这次作业的主题是字符与数值的输入输出、函数与多文件组织,我收获最大的是以下三点:

  • getchar/putchar 与缓冲输入:getchar() 是带缓冲的,读到的是"一行"而不是"一个键",所以"丢弃本行剩余字符"这种操作很常用。这次踩的 EOF 死循环坑让我真正理解了:用 getchar() 写循环时,必须同时考虑"换行"和"文件末尾(EOF)"两种结束条件,缺一不可。

  • 文件重定向:程序里写的是标准输入输出(getchar/putchar),运行时不改代码、只改命令行(< in.txt > out.txt)就能让程序读写文件。这让我体会到"标准流"这种抽象设计的强大——程序与数据源解耦,键盘、屏幕、文件可以无缝替换。

  • 多文件与函数声明分离:把函数原型(.h)、函数实现(.c)、主函数(first.c)分开,是 C 语言模块化编程的基础。.h 里的 #ifndef 防重复包含、.c 里的实现、编译时多文件一起链接,这三件事串起来,我对"程序是怎么从多个源文件变成一个可执行文件"有了完整的认识。

  • 变量地址:同名变量在不同函数里地址不同,印证了"局部变量各自独立"——C 的变量作用域和生命周期不是玄学,而是实实在在的内存分配结果。用 %p 打印地址,让我第一次"看见"了栈上的内存布局。

整体上,这次作业让我从"只会写单文件小程序"迈向了"理解字符流、函数抽象和多文件工程结构",对 C 语言的输入输出模型和函数机制有了更扎实的掌握。


posted @ 2026-11-12 17:09  grylls_teng  阅读(3)  评论(0)    收藏  举报