跳转到内容

exam-90 试卷整理与详解

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

/Users/wff/Downloads/90.pdf

这套卷子继续围绕 C 语言基础展开:数组、字符串、函数、指针、文件、宏、表达式、递归和程序改错。PDF 的 OCR 对代码符号识别有一些误差,我已经按原卷红色标注和 C 语言语法做了整理。

特别提醒:

  • 选择题第 21 题,原卷红字标的是 [整型常量]。更严谨的教材写法通常是 [常量表达式],所以我在答案速查里写成 A(原卷标 D),解析里会说明。
  • 程序设计第 2 题,原卷红字里循环写成 i <= n,但主函数按 i < n 输入数据,所以正确处理数组时应写 i < n,否则会越界。
  • 填空题第 1 题 OCR 漏掉了循环中调用 move(s, z) 的语句;根据答案 cdeab 可知程序功能是把字符串循环右移 3 次。
题号答案
1C
2C
3C
4A
5D
6C
7C
8A
9A
10D
11D
12D
13D
14D
15C
16B
17B
18B
19B
20A
21A(原卷标 D)
22B
23D
24D
25D
题号答案
1cdeab
22.5
34
4a[1]*(a + 1)
520
61
7"a"
88
92
10p = funp = fun;
题号答案
1N
2Y
3Y
4N
5N
6N
7N
8Y
9N
10Y

题目问:以下正确的说法是哪一个?

答案:C

正确说法:

用户可以重新定义标准库函数,若如此,该函数将失去原有含义。

详细解析:

标准库函数比如:

printf
scanf
strlen
strcpy

它们本来已经由系统库提供。如果用户自己又定义一个同名函数,名字会发生冲突,并且这个名字在当前程序里的含义会变成用户自己的定义。

补充提醒:

实际工程里不要这样做。重新定义标准库函数会让程序很混乱,甚至产生不可预期问题。

容易踩坑点:

  • 以为标准库函数名永远不能被用户写出来。
  • 以为使用标准库函数不需要包含头文件。

给新手的建议:

  • 用标准库函数时,正常包含对应头文件,比如字符串函数包含 <string.h>

题目整理:

int a[10] = {6, 7, 8, 9, 10};

答案:C

详细解析:

数组初始化从下标 0 开始,依次赋值:

a[0] = 6
a[1] = 7
a[2] = 8
a[3] = 9
a[4] = 10

后面的元素没有显式初值,会自动补 0

a[5] ~ a[9] = 0

容易踩坑点:

  • 以为第一个初值赋给 a[1]

给新手的建议:

  • 数组初始化和数组访问一样,都是从下标 0 开始。

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

答案:C

正确写法:

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

详细解析:

二维数组初始化时,第一维可以省略,第二维必须写出来。

这里第二维是 3,每一行 3 个元素:

1 2 3
4 5 6

所以编译器可以推断第一维是 2

容易踩坑点:

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

给新手的建议:

  • 二维数组初始化题,先看“列数有没有写出来”。

题目问:下列字符数组长度为 5 的是哪一个?

答案:A

正确写法:

char a[] = {'h', 'a', 'b', 'c', 'd'};

详细解析:

初始化列表里正好有 5 个字符元素,所以数组长度就是 5

注意:

如果写成:

char b[] = {'h', 'a', 'b', 'c', 'd', '\0'};

那么数组长度就是 6,只是它表示的字符串有效长度是 5

容易踩坑点:

  • 把“字符数组长度”和“字符串长度”混在一起。

给新手的建议:

  • 数组长度看元素个数。
  • 字符串长度看 '\0' 前面的有效字符个数。

题目整理:

char c[] = "abc";
int i = 0;
do
;
while (c[i++] != '\0');
printf("%d", i - 1);

答案:D

详细解析:

字符串 "abc" 实际存储为:

'a' 'b' 'c' '\0'

循环过程:

次数判断字符判断后 i
1c[0]1
2c[1]2
3c[2]3
4c[3]4

