跳转到内容

exam-89 试卷整理与详解

这份笔记是根据 89.pdf 整理出来的,原卷文件在本地路径:

/Users/wff/Downloads/89.pdf

这套卷子的 OCR 错字比较多,尤其是代码里的逗号、分号、括号、引号、returnstrcmpstrcat 等符号容易被识别错。我整理时按原卷红色标注和 C 语言语法一起校对,下面的代码都改成了更容易阅读和上机验证的写法。

这套题主要复习:

  • 函数参数传递、函数返回值、函数调用方式
  • 字符串比较、连接、复制
  • 一维数组、二维数组初始化和下标
  • 字符数组和字符串长度
  • 结构体、共用体、宏定义
  • switch、逻辑表达式、三目运算符
  • 程序改错和字符串处理程序设计
题号答案
1D
2A
3A
4C
5C
6A
7A
8C
9D
10B
11B
12B
13A
14C
15C
16B
17A
18A
19C
20C
21D
22B
23B
24B
25D
题号答案
115
2a=26,b=14,c=19
31
40
50
6类型
78
89
93
105.169
题号答案
1Y
2N
3Y
4Y
5N
6Y
7N
8Y
9N
10N

题目问:调用函数时,如果实参是简单变量,它和对应形参之间的数据传递方式是什么?

答案:D

详细解析:

C 语言中,普通变量作为实参时,采用值传递。

例如:

void fun(int x)
{
x = 100;
}
int a = 10;
fun(a);

函数里改的是形参 x,不会直接改外面的 a

容易踩坑点:

  • 以为函数里改了形参,外面的实参也会自动改变。

给新手的建议:

  • 普通变量传参:传的是值。
  • 想在函数里改外面的变量:通常要传地址。

题目问:以下对 C 语言函数的描述中,正确的是哪一个?

答案:A

正确说法:

C 程序必须由一个或一个以上的函数组成。

详细解析:

一个 C 程序至少要有一个 main 函数,因为程序从 main 开始执行。

其他选项的问题:

  • 有调用关系的函数不一定必须放在同一个源文件中。
  • 函数可以没有返回值,此时返回类型写 void
  • C 函数可以递归调用,但标准 C 语言不允许函数嵌套定义。

容易踩坑点:

  • 把“嵌套调用”和“嵌套定义”混为一谈。

给新手的建议:

  • C 函数可以互相调用,也可以自己调用自己,但不能在一个函数里面再定义另一个函数。

题目整理:

int max(int x, int y)
{
int z;
if (x > y)
z = x;
else
z = y;
return z;
}
int a = 45, b = 27, c = 0;
c = max(a, b);
printf("%d\n", c);

答案:A

详细解析:

调用:

max(45, 27)

因为 45 > 27,所以返回 45

最终输出:

45

容易踩坑点:

  • OCR 里 return、分号、逗号可能识别错,但题目核心是求两个数最大值。

给新手的建议:

  • 函数题先看函数功能,再代入实参。

题目问:判断两个字符串是否相等,正确的表达方式是哪一个?

答案:C

正确写法:

while (strcmp(s1, s2) == 0)

详细解析:

strcmp(s1, s2) 的返回规则:

  • 等于 0:两个字符串相等
  • 大于 0s1 大于 s2
  • 小于 0s1 小于 s2

不能写:

s1 == s2

这比较的是地址,不是字符串内容。

容易踩坑点:

  • 字符串比较内容不能直接用 ==

给新手的建议:

  • 字符串比较用 strcmp,字符串复制用 strcpy,字符串连接用 strcat

题目整理:

char array[] = "China";

问:数组 array 所占空间是多少?

答案:C

详细解析:

"China" 有 5 个可见字符:

C h i n a

字符串末尾还有一个 '\0'

所以一共占:

5 + 1 = 6

容易踩坑点:

  • 忘记字符串结束符 '\0' 也占一个字节。

给新手的建议:

  • 字符串初始化字符数组时,数组空间通常是“可见字符数 + 1”。

题目问:以下正确的函数首部形式是哪一个?

答案:A

正确写法:

double fun(int x, int y)

详细解析:

每个形参都要写类型:

int x, int y

不能写:

int x, y

另外,函数定义的函数首部后面直接跟函数体,不写分号。

容易踩坑点:

  • int x, y 当成两个形参都有类型。
  • 把函数声明和函数定义首部混淆。

