高级语言程序设计课程第五次个人作业
这个作业属于哪个课程: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),占用各自独立的存储空间,互不影响。
六、编写运行过程中遇到的问题与解决
-
get_first()函数在文件末尾(EOF)死循环:最初参考实现里丢弃剩余字符的循环写成while (getchar() != '\n') continue;,当输入不是以换行结尾(比如管道输入结束、直接遇到 EOF)时,getchar()一直返回 EOF(-1),而 EOF 不等于'\n',导致死循环、程序卡住不退出。解决:把循环条件改成while ((c = getchar()) != '\n' && c != EOF),并在跳过空白后判断if (ch == EOF) return EOF;,正确处理文件结束。 -
调和平均数公式写错:网上参考实现把调和平均数写成了
1/(1/x + 1/y),漏掉了"先取倒数再取平均"里的"平均"(除以 2),结果会变成正确值的一半。解决:按教材定义1.0 / ((1.0/x + 1.0/y) / 2.0)实现,并用 2 和 6 验证(正确结果是 3 而不是 1.5)。 -
isspace未声明警告:第 8-7 题用到了isspace()但忘了包含<ctype.h>,编译时出现implicit declaration of function 'isspace'警告。解决:在文件头部加上#include <ctype.h>后警告消除。 -
scanf里%c不会跳过空白:题 4 混合读入时,%d会自动跳过前导空格和回车,但%c不会,如果直接写scanf("%c%d%c"),中间的整数后面残留的空白会被最后一个%c吃掉。解决:在两个%c前各加一个空格(" %c"),让%c也跳过任意前导空白。 -
多文件编译时只编一个文件报"未定义引用":第一次只编译 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 语言的输入输出模型和函数机制有了更扎实的掌握。
浙公网安备 33010602011771号