第 4 次遇到 '\0' 后退出,但 i++ 已经执行,所以 i = 4

输出:

i - 1 = 3

容易踩坑点:

  • 忘记退出循环那一次 i++ 也执行了。

给新手的建议:

  • 看到 i++ 在循环条件里,最好手动列每一步。

题目问:在 C 语言程序中,函数定义和函数调用能否嵌套?

答案:C

正确说法:

函数的定义不可以嵌套,但函数的调用可以嵌套。

详细解析:

函数定义不能写在另一个函数内部:

int main(void)
{
int fun(void) // 错误
{
return 1;
}
}

但函数调用可以嵌套:

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

容易踩坑点:

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

给新手的建议:

  • 函数可以在调用时一层套一层,但定义时不要放到另一个函数里面。

题目整理:

int w = 3;
void main(void)
{
int w = 10;
printf("%d\n", fun(5) * w);
}
int fun(int k)
{
if (k == 0)
return w;
return fun(k - 1) * k;
}

答案:C

详细解析:

先看 fun 函数里用到的 wfun 函数外面能看到的是全局变量:

int w = 3;

所以:

fun(0) = 3
fun(1) = fun(0) * 1 = 3
fun(2) = fun(1) * 2 = 6
fun(3) = 6 * 3 = 18
fun(4) = 18 * 4 = 72
fun(5) = 72 * 5 = 360

main 函数中有局部变量:

int w = 10;

所以输出:

fun(5) * w = 360 * 10 = 3600

容易踩坑点:

  • 混淆全局变量 w = 3main 里的局部变量 w = 10
  • 递归题没有先找出口 k == 0

给新手的建议:

  • 如果同名变量同时出现,先看“当前函数能访问哪个变量”。

题目整理:

exce((v1, v2), (v3, v4, v5), v6);

问:实参个数是多少?

答案:A

详细解析:

最外层参数有三个:

(v1, v2)
(v3, v4, v5)
v6

括号里的逗号属于逗号表达式,不是函数参数分隔符。

容易踩坑点:

  • 看到逗号就直接数,结果把括号内部的逗号也算进去了。

给新手的建议:

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

题目整理:

int a[10];

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

答案:A

正确写法:

a[10 / 2 - 5]

详细解析:

先算下标:

10 / 2 - 5 = 5 - 5 = 0

所以:

a[10 / 2 - 5]

等价于:

a[0]

这是合法下标。

其他选项的问题:

  • a[4.5] 下标不是整数。
  • a[10] 越界。
  • a(1) 不是数组元素引用写法。

容易踩坑点:

  • 数组长度是 10,合法下标是 0 ~ 9

给新手的建议:

  • 下标表达式可以计算,但计算结果必须是合法整数下标。

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

答案:D

错误写法:

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

详细解析:

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

应写成类似:

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

这样编译器才知道每行有几个元素。

容易踩坑点:

  • 以为写了初始化列表就能省略第二维。

给新手的建议:

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

题目问:以下定义语句中错误的是哪一个?

答案:D

错误写法:

int n = 5, a[n];

详细解析:

按传统 C 语言教材和这类考试口径,数组长度应是常量表达式。

n 是变量,所以这里判错。

补充提醒:

有些现代编译器支持变长数组,但做这种教材卷子时,要按题目口径来答。

容易踩坑点:

  • 把“编译器可能支持”和“考试标准答案”混在一起。

给新手的建议:

  • 做卷子时,数组长度优先按常量表达式来判断。

题目整理:

static char str[10] = "China";

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

答案:D

详细解析:

数组长度已经写明:

str[10]

所以数组元素个数是 10

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

容易踩坑点:

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

给新手的建议:

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

题目问:以下函数原型中正确的是哪一个?

答案:D

正确写法:

void fun(int a, int b);

详细解析:

C 语言函数形参之间用逗号分隔,每个形参都要写类型。

错误示例:

void fun(int a; int b) // 错,不能用分号分隔形参
void fun(int a, b) // 错,b 没有写类型