给新手的建议:

  • 函数形参表里,每个参数都单独写类型,最稳。

题目整理:

char a1[] = "abc", a2[80] = "1234";

问:将 a1 串连接到 a2 串后面,应写哪一句?

答案:A

正确写法:

strcat(a2, a1);

详细解析:

strcat(目标串, 源串) 的意思是:把源串连接到目标串后面。

执行后 a2 变成:

1234abc

容易踩坑点:

  • strcat(a1, a2)strcat(a2, a1) 写反。

给新手的建议:

  • strcpystrcat 都是第一个参数被修改。

题目问:以下不能对二维数组 a 正确初始化的是哪一个?

答案:C

错误写法:

int a[2][3] = {{1, 2}, {3, 4}, {5, 6}};

详细解析:

a[2][3] 表示 2 行 3 列。

但上面的初始化写了 3 行:

{1, 2}
{3, 4}
{5, 6}

行数超过了定义的 2 行,所以错误。

容易踩坑点:

  • 少写初值可以补 0,多写超出数组范围就不行。

给新手的建议:

  • 二维数组初始化时,先数行数,再数列数。

题目问:以下不正确的定义语句是哪一个?

答案:D

错误写法:

int y[5] = {0, 1, 3, 5, 7, 9};

详细解析:

数组 y[5] 只能存 5 个元素,但初始化列表里给了 6 个值:

0 1 3 5 7 9

所以错误。

容易踩坑点:

  • 只看数组名和类型,没数初始化元素个数。

给新手的建议:

  • 数组初始化题,一定先比较“数组长度”和“初值个数”。

题目整理:

int a[][4] = {0, 0};

问:下面不正确的叙述是哪一个?

答案:B

详细解析:

二维数组第二维是 4,给了两个初值,所以第一维可以推断为 1

实际数组可以看作:

0 0 0 0

没显式写到的元素也会自动补 0

选项 B 说只有 a[0][0]a[0][1] 得到初值 0,其余元素得不到初值 0,这是错误的。

容易踩坑点:

  • 以为没写出来的元素就是随机值。

给新手的建议:

  • 数组只要有初始化列表,没写到的部分一般自动补 0

题目问:以下不能正确定义二维数组的是哪一个?

答案:B

错误写法:

int a[2][] = {{1, 2}, {3, 4}};

详细解析:

二维数组定义时,第二维不能省略。

正确写法应是:

int a[2][2] = {{1, 2}, {3, 4}};

容易踩坑点:

  • 以为已经写了初始化列表,所以第二维也可以省。

给新手的建议:

  • 二维数组可以省第一维,不能省第二维。

题目整理:

char a[10];

问:哪句不能从键盘给数组 a 的所有元素输入值?

答案:B

错误写法:

a = getchar();

详细解析:

数组名 a 不能作为赋值号左边整体赋值。

而且 getchar() 只能读一个字符,不可能直接给整个数组赋值。

容易踩坑点:

  • 把字符数组当成单个字符变量。

给新手的建议:

  • 字符数组整体输入可用 scanf("%s", a)
  • 逐个字符输入要写 a[i] = getchar()

题目整理:

fun(float x)
{
float y;
y = 3 * x - 4;
return y;
}

问:fun 函数返回值类型是什么?

答案:A

详细解析:

这个函数名前面没有显式写返回类型。

按老式 C 语言教材口径,如果函数没有显式说明返回类型,默认返回类型是 int

所以答案是 int

补充提醒:

现代 C 语言已经不推荐甚至不允许这种隐式 int 写法,实际写代码时应该明确写:

float fun(float x)

容易踩坑点:

  • 以为 return yyfloat,所以函数返回类型就是 float

给新手的建议:

  • 函数返回类型看函数名前面的类型,不是只看 return 后面的表达式。

题目问:下列描述中不正确的是哪一个?

答案:C

错误说法:

可以对整型数组进行整体输入、输出。

详细解析:

整型数组一般要逐个元素输入输出:

for (i = 0; i < n; i++)
scanf("%d", &a[i]);

字符数组如果存的是字符串,可以整体输入输出:

scanf("%s", s);
printf("%s", s);

容易踩坑点:

  • 把整型数组和字符数组字符串混成一类。

给新手的建议:

  • 整型数组逐个处理。
  • 字符数组作为字符串时,可以用 %s 整体处理。

