期中考试参考答案¶
本页最后更新于 ,内容可能过时
一、单选题(每题 2.5 分,共 20 分)¶
1 · 合法的关键字¶
下列选项,______ 是 C 语言中合法的关键字。
- A.
integer - B.
sizeof - C.
main - D.
array
答案
B
解析
判据是 C 的关键字表:32 个关键字中包含 sizeof(C89 起即为关键字,C23 仍然保留),另外三个都不是关键字。
- A:
integer只是普通标识符,C 里对应的类型名是int。 - B:
sizeof是关键字,用于求类型或对象的字节数 ✅ - C:
main是函数名,不是关键字。名字本身合法,只是与main函数重名会有冲突。 - D:
array是普通标识符,不是关键字。
真机实测(gcc 15.2.0,
-std=gnu23):分别写int integer;、int array;,都能通过语法检查;写int sizeof;报expected identifier or '(' before 'sizeof',说明它不能被当作名字使用。易错:
- 把
main当成关键字(它是合法的普通标识符,只是程序入口函数习惯叫这个名字)- 把
sizeof当成函数(它是运算符,不是库函数)
2 · || 与 && 的短路求值¶
如下程序段的输出结果是 ______
- A. 1, 1, 1
- B. 1, 1, 0
- C. 1, 0, 0
- D. 1, 0, 1
答案
A
解析
分两步:先确定初始化的值,再按短路规则确定哪些子表达式会被计算。
1 / 2是两个整数相除,结果是0,所以a、b、c都从 0 开始。a++先取值0(假),再让a变成 1。&&的优先级高于||,表达式按a++ || (++b && c++)结合。||左侧为假,右侧需要计算:++b使b先变成 1,值为 1(真),继续计算c++,取值为 0 后c变成 1。- 最终
a = 1、b = 1、c = 1。
真机实测(gcc 15.2.0,
-O2 -std=gnu23 -Wall):输出1, 1, 1;编译时给出两条警告suggest parentheses around '&&' within '||'和value computed is not used(整个||表达式的结果没有参与后续运算)。易错:
- 把
1 / 2当成 0.5(整型相除,结果截断为 0)- 以为
a++为假后右侧不再计算(||左侧为假时右侧必须计算)- 忽略
&&的优先级高于||,把式子读成(a++ || ++b) && c++
3 · do-while 的条件¶
执行如下循环时,说法正确的是 ______
- A. 循环体将执行一次
- B. 循环体将执行无限次
- C. 循环体不执行
- D. 系统将提示有语法错误
答案
B
解析
条件写的是 x = 1,是赋值表达式而不是比较:每轮都把 x 重新赋成 1,表达式的值也恒为 1,条件永远为真。
do-while的循环体至少执行一次,所以 C 不成立。- 条件中少了第二个
=,本意应为x == 1;按题意推导,循环不会自行结束,选 B。 - 该写法在语法上完全合法,D 不成立。
真机实测(gcc 15.2.0,
-O2 -std=gnu23 -Wall):程序在 2 秒内没有退出,被timeout终止(退出码 124)。
x = x * x 的增长没有上限
x 每轮平方,若干轮后超出 int 的表示范围;有符号整数溢出在标准中属于未定义行为。因此这段循环的性质是"条件恒为真、不会正常结束",而不是"保证无限执行"。考试按题意答 B。
易错:
- 把
x = 1读成x == 1,进而认为循环执行一次就结束- 以为
do-while的条件为假时循环体一次也不执行(先执行后判断)
4 · strlen 与 sizeof¶
若有定义 char s[] = "abc\0def";,则 strlen(s) 和 sizeof(s) 的值分别是
- A. 3 和 8
- B. 7 和 8
- C. 3 和 7
- D. 7 和 7
答案
A
解析
两个函数/运算符的口径不同:strlen 数到第一个 '\0' 为止,sizeof 是整个数组占的字节数。
- 字符串字面量
"abc\0def"里有a、b、c、\0、d、e、f共 7 个字符,另外编译器会在末尾自动补一个'\0'。 strlen(s)遇到中间那个'\0'就停,结果是 3。sizeof(s)统计整个数组,结果是 8。
真机实测(gcc 15.2.0,
-O2 -std=gnu23 -Wall):输出3 8。易错:
- 以为
strlen会跳过中间的'\0'继续往后数- 忘记字面量末尾自动补的
'\0',把sizeof算成 7
5 · 一维数组的初始化¶
对一维数组 int a[5]; 进行初始化,希望数组元素为 1, 2, 0, 0, 0,分别使用了如下方法
- (1)
int a[5] = {1, 2}; - (2)
int a[5] = {1, 2, 0, 0, 0}; - (3)
int a[5] = {1, 2, 0, 0, 0, 0}; - (4)
int a[5] = {1, 2, , , };
在上述初始化方法中正确的有 ______ 种
- A. 1
- B. 2
- C. 3
- D. 0
答案
B
解析
判据:初值个数可以少于元素个数(不足的补 0),不能多于;花括号里也不能留空的初值。
- (1) 合法 ✅:只给前两个元素,其余自动补 0,结果就是 1, 2, 0, 0, 0。
- (2) 合法 ✅:五个元素逐个给出,正是 1, 2, 0, 0, 0。
- (3) 不合法:六个初值给五个元素的数组,标准把初值过多视为约束违反。
- (4) 不合法:
,之间没有表达式,编译不过。
共 2 种,选 B。
真机实测(gcc 15.2.0,
-std=gnu23 -Wall):
- (1)、(2) 编译通过。
- (3) 给出警告
excess elements in array initializer,但 gcc 仍然生成代码,数组元素实测为1 2 0 0 0。按标准它不合法,按 gcc 的宽松处理它"能编译",本题按标准判为不正确。- (4) 报错
expected expression before ',' token,编译不过。
警告与错误不是一回事
gcc 对「初值过多」只发警告,该写法不符合标准,但程序照常生成。判卷以语言规则为准,不以编译器是否报错为准。
易错:
- 把 (3) 当成正确(初值 6 个,超过数组长度 5)
- 把 (4) 当成"省略的初值补 0"(省略只能在末尾整体省略,不能留空位)
6 · 结构化程序设计的三种基本结构¶
C 语言中用于结构化程序设计的 3 种基本结构是 ______
- A. 顺序结构, 选择结构, 循环结构
- B.
if,switch,break - C.
for,while,do-while - D.
if,for,continue
答案
A
解析
三种基本结构是顺序、选择、循环,与具体用哪条语句实现无关。
- A:顺序、选择、循环 ✅
- B:
break是跳转语句,不是基本结构。 - C:这是循环语句的三种写法,只对应"循环结构"一种。
- D:三种语句分属选择、循环、跳转,同样不是三种基本结构的名称。
易错:把"实现某一结构的语句"当成"结构本身"来选(B、C、D 都是这一类)。
7 · if 条件的等价写法¶
在 if(x) 条件中的 x 与下面条件表达式等价的是
- A.
x!=0 - B.
x==1 - C.
x!=1 - D.
x==0
答案
A
解析
C 的条件按"非零为真、零为假"判断,所以 if(x) 成立当且仅当 x != 0。
- A:
x != 0✅,与if(x)的判据一致。 - B:
x == 1把 2、3、-1 这类非零值排除掉了。 - C:
x != 1在x为 0 时也成立,与原条件相反。 - D:
x == 0正好与原条件相反。
易错:以为条件必须等于 1 才成立。
8 · 二维数组的说明¶
以下对二维数组 a 的正确说明是( )
- A.
int a[3][]; - B.
float a[3, 4]; - C.
double a[1][4]; - D.
float a(3)(4);
答案
C
解析
判据:二维数组的第二维长度必须写出来,两维各用一对方括号。
- A:第二维没有长度,元素类型成了不完整的
int[],报错array type has incomplete element type 'int[]'。 - B:维度之间用逗号,报错
expected ']' before ',' token。 - C:
double a[1][4]合法 ✅ - D:维度写成了圆括号,报错
expected declaration specifiers or '...' before numeric constant。
真机实测(gcc 15.2.0,
-std=gnu23):四段声明逐个做语法检查,只有 C 通过。易错:把
a[3, 4]当成"三维度写法"(逗号在方括号里是逗号表达式,不是分隔两个维度)。
二、程序阅读题(每题 5 分,共 20 分)¶
1 · 程序功能¶
以下程序的功能是 ______
#include <stdio.h>
int main() {
int i, s = 0;
for (i = 1; i < 10; i += 2)
s += i + 1;
printf("%d\n", s);
return 0;
}
答案
计算并输出 2+4+6+8+10 的和
解析
i 从 1 开始、每次加 2,取到 1、3、5、7、9;循环体累加的是 i + 1,即 2、4、6、8、10。
- 累加项:
2 + 4 + 6 + 8 + 10 = 30 - 输出的是这个和,所以功能是"计算并输出 2+4+6+8+10 的和"。
真机实测(gcc 15.2.0,
-O2 -std=gnu23 -Wall):输出30。易错:把累加项看成
i本身,误答"1+3+5+7+9 的和"(那是 25)。
2 · switch 穿透¶
以下程序运行所产生的输出是 ______
#include <stdio.h>
int main() {
int i, a = 0, b = 0, c = 0;
for (i = 0; i < 10; i++) {
switch (i % 4) {
case 0: a++;
case 1: b++;
case 2: c++; break;
case 3: a++; break;
}
}
printf("%d,%d,%d\n", a, b, c);
return 0;
}
答案
5,6,8
解析
i 取 0~9,i % 4 依次是 0、1、2、3 循环;case 0 和 case 1 后面没有 break,会继续往下执行。
i % 4 == 0(i = 0, 4, 8,共 3 次):a++后穿透执行case 1的b++、case 2的c++再break→ 每次加 a、b、c 各 1i % 4 == 1(i = 1, 5, 9,共 3 次):b++、c++→ b、c 各加 3i % 4 == 2(i = 2, 6,共 2 次):只c++→ c 加 2i % 4 == 3(i = 3, 7,共 2 次):a++后break→ a 加 2
汇总:a = 3 + 2 = 5,b = 3 + 3 = 6,c = 3 + 3 + 2 = 8。
真机实测(gcc 15.2.0,
-O2 -std=gnu23 -Wall):输出5,6,8。易错:
- 把每个
case当成独立的if,忽略穿透- 次数统计错误:0~9 里模 4 余 0 的是 0、4、8 三个,余 1 的是 1、5、9 三个
3 · continue 与 break¶
写出下面程序执行后的屏幕显示结果 ______
#include <stdio.h>
int main() {
int i, j = 0;
for (i = 0; i < 20; i++) {
if (i % 4 == 0) continue;
if (i - 9) j++;
else break;
}
printf("i=%d,j=%d\n", i, j);
return 0;
}
答案
i=9,j=6
解析
i % 4 == 0时continue,跳过本轮:0、4、8、12、16 都不参与后面的判断。if (i - 9)以差值作条件:i不等于 9 时差非零(真),j++;i等于 9 时差为 0(假),执行break。i从 0 递增到 9 的过程中,使j加 1 的是 1、2、3、5、6、7,共 6 次。i = 9时break退出循环,此时i保持为 9、j为 6。
真机实测(gcc 15.2.0,
-O2 -std=gnu23 -Wall):输出i=9,j=6。易错:
- 把
if (i - 9)当成"i 等于 9 时成立"(正好相反)- 忽略
break时i已递增到 9,把输出写成i=8
4 · while 条件中的自增¶
写出下面程序执行后的输出结果 ______
#include <stdio.h>
int main() {
int x = 0, y = 4, z = 2;
while (z-- > 0 && ++x < 3)
y = y - 1;
printf("%d,%d,%d\n", x, y, z);
return 0;
}
答案
2,2,-1
解析
z-- 是后缀自减,比较时用的是自减前的值;++x 是前缀自增,先加再用。逐轮看:
- 第 1 轮:
z取 2 比较(2 > 0 真),z变成 1;++x使x变 1(1 < 3 真)→y减为 3 - 第 2 轮:
z取 1 比较(真),z变成 0;++x使x变 2(真)→y减为 2 - 第 3 轮:
z取 0 比较(0 > 0 假),z变成 -1;&&短路,右侧不再计算,x保持 2
循环结束时 x = 2、y = 2、z = -1。
真机实测(gcc 15.2.0,
-O2 -std=gnu23 -Wall):输出2,2,-1。易错:
- 以为
z--比较时用的是减 1 之后的值(后缀先用后改)- 在第三轮仍去算
++x(&&左侧为假时右侧短路,x不再增加)- 以为循环结束时
z停在 0(比较那一次自减已经发生,z是 -1)
三、改错题(10 分)¶
注意:不得增行或删行,也不得更改程序结构。
1 · 正整数转换成数字字符序列¶
以下程序将输入的正整数转换成数字字符序列,输出转换结果
/* 1 */ #include <stdio.h>
/* 2 */
/* 3 */ int main()
/* 4 */ {
/* 5 */ char s[10];
/* 6 */ int i = 0, n, t;
/* 7 */ scanf("%d", n);
/* 8 */ if (n = 0) s[i++] = '0';
/* 9 */ else {
/* 10 */ t = n;
/* 11 */ while (t > 0) { s[i++] = t % 10 + '0'; t /= 10; }
/* 12 */ }
/* 13 */ while (i > 0) putchar(s[--i]);
/* 14 */ printf("\n");
/* 15 */ return 0;
/* 16 */ }
答案
答案可能不唯一,还在讨论当中
如果你的答案正确,且和下面的不同,请及时反馈给助教
- 第 7 行改为
scanf("%d", &n); - 第 8 行改为
if (n == 0) s[i++] = '0';
解析
第 7 行少取地址符:scanf 需要变量的地址才能写回,传 n 的值会被当成指针使用。
第 8 行把比较写成了赋值:n = 0 会把 n 清零,表达式的值恒为 0,if 分支永远不执行。
- 两处都改完之后,输入 0 走
if分支输出0;输入正整数走else分支逐位取余存进s,再逆序输出。
真机实测(gcc 15.2.0,
-O2 -std=gnu23 -Wall):
- 原样编译只给出两条警告(
format '%d' expects argument of type 'int *', but argument 2 has type 'int'、suggest parentheses around assignment used as truth value),程序仍能生成。运行时输入 12345 出现段错误(退出码 139),原因是scanf把n的未初始化值当成了地址。- 改完之后
scanf的格式串与参数类型一致,警告消失。输入 0 输出0,输入 7 输出7,输入 12345 输出12345。
第 8 行为什么不会让 if 恒为真
n = 0 的值是 0,按"非零为真"判断属于假,所以是 if 分支被跳过,不是恒为真。若写成 n = 1 才是恒为真。
易错:
- 只改第 7 行的
&,漏掉第 8 行的==- 把第 8 行改成
if (n = 0)之外的其他结构(题目不允许增删行、改结构)
2 · 累加数列¶
已知数列:S=1+1/(1×2)+1/(2×3)+1/(3×4)+...,以下程序输入一个阈值 eps,不断累加,直到当前项的值不大于 eps 时停止,输出累加和
/* 17 */ #include <stdio.h>
/* 18 */ int main()
/* 19 */ {
/* 20 */ int i;
/* 21 */ double t, s = 0, eps;
/* 22 */ scanf("%f", &eps);
/* 23 */ i = 1;
/* 24 */ t = 1.0;
/* 25 */ while (t > eps)
/* 26 */ {
/* 27 */ s = s + t;
/* 28 */ i++;
/* 29 */ t = 1 / (i * (i + 1));
/* 30 */ }
/* 31 */ printf("sum = %lf\n", s);
/* 32 */ return 0;
/* 33 */ }
答案
答案可能不唯一,还在讨论当中
如果你的答案正确,且和下面的不同,请及时反馈给助教
- 第 22 行改为
scanf("%lf", &eps); - 第 23 行改为
i = 0; - 第 29 行改为
t = 1.0 / (i * (i + 1.0));
解析
三处错误分别在第 22、23、29 行:
- 第 22 行:
eps是double,scanf读double要用%lf。%f按float *解释,只写 4 个字节,eps的值是错的。 - 第 23 行:序列首项是 1。循环体先把
t累加、再让i自增、最后按1/(i(i+1))生成下一项,所以进循环时i应为 0:第一次i++后是 1,下一项正是 1/(1×2)。写成i = 1会让第 2 项变成 1/(2×3),漏掉 1/2。 - 第 29 行:
1 / (i * (i + 1))是整数除法,结果恒为 0,t在第二轮变成 0,循环立即结束。
三处改完,t 依次为 1、1/2、1/6、1/12……,与题面数列一致。
真机实测(gcc 15.2.0,
-O2 -std=gnu23 -Wall):
- 原样编译给出警告
format '%f' expects argument of type 'float *', but argument 2 has type 'double *'。输入0.001时输出sum = 1.000000——整数除法让t立刻归零,只累加了首项。- 改完之后输入
0.001输出sum = 1.968750,输入0.000001输出sum = 1.999000,随阈值减小向 2 收敛。
第 29 行写成 1.0 / (i * (i + 1.0)) 的理由
分子 1.0 是 double,i * (i + 1.0) 也因为 i + 1.0 变成 double,整个除法按浮点做。只把分子写成 1.0 也能得到正确结果,两种写法等价。
易错:
- 只改第 22 行就认为改动完成(第 23、29 行仍然错)
- 漏掉第 23 行:
i = 1使第二项成为 1/(2×3),与题面数列错位- 改为
i = 0后是否出现1/(0×1)除零:不会,i++在生成下一项之前执行
四、完全程序题(每空 3 分,共 21 分)¶
1 · 数组线性查找¶
以下是一个数组线性查找的程序:从数组的第一个位置开始,找到 key 的第二个出现时,记录位置
#include <stdio.h>
int main() {
int a[10] = {3, 7, 2, 7, 9, 7, 1, 7, 5, 7};
int n = 10, key = 7, i, count = 0, pos = ______(1)______;
for (i = 0; i < n; i++) {
if (a[i] == key) {
______(2)______;
if (______(3)______) {
pos = i;
______(4)______;
}
}
}
if (pos != -1)
printf("第二次出现的位置: %d\n", pos);
else
printf("key 出现次数不足两次\n");
return 0;
}
答案
- (1) -1
- (2) count++
- (3) count == 2
- (4) break
解析
后面的 if (pos != -1) 说明 pos 的初值是"没找到"的标记,取 -1。
- (2) 每遇到一次
key都要计数,填count++。 - (3) 题目要的是"第二次出现",所以计数值到 2 时命中,填
count == 2。 - (4) 记录位置后不需要继续找,填
break结束循环。
按题意推导:a 中 key = 7 出现在下标 1、3、5、7、9,第二次出现在下标 3。
真机实测(gcc 15.2.0,
-O2 -std=gnu23 -Wall):补全后输出第二次出现的位置: 3。易错:
- (1) 填 0:那样即便
key只出现一次也会按"位置 0"输出,与pos != -1的判据冲突- (3) 填
count > 2:要到第三次出现才命中- (4) 漏填
break,pos会被后续出现的位置覆盖
2 · 选择排序¶
使用选择排序算法将数组 a 中的数值按从小到大的顺序排序,然后输出排序后的数组
选择排序的基本思想:每一趟从待排序的元素中选出最小的元素,与待排序部分的第一个元素交换,直到全部排完
#include <stdio.h>
int main() {
int a[] = {4, 45, 65, 43, 32, 3, 72, 27};
int n, i, j, minIndex, t;
n = ______(5)______;
for (i = 0; i < n - 1; i++) {
______(6)______;
for (j = i + 1; j < n; j++)
if (a[j] < ______(7)______)
minIndex = j;
if (minIndex != i) {
t = a[i];
a[i] = a[minIndex];
a[minIndex] = t;
}
}
return 0;
}
答案
- (5) sizeof(a) / sizeof(a[0])
- (6) minIndex = i
- (7) a[minIndex]
解析
- (5)
a没有显式给出长度,元素总数 = 数组总字节数 / 单个元素字节数。sizeof(a)是 32,sizeof(a[0])是 4,商为 8。 - (6) 每一趟先假定待排序部分的第一个元素最小,填
minIndex = i。 - (7) 内层循环逐个与当前记录的最小值比较,填
a[minIndex]。
按题意推导:每趟把未排序部分的最小值换到第 i 位,i 从 0 到 n - 2,结束后整体升序。
真机实测(gcc 15.2.0,
-O2 -std=gnu23 -Wall):补全后数组为3 4 27 32 43 45 65 72(原程序只排序不输出,这里补了一句打印用于核对)。
sizeof(a) 只在数组名上可用
sizeof(a) / sizeof(a[0]) 成立的前提是 a 还是数组类型。若 a 作为参数传进函数,它会退化成指针,sizeof(a) 变成指针大小,这个除法就不再是元素个数。
易错:
- (6) 填
minIndex = 0:从第二趟起把已排好的元素也纳入比较范围- (7) 填
a[j]:a[j]与自身比较恒不成立,minIndex不再更新
五、编程题(第 1 题 14 分,第 2 题 15 分,共 29 分)¶
1 · 矩阵上下翻转¶
输入一个 n 行 m 列(0<n,m<20)的矩阵 A,输出它上下翻转(第 1 行与最后一行交换,第 2 行与倒数第 2 行交换……)后的矩阵 B
输入:
输出:
参考代码:
#include <stdio.h>
int main() {
int n, m, i, j;
int a[20][20];
scanf("%d %d", &n, &m);
for (i = 0; i < n; i++)
for (j = 0; j < m; j++)
scanf("%d", &a[i][j]);
for (i = n - 1; i >= 0; i--) {
for (j = 0; j < m; j++) {
if (j > 0)
printf(" ");
printf("%d", a[i][j]);
}
printf("\n");
}
return 0;
}
解析
思路一(参考代码采用):先按行读入,再从最后一行开始逐行输出。翻转后的第 1 行就是原来的第 n 行,第 2 行是原来的第 n-1 行,因此输出时行号从 n - 1 递减到 0 即可。
- 数组按上界开成
a[20][20]:题目给定0 < n, m < 20,开 20 足够。 - 每行元素之间输出一个空格,用
if (j > 0)控制不在行首补空格;每行结束输出换行。 - 时间与空间都是 O(nm)。
思路二:先在数组里把第 i 行与第 n-1-i 行整行交换,再按正常顺序输出。两种做法结果相同,参考代码不执行交换。
真机实测(gcc 15.2.0,
-O2 -std=gnu23 -Wall):输入题面样例,输出与题目给出的输出逐行一致;另测 1 行 3 列(1 3/1 2 3),输出仍为1 2 3,行号递减的循环对n = 1也成立。
不用真的构造矩阵 B
题目说"输出翻转后的矩阵 B",按行倒序输出即可达到同样效果,不必额外开一个数组存 B。判分依据输出结果,不检查中间是否交换过。
易错:
- 行循环写成
i = 0; i < n; i++,内层再按n-1-i取行,容易与列下标写混- 每个数字后面都打印空格,行尾多一个空格
- 把
n和m的顺序写反
2 · 删除字符串中的字符¶
编写程序,输入两个字符串 str1 和 str2(长度均小于 120),删除 str1 中所有在 str2 出现的字符(大写和小写都要删除),输出处理后的 str1。要求:不能使用其他数组
例:
输入:
输出:
参考代码:
#include <stdio.h>
#include <string.h>
int main() {
char str1[120], str2[120];
char c1, c2;
int i, j, is_remove;
fgets(str1, 120, stdin);
fgets(str2, 120, stdin);
str1[strcspn(str1, "\n")] = '\0';
str2[strcspn(str2, "\n")] = '\0';
for (i = 0; str1[i] != '\0'; i++) {
is_remove = 0;
for (j = 0; str2[j] != '\0'; j++) {
c1 = str1[i];
c2 = str2[j];
if (c1 >= 'A' && c1 <= 'Z')
c1 = c1 - 'A' + 'a';
if (c2 >= 'A' && c2 <= 'Z')
c2 = c2 - 'A' + 'a';
if (c1 == c2) {
is_remove = 1;
break;
}
}
if (!is_remove)
putchar(str1[i]);
}
putchar('\n');
return 0;
}
另一版(用 tolower 转换大小写):
#include <stdio.h>
#include <string.h>
#include <ctype.h>
int main() {
char str1[120], str2[120];
int i, j, is_remove;
fgets(str1, 120, stdin);
fgets(str2, 120, stdin);
str1[strcspn(str1, "\n")] = '\0';
str2[strcspn(str2, "\n")] = '\0';
for (i = 0; str1[i] != '\0'; i++) {
is_remove = 0;
for (j = 0; str2[j] != '\0'; j++) {
if (tolower(str1[i]) == tolower(str2[j])) {
is_remove = 1;
break;
}
}
if (!is_remove)
putchar(str1[i]);
}
putchar('\n');
return 0;
}
解析
逐个检查 str1 的每个字符,看它在 str2 里有没有出现过(忽略大小写):出现过就不输出,没出现过就输出。
- 大小写统一有两种写法:手写
if (c >= 'A' && c <= 'Z') c = c - 'A' + 'a';,或调用tolower。两版其余部分相同。 - 题目要求"不能使用其他数组",所以不另开数组存结果。参考代码直接
putchar输出;也可以把保留下来的字符按str1[k++]写回str1(k不会超过i,不会覆盖尚未检查的字符),最后补'\0'再printf("%s\n", str1)。 - 字符串长度小于 120,数组开 120 足够;
fgets最多读入 119 个字符,再补'\0'。 fgets会把行末换行也读进来,所以用strcspn找到换行并改成'\0'。不处理的话,str1末尾的换行会随字符一起输出;str2末尾的换行还会参与比较,把str1的换行当成"要删除的字符"。- 时间 O(|str1| × |str2|),空间只用两个输入数组。
真机实测(gcc 15.2.0,
-O2 -std=gnu23 -Wall):
- 题面样例(
I love C Language./love)输出I C anguag.,与题目一致。中间两个空格来自I后面的空格和被删空的love部分。- 第二行不以换行结尾(
love后直接 EOF)时输出不变,说明换行处理对两种输入都成立。- 另测
Hello World/lo,输出He Wrd,覆盖了大写H、W的情况。
为什么用 strcspn 而不是 strlen(str1) - 1
strlen(str1) - 1 假定最后一定是换行,输入末尾没有换行时会删掉最后一个正常字符。strcspn(str1, "\n") 返回第一个换行或 '\0' 的位置,两种情况都安全。
易错:
- 忘了
fgets的换行,输出多一个空行- 用
scanf("%s", str1)读入,遇到空格就停止,只读到第一个词- 只按原样比较,未把
H与h视为同一字符- 另开一个数组存结果,违反"不能使用其他数组"的约束