容易踩坑点:

  • 把形参列表里的逗号写成分号。
  • 以为 int a, b 在函数形参里也表示两个参数都是 int

给新手的建议:

  • 函数形参最稳写法:每个参数都写完整类型。

题目整理:

char s1[] = "abc", s2[20], *t = s2;
gets(t);

问:当字符串 s1 大于 s2 时,输出 s2,应该写哪一句?

答案:D

正确写法:

if (strcmp(s1, t) > 0)
puts(s2);

详细解析:

这里:

t = s2;

所以 t 指向的就是 s2

strcmp(s1, t) > 0 就表示:

s1 > s2

容易踩坑点:

  • s1 > s2 比较字符串。
  • 忘记 ts2 指向同一段数组。

给新手的建议:

  • 字符串大小比较用 strcmp

题目问:下列不能正确进行字符串赋值的是哪一个?

答案:C

错误写法:

char s[5] = {'a', 'b', 'c', 'd', 'e'};

详细解析:

如果题目说的是“字符串赋值”,字符数组末尾应有 '\0'

上面这个数组只有 5 个普通字符,没有空间保存字符串结束符,所以不能作为正常字符串使用。

其他写法:

char s[5] = "abc";
char s[6] = "abcde";
char s[] = "abcde";

都能形成合法字符串。

容易踩坑点:

  • 把普通字符数组和字符串混为一谈。

给新手的建议:

  • 字符串一定要有结尾标志 '\0'

题目问:在 C 语言函数中,关于形参的正确说法是哪一个?

答案:B

正确说法:

可以有形参,也可以没有形参。

详细解析:

有形参的函数:

int add(int a, int b)
{
return a + b;
}

无形参的函数:

void print_line(void)
{
printf("hello\n");
}

容易踩坑点:

  • 以为函数必须有参数。

给新手的建议:

  • 不需要参数时,建议写 void,比如 int main(void)

题目整理:

fff(float x)
{
return 5;
}

问:函数类型是什么?

答案:B

详细解析:

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

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

所以函数类型是 int

补充提醒:

现代 C 语言不推荐这种写法。实际写代码时应明确写:

int fff(float x)

容易踩坑点:

  • 以为函数类型和参数 x 的类型相同。

给新手的建议:

  • 函数返回类型看函数名前面的类型。

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

答案:B

正确写法:

double a[1][4];

详细解析:

二维数组定义格式:

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

其他选项的问题:

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

容易踩坑点:

  • 用圆括号写数组。

给新手的建议:

  • 数组下标一定用方括号 []

题目问:与实际参数为实型数组名相对应的形式参数不可以定义为什么?

答案:B

错误写法:

float a;

详细解析:

如果实参是实型数组名,传过去的是数组首地址。

一维数组形参可以写:

float a[]
float *a

二维数组或数组指针可能写:

float (*a)[3]

float a 只是一个普通浮点变量,不能接收数组地址。

容易踩坑点:

  • 把数组参数当成普通变量参数。

给新手的建议:

  • 数组名作实参时,形参要能接收地址。

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

答案:A

正确写法:

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

详细解析:

6 * 1 是一个表达式,结果为 6

所以数组初始化为:

6 0 0 0 0 0

其他选项的问题:

  • int a[6] = {}; 在传统教材语境里通常不作为正确写法。
  • int a[6] = {1...3}; 不是标准初始化写法。
  • int a[6] = (0,0,0); 数组初始化不能用圆括号。

容易踩坑点:

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

给新手的建议:

  • 初始化列表里写几个值,就初始化前几个元素;剩余元素补 0

题目问:一维数组的定义方式是什么?

更严谨答案:A

原卷标注:D

推荐记法:

类型说明符 数组名[常量表达式];

详细解析:

教材中一维数组定义通常写成:

int a[10];
int b[2 + 3];

方括号里应是常量表达式。

原卷把 [整型常量] 标为答案,这是一种更窄的说法,只覆盖了 10 这种写法,但不能很好覆盖 2 + 3 这种常量表达式。