题目问:以下对二维数组 a 的正确说明是哪一个?

答案:C

正确写法:

double a[1][4];

详细解析:

二维数组定义格式:

类型名 数组名[行数][列数];

其他选项的问题:

  • int a[3][] 第二维省略,错误。
  • float a(3)(4) 不是 C 语言数组定义。
  • float a(3,4) 也不是数组定义。

容易踩坑点:

  • 把数学里的括号写法带到 C 语言里。

给新手的建议:

  • 数组使用方括号 [],不是圆括号 ()

题目整理:

int a[10];

问:对 a 数组元素的正确引用是哪一个?

答案:B

正确写法:

a[10 - 10]

详细解析:

a[10 - 10] 等价于:

a[0]

这是合法数组元素。

a[10] 越界,因为合法下标是:

0 ~ 9

容易踩坑点:

  • 数组长度是 10,最后下标却是 9。

给新手的建议:

  • 数组下标从 0 开始,这个点要反复提醒自己。

题目整理:

func(rec1, rec2 + rec3, (rec4, rec5));

问:这个函数调用语句中有几个实参?

答案:A

详细解析:

最外层实参有 3 个:

rec1
rec2 + rec3
(rec4, rec5)

括号里的逗号属于逗号表达式,不增加实参个数。

容易踩坑点:

  • (rec4, rec5) 看成两个参数。

给新手的建议:

  • 数参数,只看最外层逗号。

题目问:能对一维数组正确初始化的语句是哪一个?

答案:A

正确写法:

int a[6] = {6 * 1};

详细解析:

这句是合法的,它会把第一个元素初始化为 6,其余元素补 0

6 0 0 0 0 0

其他选项的问题:

  • 数组初始化应该用 {},不能用 ()
  • 空的 {} 在传统教材语境里通常不作为正确写法。
  • int a[6] = {1,,,3}; 中间缺少表达式。

容易踩坑点:

  • 以为 {6 * 1} 会让 6 个元素都变成 1。

给新手的建议:

  • 初始化列表里有几个值,就按顺序初始化几个元素;剩下的补 0

题目问:以下程序段中,不能正确赋字符串的是哪一个?

答案:C

错误写法:

char s[10];
s = "abcdefg";

详细解析:

数组名不能作为赋值号左边整体赋值。

如果定义后再给字符数组赋字符串,应该用:

strcpy(s, "abcdefg");

容易踩坑点:

  • 把字符数组名当成普通变量。

给新手的建议:

  • 定义时可以初始化:char s[10] = "abcdefg";
  • 定义后再复制:strcpy(s, "abcdefg");

题目问:C 语言程序中,如果对函数类型不加显式说明,则函数的隐含说明类型是什么?

答案:C

详细解析:

按老式 C 语言教材口径,未显式说明返回类型时,默认是 int

例如:

fun(void)
{
return 1;
}

会被看作返回 int

补充提醒:

现代 C 语言不建议这样写,实际写代码要明确返回类型:

int fun(void)

容易踩坑点:

  • 以为没写类型就是 void

给新手的建议:

  • 考试按老教材记:不写返回类型,默认 int
  • 自己写代码时别省略返回类型。

题目整理:

int a[10];

问:给数组 a 的所有元素分别赋值为 1, 2, 3, ... 的语句是哪一个?

答案:D

正确写法:

for (i = 1; i < 11; i++)
a[i - 1] = i;

详细解析:

i110,而数组下标需要从 09

i = 1 -> a[0] = 1
i = 2 -> a[1] = 2
...
i = 10 -> a[9] = 10

容易踩坑点:

  • 循环变量从 1 开始时,忘记数组下标要减 1。

给新手的建议:

  • 值从 1 开始,下标从 0 开始,常见搭配是 a[i - 1] = i

题目问:C 语言中函数调用的方式有哪些?

答案:B

详细解析:

函数调用常见三种方式:

第一种,作为语句:

printf("hello\n");

第二种,作为表达式的一部分:

x = max(a, b);

第三种,作为另一个函数的参数:

printf("%d\n", max(a, b));

容易踩坑点:

  • 只记得函数可以单独调用,忘记它还可以放在表达式或实参里。

给新手的建议:

  • 看到函数调用,先看它所在位置:单独一句、等号右边、还是另一个函数的括号里。

题目整理:

int a[3][4] = {0};

答案:B

详细解析:

= {0} 会让数组所有元素都初始化为 0

可以理解为:

0 0 0 0
0 0 0 0
0 0 0 0

容易踩坑点:

  • 以为只有 a[0][0]0

给新手的建议:

  • 数组的 {0} 初始化,是非常常见的全 0 初始化。

题目整理:

static char str[10] = "china";

问:数组元素个数是多少?

答案:B

详细解析:

数组长度已经明确写在方括号里:

str[10]

所以数组元素个数是 10

字符串 "china" 只决定前几个元素的初值,不改变数组长度。

容易踩坑点:

  • 把字符串长度 5 或占用空间 6 当成数组元素个数。

给新手的建议:

  • 题目问“数组元素个数”,优先看方括号里的数字。

题目整理:

#include <stdio.h>
int z;
int p(int x)
{
static int y;
return x + y;
}
void main(void)
{
int a, b;
printf("%d\n", p(a));
}

问:如果不再定义新的变量,在 main 函数中可以使用的所有变量有哪些?

答案:D

详细解析:

main 中可以直接使用:

  • amain 的局部变量
  • bmain 的局部变量
  • z:全局变量

不能直接使用:

  • x:函数 p 的形参,只在 p 内有效
  • y:函数 p 的静态局部变量,只在 p 内有效

所以答案是:

a, b, z

容易踩坑点:

  • 以为 static 局部变量可以在别的函数里直接访问。

给新手的建议:

  • 变量能不能用,先看它定义在哪个作用域。

题目:字符串 \\name\\101ddress\b\xaf 的长度。

答案:15

详细解析:

这题主要考转义字符。按卷面给出的答案,字符串长度为 15

转义字符题要记住:

  • \\ 表示一个反斜杠字符
  • \b 表示一个退格字符
  • \x.. 表示十六进制转义字符
  • 八进制或十六进制转义序列整体通常按一个字符来数

补充提醒:

这道题的 OCR 识别不太清楚,反斜杠个数会直接影响长度。考试时要以原题字符串为准,做题方法是把每个转义序列圈起来,一个转义序列按一个字符数。

容易踩坑点:

  • \b 当成两个普通字符。
  • \x.. 拆开数。

给新手的建议:

  • 转义字符长度题,不要从左到右硬数,先把所有反斜杠开头的转义序列标出来。

题目整理:

int a = 25, b = 14, c = 19;
a++ <= 2 && b-- <= 2 && c++
? printf("***a=%d,b=%d,c=%d\n", a, b, c)
: printf("a=%d,b=%d,c=%d\n", a, b, c);

答案:a=26,b=14,c=19

详细解析:

先看第一个条件:

a++ <= 2

a++ 先使用旧值 25,再让 a 变成 26

25 <= 2

结果是假。

由于 && 有短路特性,后面的:

b-- <= 2
c++

都不会执行。

所以:

a = 26
b = 14
c = 19

条件为假,执行冒号后面的 printf

容易踩坑点:

  • 忘记 a++ 虽然比较用旧值,但变量本身已经加 1。
  • 忘记 && 前面为假时,后面不再计算。

给新手的建议:

  • 遇到 &&++ 混在一起,一定按短路规则一步一步算。

题目:

a = 13;
b = 6;
a && b

答案:1

详细解析:

136 都是非 0,在 C 语言里都表示真。

真 && 真 = 真

所以结果为 1

容易踩坑点:

  • 以为 && 会返回原来的数字。

给新手的建议:

  • 逻辑运算结果通常是 01

题目整理:

a = 12;
(0 < a) && (a < 2)

答案:0

详细解析:

0 < 12 -> 真
12 < 2 -> 假
真 && 假 -> 假

所以结果为 0

容易踩坑点:

  • 只看到 0 < a 成立,就忘记后面还要同时成立。

题目整理:

int x = 2, y = 3, z = 4;
x + y && x == y

答案:0

详细解析:

先按优先级理解:

(x + y) && (x == y)

计算:

x + y = 5 -> 真
x == y -> 2 == 3 -> 假
真 && 假 -> 假

所以结果是 0

容易踩坑点:

  • +==&& 的优先级不熟。

给新手的建议:

  • 表达式题不确定时,先手动加括号。

题目:结构体是不同数据类型的数据集合,作为数据类型,必须先定义结构体什么,再定义结构体变量?

答案:类型

详细解析:

结构体使用时通常先定义结构体类型:

struct Student
{
int id;
char name[20];
};

再定义结构体变量:

struct Student s1;

容易踩坑点:

  • 把结构体类型和结构体变量混在一起。

给新手的建议:

  • struct Student 是类型,s1 才是变量。

题目给出数组:

下标:0 1 2 3 4 5 6 7 8 9
元素:9 4 12 8 2 10 7 5 1 3

问:数值最小的元素下标是多少?

答案:8

详细解析:

最小值是 1,它位于:

a[8]

所以答案是 8

容易踩坑点:

  • 把第 9 个位置错写成下标 9

给新手的建议:

  • 数组题要把“第几个”和“下标是多少”分开,下标从 0 开始。

题目整理:

int f(int x, int y)
{
return (y - x) * x;
}
int a = 3, b = 4, c = 5, d;
d = f(f(3, 4), f(3, 5));
printf("%d\n", d);

答案:9

详细解析:

先算内层:

f(3, 4) = (4 - 3) * 3 = 3
f(3, 5) = (5 - 3) * 3 = 6

再算外层:

f(3, 6) = (6 - 3) * 3 = 9

所以输出 9

容易踩坑点:

  • 函数嵌套调用时,没有先算内层。

给新手的建议:

  • 嵌套函数调用按“从内到外”代值。

题目整理:

int a = 1, b = 2, c = 3;
if (a > c)
b = a;
a = c;
c = b;

问:执行后 a 的值是多少?

答案:3

详细解析:

先判断:

a > c -> 1 > 3 -> 假

所以:

b = a;

不会执行。

后面两句不属于 if,一定会执行:

a = c; // a = 3
c = b; // c = 2

所以 a = 3

容易踩坑点:

  • 没有大括号时,if 只控制紧跟着的一条语句。

给新手的建议:

  • 新手写 if 时建议总是加 {},减少误解。

题目整理:

float x;
x = 5.16894;
(int)(x * 1000 + 0.5) / (float)1000

答案:5.169

详细解析:

先算:

x * 1000 = 5168.94
x * 1000 + 0.5 = 5169.44

强制转换为 int 后,小数部分被截断:

(int)5169.44 = 5169

再除以 1000.0

5169 / 1000.0 = 5.169

这个写法相当于保留三位小数的四舍五入。

容易踩坑点:

  • 以为 (int) 是四舍五入,其实它是直接截断。
  • +0.5 才是实现四舍五入的关键。

给新手的建议:

  • 保留小数题要分清“截断”和“四舍五入”。

题目:C 语言本身不提供输入输出语句,输入和输出操作由函数实现。

答案:Y

解析:

C 语言的输入输出通常通过标准库函数实现,例如:

printf();
scanf();
getchar();
putchar();

题目:

char array[] = "hello";

判断:数组 array 所占空间为 5 个字节。

答案:N

解析:

"hello" 有 5 个可见字符,但还要加结尾的 '\0'

所以占 6 个字节。

题目:共用体变量所占的内存长度等于最长成员的长度。

答案:Y

解析:

按教材口径,共用体成员共用同一块内存,所以所占空间按最长成员计算。

补充提醒:

实际编译器还可能考虑内存对齐,但入门考试一般按最长成员长度判断。

题目整理:

a = 3;
b = 2;
c = 1;
(a > b) == c

判断:值为真,即为 1

答案:Y

解析:

a > b -> 3 > 2 -> 真 -> 1
c = 1
1 == 1 -> 真

所以结果为 1

题目:C 语言的 switch 语句中,case 后可以是常量、常量表达式,也可以是有确定值的变量。

答案:N

解析:

case 后面必须是整型常量表达式,不能是普通变量。

正确:

case 1:
case 2 + 3:

不正确:

case x:

题目整理:

int a[3][3] = {{3, 5}, {8, 9}, {12, 35}}, i, sum = 0;
for (i = 0; i < 3; i++)
sum += a[i][2 - i];

判断:sum = 21

答案:Y

解析:

数组补全后:

3 5 0
8 9 0
12 35 0

取的元素是:

a[0][2] = 0
a[1][1] = 9
a[2][0] = 12

所以:

sum = 0 + 9 + 12 = 21

题目整理:

int i = 20;
switch (i / 10)
{
case 2:
printf("A");
case 1:
printf("B");
}

判断:输出结果为 A

答案:N

解析:

i / 10 = 2,所以先进入 case 2,输出 A

case 2 后面没有 break,程序会继续执行 case 1,再输出 B

实际输出:

AB

题目:整数 -32100 可以赋值给 int 型和 long int 型变量。

答案:Y

解析:

按传统 16 位有符号 int 环境:

-32768 ~ 32767

-32100 在范围内,可以赋给 int,也可以赋给 long int

题目:

int a[10] = {1 * 10};

判断:可以使数组中全部元素的值为 1

答案:N

解析:

1 * 10 的结果是 10

所以数组实际初始化为:

10 0 0 0 0 0 0 0 0 0

不是全部元素都是 1

题目:宏定义中使用了变量 t,由于变量 t 没定义,所以此宏定义是错误的。

答案:N

解析:

宏定义本身只是文本替换规则,不会在定义时检查变量 t 是否存在。

但如果使用宏展开后,代码里没有提前定义 t,编译时会报错。

易错点:

  • 宏定义阶段和宏展开后编译阶段不是一回事。

功能:根据企业当月利润计算奖金。

规则整理:

  • 利润 <= 10 万:奖金为利润的 10%
  • 10 万 ~ 20 万:超过 10 万部分按 7.5%
  • 20 万 ~ 40 万:超过 20 万部分按 5%
  • 40 万 ~ 60 万:超过 40 万部分按 3%
  • 60 万 ~ 100 万:超过 60 万部分按 1.5%
  • > 100 万:超过 100 万部分按 1%

整理后的正确代码:

#include <stdio.h>
int main(void)
{
long int i;
double bonus1, bonus2, bonus4, bonus6, bonus10, bonus;
scanf("%ld", &i);
bonus1 = 100000 * 0.1;
bonus2 = bonus1 + 100000 * 0.075;
bonus4 = bonus2 + 200000 * 0.05;
bonus6 = bonus4 + 200000 * 0.03;
bonus10 = bonus6 + 400000 * 0.015;
if (i <= 100000)
bonus = i * 0.1;
else if (i <= 200000)
bonus = bonus1 + (i - 100000) * 0.075;
else if (i <= 400000)
bonus = bonus2 + (i - 200000) * 0.05;
else if (i <= 600000)
bonus = bonus4 + (i - 400000) * 0.03;
else if (i <= 1000000)
bonus = bonus6 + (i - 600000) * 0.015;
else
bonus = bonus10 + (i - 1000000) * 0.01;
printf("bonus=%f\n", bonus);
return 0;
}

原题中主要错误:

第一处:

scanf("%ld" &i);

应改为:

scanf("%ld", &i);

第二处:

if (i > 100000)

应改为:

if (i <= 100000)

第三处:

printf("bonus=%d", bonus)

应补分号,并且因为 bonusdouble,更稳的输出格式是:

printf("bonus=%f\n", bonus);

容易踩坑点:

  • scanf 的格式串和变量地址之间要有逗号。
  • 分段计算奖金时,区间边界很容易写反。
  • double 类型输出应用 %f

给新手的建议:

  • 分段计算题先画区间,再写 if...else if...else

功能:输入一个字符串,将其中的字符 'a' 替换成字符串 "shu" 后输出。

例如:

输入:123abcaHello
输出:123shubcshuHello

整理后的正确代码:

#include <stdio.h>
int main(void)
{
int i;
char line[81];
scanf("%80s", line);
for (i = 0; line[i] != '\0'; i++)
{
if (line[i] == 'a')
printf("shu");
else
printf("%c", line[i]);
}
printf("\n");
return 0;
}

原题中主要错误:

第一处:

scanf("%s", &line);

应改为:

scanf("%s", line);

字符数组名本身就是首地址,不需要再写 &

第二处:

for (i = 0; line[i] != '\n'; i++)

应改为:

for (i = 0; line[i] != '\0'; i++)

字符串结束标志是 '\0',不是换行符。

第三处:

if (line[i] = 'a')

应改为:

if (line[i] == 'a')

= 是赋值,== 才是判断相等。

第四处:

printf("%s", line[i]);

应改为:

printf("%c", line[i]);

line[i] 是单个字符,应该用 %c