容易踩坑点:

  • 把任意整型变量也当成合法数组长度。

给新手的建议:

  • 做题时如果老师按原卷答案讲,记住它想强调“数组长度不能是普通变量”。
  • 真正理解时,优先记“常量表达式”。

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

答案:B

错误写法:

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

详细解析:

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

如果定义后再给字符数组复制字符串,应写:

strcpy(s, "abcdefg");

容易踩坑点:

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

给新手的建议:

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

题目整理:

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

答案:D

详细解析:

= {0} 会让二维数组中每个元素都初始化为 0

可以理解为:

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

容易踩坑点:

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

给新手的建议:

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

题目整理:

char a[10];

问:以下语句中不能从键盘给 a 数组所有元素输入值的是哪一个?

答案:D

错误写法:

a = getchar();

详细解析:

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

而且 getchar() 只读取一个字符,不可能直接填充整个数组。

容易踩坑点:

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

给新手的建议:

  • 逐个输入字符用 a[i] = getchar()
  • 输入字符串可以用 scanf("%s", a)

题目整理:

strcat(strcpy(str1, str2), str3)

答案:D

详细解析:

先执行内层:

strcpy(str1, str2)

作用是把 str2 复制到 str1 中。

strcpy 的返回值是目标字符串 str1 的地址,所以外层等价于:

strcat(str1, str3)

作用是把 str3 连接到 str1 后面。

所以整体功能是:

先把 str2 复制到 str1 中,再把 str3 连接到 str1 后面。

容易踩坑点:

  • 不知道 strcpy 会返回目标字符串地址。
  • strcat 的两个参数顺序理解反。

给新手的建议:

  • 字符串函数嵌套时,先看内层,再看外层。

题目功能:输入 3,abcde,将字符串循环右移 3 次,输出结果。

答案:cdeab

整理后的关键代码:

#include <string.h>
void move(char *str, int n)
{
char temp;
int i;
temp = str[n - 1];
for (i = n - 1; i > 0; i--)
str[i] = str[i - 1];
str[0] = temp;
}
int main(void)
{
char s[50];
int n, i, z;
scanf("%d,%s", &n, s);
z = strlen(s);
for (i = 1; i <= n; i++)
move(s, z);
printf("%s\n", s);
return 0;
}

详细解析:

move(s, z) 的作用是把字符串整体右移一位:

abcde -> eabcd

输入 n = 3,所以右移 3 次:

abcde
eabcd
deabc
cdeab

所以输出:

cdeab

容易踩坑点:

  • OCR 漏掉了 move(s, z) 这句,直接看会觉得程序没有改变字符串。
  • 循环右移一位时,要先保存最后一个字符。

给新手的建议:

  • 字符串移动题先拿短字符串手动模拟。

题目整理:

x = 2.5;
a = 7;
y = 4.7;
x + a % 3 * (int)(x + y) % 2 / 4

答案:2.5

详细解析:

先算强制转换:

(int)(x + y) = (int)(2.5 + 4.7) = (int)7.2 = 7

再按 *%/ 同级从左到右:

a % 3 = 7 % 3 = 1
1 * 7 = 7
7 % 2 = 1
1 / 4 = 0

这里 1 / 4 是整数除法,结果为 0

所以:

x + 0 = 2.5

容易踩坑点:

  • 忘记整数除法 1 / 4 的结果是 0
  • 忘记 % 的操作数应为整型。

给新手的建议:

  • 混合表达式题先标出哪些部分是整数运算,哪些部分是浮点运算。

题目整理:

int a[5] = {2, 4, 6, 8, 10}, *p;
p = a;
p++;
printf("%d", *p);

答案:4

详细解析:

p = a 后,p 指向 a[0]

执行:

p++;

后,p 指向下一个元素 a[1]

所以:

*p = a[1] = 4

容易踩坑点:

  • p++ 不是让 *p 的值加 1,而是让指针指向下一个元素。

给新手的建议:

  • 指针和数组结合时,p++ 表示“往后移动一个元素位置”。