容易踩坑点:

  • 字符数组输入时不要写 &line
  • 字符串结束符是 '\0'
  • 判断相等用 ==
  • 单个字符输出用 %c,字符串输出用 %s

给新手的建议:

  • 这道题很适合练习“逐个字符扫描字符串”的模板。

功能:对长度为 8 个字符的字符串,将 8 个字符按降序排列。

例如:

原字符串:CEAedcab
排序后:edcbaECA

参考答案:

#include <stdio.h>
void fun(char *s, int num)
{
int i, j;
char t;
for (i = 0; i < num; i++)
{
for (j = i + 1; j < num; j++)
{
if (s[i] < s[j])
{
t = s[i];
s[i] = s[j];
s[j] = t;
}
}
}
}
int main(void)
{
char s[10];
printf("输入 8 个字符的字符串:");
scanf("%9s", s);
fun(s, 8);
printf("\n%s\n", s);
return 0;
}

详细解题过程:

这题本质是字符排序,和整数排序思路一样。

外层循环固定当前位置:

for (i = 0; i < num; i++)

内层循环找后面更大的字符:

for (j = i + 1; j < num; j++)

如果后面的字符更大,就交换:

if (s[i] < s[j])

因为题目要求降序,所以大的字符要放前面。

容易踩坑点:

  • 降序条件应是 s[i] < s[j],不是 s[i] > s[j]
  • 字符比较用的是字符编码顺序,通常小写字母编码大于大写字母。

给新手的建议:

  • 排序题先明确“升序还是降序”,再决定交换条件。

功能:统计一个长度为 2 的字符串在另一个字符串中出现的次数。

例如:

主字符串:asdasasafgasdaszx67asdmklo
子字符串:as
输出:6

参考答案:

#include <stdio.h>
#include <string.h>
int fun(char *str, char *substr)
{
int i;
int n = 0;
int len = strlen(str);
for (i = 0; i <= len - 2; i++)
{
if (str[i] == substr[0] && str[i + 1] == substr[1])
n++;
}
return n;
}
int main(void)
{
char str[81], substr[3];
int n;
printf("输入主字符串:");
scanf("%80s", str);
printf("输入子字符串:");
scanf("%2s", substr);
n = fun(str, substr);
printf("n=%d\n", n);
return 0;
}

详细解题过程:

因为子字符串长度为 2,所以每次只需要比较两个字符:

str[i] == substr[0]
str[i + 1] == substr[1]

如果两个都相等,就说明从 str[i] 位置开始出现了一次子字符串:

n++;

循环条件写成:

i <= strlen(str) - 2

原因是最后一次比较要访问 str[i + 1],所以 i 不能超过倒数第二个字符的位置。

容易踩坑点:

  • 循环写成 i < strlen(str),导致访问 str[i + 1] 越界。
  • printf("n=%d\n" n); 少了逗号,应写 printf("n=%d\n", n);
  • substr[3] 是为了存两个字符加一个 '\0'

给新手的建议:

  • 子串匹配题先确定“每次比较几个字符”,再决定循环到哪里结束。

这套 exam-89 比较集中地考了这些基础:

  • 普通变量传参是值传递。
  • 字符串不能直接用 == 比较内容。
  • 字符数组长度和字符串长度要分清。
  • 二维数组第二维不能省略。
  • 函数不写返回类型时,老教材按 int 处理。
  • switch 没有 break 会继续往下执行。
  • 字符数组输入不需要写 &
  • 把选择题第 1、13、20、22、25 题放在一起看,它们都和函数有关。
  • 把选择题第 4、5、7、12、14、19、24 题放在一起看,它们都和字符串有关。
  • 把选择题第 8、10、11、15、16、18、21、23 题放在一起看,它们都和数组有关。
  • 改错题 2 很值得手敲一遍,因为它把 scanf'\0'==%c 这几个新手高频错误集中在一起了。
  • 程序设计 1 练排序,程序设计 2 练字符串扫描,这两类题后面还会反复出现。

第 89 套试卷的重点不是难算法,而是 C 语言基础细节是否稳定:

  • 函数调用时传的是值还是地址
  • 数组下标是否越界
  • 字符串是否带 '\0'
  • scanf 参数要不要加 &
  • === 是否分清
  • switch 是否需要 break

这些点看起来小,但考试和写程序时都特别容易扣分。把这套题吃透,对数组、字符串和函数这三块会很有帮助。