题目:按内存排列顺序,数组 char a[2] 中的所有元素是 a[0] 和什么?

答案:a[1]*(a + 1)

详细解析:

数组:

char a[2];

有两个元素:

a[0]
a[1]

其中:

a[1]

也可以写成:

*(a + 1)

容易踩坑点:

  • 忘记下标从 0 开始。
  • 不理解 a[1]*(a + 1) 的关系。

给新手的建议:

  • a[i] 可以理解成 *(a + i)

题目整理:

int x = 2, y = 2;
x *= y + 8;

答案:20

详细解析:

复合赋值:

x *= y + 8;

等价于:

x = x * (y + 8);

所以:

x = 2 * (2 + 8) = 20

容易踩坑点:

  • 错算成 x = x * y + 8

给新手的建议:

  • 复合赋值右边整体参与运算。

题目整理:

int x = 2;
z = x++ - 1;

答案:1

详细解析:

x++ 是后置自增,表达式先使用旧值 2,然后 x 才变成 3

所以:

z = 2 - 1 = 1

容易踩坑点:

  • x++ 当成先加再用。

给新手的建议:

  • x++ 是先用后加,++x 是先加后用。

题目整理:

FILE *fw;
fw = fopen("readme.txt", ____);

要求:向文本文件 readme.txt 的最后续写内容。

答案:"a"

完整写法:

fw = fopen("readme.txt", "a");

详细解析:

文件打开模式:

  • "r":只读
  • "w":写入,会清空原内容
  • "a":追加写入,从文件末尾继续写

容易踩坑点:

  • 想“续写”却用了 "w",导致原内容被清空。

给新手的建议:

  • append 追加,对应 "a"

题目整理:

(a = 5, b = 2, a > b ? a++ : b++, a + b)

答案:8

详细解析:

先执行逗号表达式前两项:

a = 5
b = 2

然后看三目运算:

a > b -> 5 > 2 -> 真

所以执行:

a++

a++ 执行后,a 变成 6

最后整个逗号表达式的值看最后一项:

a + b = 6 + 2 = 8

容易踩坑点:

  • 忘记逗号表达式的值看最后一个表达式。
  • 忘记 a++ 会让 a 最终加 1。

给新手的建议:

  • 逗号表达式和三目运算混在一起时,先拆成多行。

题目问:unsigned int 定义无符号基本整型变量,分配几个字节?

答案:2

详细解析:

按传统 Turbo C 教材环境,unsigned int2 个字节。

补充提醒:

现代很多编译器中,unsigned int 通常占 4 个字节。考试题如果没有特别说明,通常按教材环境答。

容易踩坑点:

  • 把现代电脑环境和教材环境混在一起。

给新手的建议:

  • 考试按题目环境;实际写程序用 sizeof(unsigned int) 查看。

题目问:将函数 fun 的入口地址赋给指针变量 p 的语句是什么?

答案:p = fun

完整语句:

p = fun;

详细解析:

函数名 fun 可以表示函数入口地址。

如果已经定义了函数指针 p,那么:

p = fun;

就是把函数 fun 的入口地址赋给 p

容易踩坑点:

  • 写成 p = fun(),这表示调用函数,而不是取函数地址。

给新手的建议:

  • 函数名不加括号表示函数地址;加括号表示调用函数。

题目:通过 return 语句,函数可以带回一个或一个以上的返回值。

答案:N

解析:

return 语句一次只能直接返回一个值。

如果想让函数“带回多个结果”,常见做法是传地址或使用结构体。

题目:

#define S(a, b) a * b
area = S(3, 2);

判断:area 的值为 6

答案:Y

解析:

宏展开后:

area = 3 * 2;

结果是 6

补充提醒:

这个宏在更复杂表达式中可能有优先级问题,最好写成:

#define S(a, b) ((a) * (b))

题目整理:

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

判断:结果为真,即 1

答案:Y

解析:

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

所以结果为 1

题目:

float a;
scanf("%7.2f", &a);

判断:这是合法语句。

答案:N

解析:

scanf 的格式控制中可以写宽度,比如:

scanf("%7f", &a);

但不能像 printf 一样写精度 .2

容易踩坑点:

  • printf("%.2f") 的格式照搬到 scanf

题目:

7 && 3 + 12

判断:值是 13

答案:N

解析:

先算加法:

3 + 12 = 15

再算逻辑与:

7 && 15 -> 真 && 真 -> 1

所以结果是 1,不是 13

题目:进行宏定义时,宏名必须使用大写字母表示。

答案:N

解析:

宏名常用大写只是编程习惯,不是语法强制要求。

题目:

file *fp;
fp = fopen("a.txt", "r");

判断:在 Turbo C 中是合法的。

答案:N

解析:

文件指针类型应写成大写:

FILE *fp;

file 不是标准写法。

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

答案:Y

解析:

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

-32768 ~ 32767

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

题目:

#define S(a, b) t = a; a = b; b = t

判断:由于变量 t 没定义,所以此宏定义是错误的。

答案:N

解析:

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

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

题目:C 语言中求余运算符 % 要求两个操作数都必须是整型或字符型数据,不可以为实型数据。

答案:Y

解析:

% 只能用于整数类型。

正确:

7 % 3

错误:

7.5 % 2

容易踩坑点:

  • 以为 % 可以直接对小数求余。

功能:根据下面的级数求 π 的近似值,并作为函数值返回。

题目给出的公式可以理解为:

π / 2 = 1 + 1/3 + 1/3 * 2/5 + 1/3 * 2/5 * 3/7 + ...

整理后的正确代码:

#include <stdio.h>
double fun(double eps)
{
double s, t;
int n = 1;
s = 0.0;
t = 1.0;
while (t > eps)
{
s += t;
t = t * n / (2.0 * n + 1.0);
n++;
}
return 2 * s;
}
int main(void)
{
double x;
scanf("%lf", &x);
printf("\neps=%lf,Pi=%lf\n\n", x, fun(x));
return 0;
}

三处错误说明:

第一处:

while (t <= eps)

应改为:

while (t > eps)

当当前项仍然大于精度 eps 时,才继续累加。

第二处:

t = n / (2 * n + 1) * t;

应改为:

t = t * n / (2.0 * n + 1.0);

原因是要避免整数除法。如果写 n / (2 * n + 1),当 n = 1 时就是 1 / 3,整数除法结果为 0

第三处:

return s;

应改为:

return 2 * s;

因为公式求的是 π / 2,所以最终要乘以 2

容易踩坑点:

  • 级数题条件写反。
  • 忘记整数除法会截断小数。
  • 忘记公式左边是 π / 2,不是 π

给新手的建议:

  • 级数题先看“每一项怎么从上一项变来”,再写循环。

功能:按递归公式求函数值。

题目给出的例子:

n = 5 时,函数值为 240
n = 3 时,函数值为 60

整理后的正确代码:

#include <stdio.h>
int fun(int n)
{
int c;
if (n == 1)
c = 15;
else
c = fun(n - 1) * 2;
return c;
}
int main(void)
{
int n;
printf("Enter n:");
scanf("%d", &n);
printf("The result :%d\n\n", fun(n));
return 0;
}

错误说明:

第一处:

fun(double n)

应改为:

int fun(int n)

题目中的 n 是递归层数,应使用整数。

第二处:

if (n = 1)

应改为:

if (n == 1)

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

补充修正:

原题里 scanf("%d" &n); 也缺少逗号,应写:

scanf("%d", &n);

容易踩坑点:

  • 递归出口条件写成赋值。
  • 忘记 scanf 的格式串和变量地址之间有逗号。

给新手的建议:

  • 递归题先找出口,这题出口就是 n == 1

功能:将从键盘输入的每个单词的第一个字母转换为大写字母。输入时各单词用空格隔开,用 . 结束输入。

参考答案:

#include <stdio.h>
int fun(char *c, int status)
{
if (*c == ' ')
return 1;
if (status && *c >= 'a' && *c <= 'z')
*c += 'A' - 'a';
return 0;
}
int main(void)
{
int flag = 1;
char ch;
printf("请输入一字符串,用点号结束输入!\n");
do
{
ch = getchar();
flag = fun(&ch, flag);
putchar(ch);
} while (ch != '.');
printf("\n");
return 0;
}

详细解题过程:

flag 表示当前字符是否是一个单词的开头:

  • flag = 1:当前字符是单词开头
  • flag = 0:当前字符不是单词开头

如果读到空格:

if (*c == ' ')
return 1;

说明下一个非空格字符可能是新单词开头。

如果当前是单词开头,并且是小写字母:

if (status && *c >= 'a' && *c <= 'z')
*c += 'A' - 'a';

就把它转成大写。

容易踩坑点:

  • 空格字符应写成 ' ',不是空字符串。
  • 判断小写字母范围要写 *c >= 'a' && *c <= 'z'
  • flag = fun(&ch, flag); 语句末尾是分号,不是冒号。
  • do...while 结尾要有分号。

给新手的建议:

  • 这种题要先弄清楚状态变量 flag 的含义,它不是随便起的变量。

功能:给定 n 个数据,求最大值第一次出现的位置。

参考答案:

#include <stdio.h>
int station(int s[], int n)
{
int i, k;
k = 0;
for (i = 1; i < n; i++)
{
if (s[i] > s[k])
k = i;
}
return k + 1;
}
int main(void)
{
int a[100], n, i, t;
scanf("%d", &n);
for (i = 0; i < n; i++)
scanf("%d", &a[i]);
t = station(a, n);
printf("the max value position is:%d\n", t);
return 0;
}

详细解题过程:

先假设第一个元素最大:

k = 0;

从第二个元素开始比较:

for (i = 1; i < n; i++)

如果发现更大的值,就记录它的下标:

if (s[i] > s[k])
k = i;

最后返回:

return k + 1;

因为题目要的是“位置”,通常从 1 开始数;而数组下标从 0 开始,所以要加 1

为什么不用 >=

题目说如果最大值出现多次,求第一次出现的位置。只有遇到“严格更大”的值才更新 k,所以应该用:

s[i] > s[k]

不能用:

s[i] >= s[k]

容易踩坑点:

  • 原卷红字写了 i <= n,但数组合法下标是 0 ~ n - 1,所以这里应写 i < n
  • 题目要求第一次出现的位置,所以比较条件要用 >,不要用 >=
  • printf 的格式串和变量之间要有逗号。

给新手的建议:

  • “找最大值位置”题的模板是:先假设第 0 个最大,再从第 1 个开始比较。

这套 exam-90 很适合检查下面这些基础:

  • 数组初始化从 a[0] 开始,不是从 a[1] 开始。
  • 字符数组长度和字符串长度要分清。
  • 函数定义不能嵌套,但函数调用可以嵌套。
  • 普通变量、数组名、函数名作为参数时含义不同。
  • strcpystrcatstrcmp 的参数顺序要熟。
  • scanfprintf 的格式控制不能混用。
  • 递归题先找出口,再看递推关系。
  • 把选择题第 2、4、9、10、11、12、18、20、21、23、24 题放在一起复习,它们都和数组有关。
  • 把选择题第 14、15、22、25 题放在一起复习,它们都和字符串有关。
  • 把选择题第 6、7、8、13、16、17、19 题放在一起复习,它们都和函数有关。
  • 填空题第 1 题建议手动画字符串右移过程。
  • 程序设计第 2 题建议手写一遍,因为“第一次出现的位置”这个细节很容易写错。

第 90 套卷子的重点依旧是 C 语言基础细节:

  • 下标从 0 开始
  • 字符串以 '\0' 结束
  • 数组名常常代表首地址
  • 函数名可以代表函数入口地址
  • === 不能混
  • 整数除法会截断小数

这些点都不花哨,但很容易在考试和写代码时反复出错。你复习这套时,重点不要只背答案,要能说清楚每一题为什么这么选。