跳转到内容

exam-82 试卷整理与详解

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

/Users/wff/Downloads/82.pdf

这套题主要复习 C 语言里的函数调用、数组初始化、字符串处理、二维数组、指针、文件读写、宏和结构体基础,以及两类常见程序设计题:判断素数、判断整除。

PDF 里的 OCR 对代码符号有一些误差,例如 '\0'\x10、逗号、分号、花括号、==% 等位置容易识别错。我整理时已经按原卷红色标注和 C 语言语法做了校对。

特别提醒:

  • 选择题第 21 题问的是“字符数组初始化不正确”,不是“字符串赋值不正确”。所以 char s[5] = {'a','b','c','d','e'}; 作为字符数组是可以的,只是不能当普通字符串使用。
  • 选择题第 23 题按传统教材和 Turbo C 考试口径判断,int n = 5, a[n]; 是错误的。现代部分编译器可能支持变长数组,但做这类基础卷子要按教材标准答。
  • 选择题第 25 题要分清“形参”和“实参”:函数调用可以作为实参,但不能作为函数定义中的形参。
题号答案
1C
2A
3B
4D
5B
6C
7A
8A
9A
10A
11A
12D
13C
14A
15A
16C
17C
18D
19B
20C
21C
22C
23A
24A
25A
题号答案
10
2"filea.dat", "r"
312 34
4x >= 11 <= x
52
6*pmax = *pxmax = x 等等价写法
7p = funp = fun;
858
96
1015
题号答案
1Y
2Y
3N
4Y
5N
6N
7Y
8N
9Y
10N

题目问:在 C 语言中,调用函数除函数名外,还必须有什么?

答案:C

正确答案:

()

详细解析:

函数调用的基本格式是:

函数名(实参列表)

即使函数没有参数,也必须写一对小括号。

例如:

printf("hello\n");
getchar();
fun();

fun 只是函数名,fun() 才表示调用函数。

容易踩坑点:

  • 以为无参函数调用时可以省略 ()
  • 把函数声明和函数调用混在一起。

给新手的建议:

  • 看到函数名后面有 (),才表示“调用它”。

题目整理:

int k[30] = {12, 324, 45, 6, 768, 98, 21, 34, 453, 456};
int count = 0, i = 0;
while (k[i])
{
if (k[i] % 2 == 0 || k[i] % 5 == 0)
count++;
i++;
}
printf("%d,%d\n", count, i);

答案:A

详细解析:

数组只显式初始化了前 10 个数,后面的元素会自动补 0

所以:

k[10] = 0

循环条件是:

while (k[i])

i = 10 时遇到 k[10] = 0,循环结束。因此一共检查前 10 个数,最后 i = 10

再数满足条件的数:

元素是否能被 2 或 5 整除
12
324
45
6
768
98
21
34
453
456

满足条件的共有 8 个。

输出:

8,10

容易踩坑点:

  • 忘记数组没写到的元素会自动补 0
  • || 误认为两个条件都要满足。

给新手的建议:

  • while(k[i]) 这种写法本质是在判断 k[i] != 0

题目整理:

int a = 1, b = 2, c = 3, d = 4, e = 5;
printf("%d\n", func((a + b, b + c, c + a), (d + e)));
int func(int x, int y)
{
return x + y;
}

答案:B

详细解析:

第一个实参是逗号表达式:

(a + b, b + c, c + a)

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

c + a = 3 + 1 = 4

第二个实参:

d + e = 4 + 5 = 9

所以函数调用等价于:

func(4, 9)

返回:

4 + 9 = 13

容易踩坑点:

  • 把逗号表达式里的三个表达式都加起来。
  • 数实参时把括号里面的逗号也当作参数分隔符。

给新手的建议:

  • 逗号表达式记一句:整体值看最后一个表达式。

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

答案:D

正确写法:

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

详细解析:

初始化列表里一共有 5 个字符:

'h' 'a' 'b' 'c' 'd'

所以数组长度为 5

其他选项:

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

数组长度已经写明是 6

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

初始化列表有 6 个元素,所以长度是 6

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

数组长度已经写明是 10

容易踩坑点:

  • 把字符串长度和字符数组长度混在一起。
  • 看到 '\0' 就只算前面的有效字符。

给新手的建议:

  • 题目问数组长度,看数组实际有多少个元素;题目问字符串长度,才看 '\0' 前面有几个字符。

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

答案:B

错误写法:

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

详细解析:

y[5] 表示数组最多有 5 个元素:

y[0] ~ y[4]

但初始化列表里给了 6 个数:

0, 1, 3, 5, 7, 9

初值个数超过数组长度,所以错误。

补充说明:

原卷选项 A 的 OCR 容易把转义字符识别错。按原卷红字答案,本题重点考的是 B 中数组长度和初值个数不匹配。

容易踩坑点:

  • 数组长度写了 5,却放了 6 个初值。

给新手的建议:

  • 数组初始化时,先数方括号里的空间,再数花括号里的初值。

题目整理:

int func(int a, int b)
{
int c;
c = a + b;
return c;
}
int x = 6, y = 7, z = 8, r;
r = func((x--, y++, x + y), z--);
printf("%d\n", r);

答案:C

详细解析:

先看第一个实参:

(x--, y++, x + y)

这是逗号表达式,依次执行:

x--:先使用 x 的旧值,然后 x 变成 5
y++:先使用 y 的旧值,然后 y 变成 8
x + y:此时 x = 5,y = 8,所以结果是 13

第二个实参:

z--

作为实参时使用旧值 8,然后 z 变成 7

所以函数调用等价于:

func(13, 8)

返回:

13 + 8 = 21

容易踩坑点:

  • x-- 是先用后减,y++ 是先用后加。
  • 逗号表达式的值看最后一个表达式。

给新手的建议:

  • 自增自减和逗号表达式混在一起时,一定要按顺序写变量变化过程。

题目整理:

char a[10];

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

答案:A

错误写法:

a = getchar();

详细解析:

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

而且:

getchar()

一次只读取一个字符,不可能直接给整个数组输入所有元素。

可以逐个输入:

for (i = 0; i < 10; i++)
a[i] = getchar();

也可以按字符串输入:

scanf("%s", a);

容易踩坑点:

  • 把字符数组当成一个普通字符变量。
  • 忘记数组名不能整体赋值。

给新手的建议:

  • 给数组元素赋值,要么逐个 a[i] = ...,要么使用字符串输入函数。

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

答案:A

正确写法:

#define SIZE 10
int a[SIZE];

详细解析:

SIZE 是宏常量,预处理后相当于:

int a[10];

按传统教材口径,数组长度应该是常量表达式。

其他选项的问题:

  • int n = 10, a[n];n 是变量,按传统教材口径不正确。
  • scanf 输入后再定义 int a[n];,同样是变量长度数组,传统教材不认可。
  • int a(10); 不是 C 语言数组定义写法。

容易踩坑点:

  • 把变量 n 当作数组长度常量。
  • 用圆括号定义数组。

给新手的建议:

  • 数组定义用方括号:int a[10];

题目问:以下能正确定义数组并正确赋初值的语句是哪一个?

答案:A

正确写法:

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

详细解析:

d[3][2] 表示 3 行 2 列。给出的初值可以放成:

1 2
3 4
0 0

没有显式给出的元素自动补 0

其他选项的问题:

  • int a[1][2] = {{1}, {3}}; 只有 1 行,却给了 2 行初值。
  • int c[2][] = ... 第二维省略,错误。
  • int N = 5, b[N][N]; 按传统教材口径,数组长度不能用普通变量。

容易踩坑点:

  • 二维数组可以省略第一维,但不能省略第二维。
  • 初值少可以补 0,行数超了不行。

给新手的建议:

  • 二维数组题先检查“第二维有没有写”。

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

答案:A

错误写法:

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

详细解析:

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

正确示例:

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

这里第一维可以由初值推断出来,但第二维必须明确,因为编译器需要知道每一行有几个元素。

容易踩坑点:

  • 以为有初始化列表就可以省略任意一维。

给新手的建议:

  • 二维数组口诀:第一维可省,第二维必须写。

题目整理:

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

答案:A

详细解析:

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

可以理解为:

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

容易踩坑点:

  • 以为只有 a[0][0] 得到初值 0

给新手的建议:

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

题目整理:

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

问:将 a1 串连接到 a2 串后面的语句是哪一个?

答案:D

正确写法:

strcat(a2, a1);

详细解析:

strcat 的格式是:

strcat(目标字符串, 要追加的字符串);

所以 strcat(a2, a1) 表示把 a1 接到 a2 后面。

执行后:

a2 = "1234abc"

容易踩坑点:

  • strcat 两个参数顺序写反。
  • 忘记目标数组必须有足够空间。

给新手的建议:

  • strcat(a2, a1) 可以读成“把 a1 接到 a2 后面”。

题目问:数组名作为实参数传递给函数时,数组名被处理为什么?

答案:C

正确理解:

数组名被处理为该数组的首地址。

详细解析:

例如:

int a[10];
fun(a);

这里传给函数的不是整个数组的复制品,而是数组首元素的地址,也可以理解为:

&a[0]

容易踩坑点:

  • 以为数组传参会把所有元素复制一份。

给新手的建议:

  • 数组名作实参,传的是地址。

题目整理:

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

问:当字符串 s1 大于字符串 s2 时,输出 s2,正确语句是哪一个?

答案:A

正确写法:

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

详细解析:

这里:

t = s2;

所以 t 指向的就是 s2

比较字符串大小要用 strcmp

strcmp(s1, t) > 0 表示 s1 大于 t 所指字符串

因为 t 指向 s2,所以这就等价于判断 s1 > s2

容易踩坑点:

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

给新手的建议:

  • 字符串大小比较用 strcmp,不要直接用 >

题目问:对于 void 类型函数,调用时不可作为哪一种?

答案:A

正确理解:

void 函数没有返回值,所以不能作为表达式使用。

例如:

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

可以这样调用:

show();

不能这样写:

int x = show(); // 错误

容易踩坑点:

  • 以为所有函数调用都有结果值。

给新手的建议:

  • void 函数是为了“做动作”,不是为了“拿结果”。

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

答案:C

错误写法:

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

详细解析:

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

但初始化时给了 3 行:

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

行数超出,所以错误。

其他选项:

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

第一维可以省略,第二维写了 3,合法。

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

合法,缺少的元素补 0

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

合法,全部初始化为 0

容易踩坑点:

  • 少给初值可以补 0,多给行数不行。

给新手的建议:

  • 二维数组初始化时,先看行数有没有超过定义。

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

答案:C

错误写法:

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

详细解析:

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

定义时可以初始化:

char s[10] = "abcdefg";

定义后再复制字符串,应写:

strcpy(s, "abcdefg");

容易踩坑点:

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

给新手的建议:

  • 字符数组定义时可以用 = 初始化,定义之后复制字符串用 strcpy

题目整理:

long fib(int n)
{
if (n > 2)
return fib(n - 1) + fib(n - 2);
else
return 2;
}
printf("%d\n", fib(3));

答案:D

详细解析:

递归出口:

n <= 2 时,fib(n) = 2

所以:

fib(3) = fib(2) + fib(1)
= 2 + 2
= 4

容易踩坑点:

  • 看到 fib 就套普通斐波那契数列,忘记这题的前两项都是 2

给新手的建议:

  • 递归题先看出口值。

题目整理:

int n[2] = {0}, i, j, k = 1;
for (i = 0; i <= k; i++)
for (j = 0; j <= k; j++)
n[j] = n[i] + 1;
printf("%d\n", n[k]);

答案:B

详细解析:

初始:

n[0] = 0
n[1] = 0
k = 1

外层 i01,内层 j 也取 01

逐步模拟:

ij语句执行后 n[0]执行后 n[1]
00n[0] = n[0] + 110
01n[1] = n[0] + 112
10n[0] = n[1] + 132
11n[1] = n[1] + 133

最后输出:

n[k] = n[1] = 3

容易踩坑点:

  • 没有注意 n[i] 的值会在前面循环中变化。
  • 内层循环没有花括号时,只控制下一条语句。

给新手的建议:

  • 数组循环赋值题,用表格模拟最稳。

题目问:用户定义的函数不可以调用的函数是哪一个?

答案:C

按本卷考试口径:

用户定义函数不可以调用 main 函数。

详细解析:

main 函数是程序入口。通常程序启动时由系统调用 main,再由 main 去调用其他函数。

本类教材题一般要求理解为:

main 可以调用其他函数,其他用户自定义函数不应该调用 main。

容易踩坑点:

  • main 当成普通函数随便调用。

给新手的建议:

  • 初学阶段把 main 记成“程序总入口”,不要在其他函数里调用它。

题目问:下列对字符数组的初始化不正确的是哪一个?

答案:C

错误写法:

char s[5] = "abcde";

详细解析:

字符串 "abcde" 实际需要 6 个位置:

'a' 'b' 'c' 'd' 'e' '\0'

但是 s[5] 只有 5 个位置,放不下结尾的 '\0',所以作为字符串初始化是不正确的。

重点区分:

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

这个选项作为“字符数组初始化”是可以的,因为它只是放 5 个普通字符,不要求自动追加 '\0'

但它不能当作正常字符串使用,因为它没有字符串结束符。

容易踩坑点:

  • 把字符数组初始化和字符串初始化混在一起。

给新手的建议:

  • 双引号 "abcde" 是字符串,会自动带 '\0'
  • 单个字符列表 {'a','b','c','d','e'} 只是字符数组,不会自动多塞一个 '\0'

题目整理:

char array[] = "China";

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

答案:C

详细解析:

字符串 "China" 的有效字符有 5 个:

C h i n a

字符串结尾还会自动加一个 '\0'

C h i n a \0

所以数组占:

6 个字节

容易踩坑点:

  • 只数有效字符,忘记字符串结束符。

给新手的建议:

  • 字符串数组占用空间通常是“有效字符个数 + 1”。

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

答案:A

错误写法:

int n = 5, a[n];

详细解析:

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

n 是变量,不是常量表达式,所以这题判错。

其他选项:

char *a[3];
char s[10] = "test";
int a[] = {1, 2};

这些都是合法写法。

补充提醒:

现代部分编译器支持变长数组,但这类基础考试通常不按这个标准判。

容易踩坑点:

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

给新手的建议:

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

题目整理:

static char str[10] = "China";

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

答案:A

详细解析:

数组长度已经写明:

str[10]

所以数组元素个数就是 10

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

容易踩坑点:

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

给新手的建议:

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

题目问:若已定义的函数有返回值,以下关于该函数调用的叙述中错误的是哪一个?

答案:A

错误说法:

函数调用可以作为一个函数的形参。

详细解析:

函数调用有返回值时,可以出现在表达式中:

x = fun() + 3;

也可以作为另一个函数的实参:

printf("%d\n", fun());

也可以作为独立语句存在:

fun();

但“形参”是函数定义时写在参数列表里的变量,例如:

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

这里 ab 是形参。函数调用不能作为形参写在函数定义里。

容易踩坑点:

  • 混淆形参和实参。

给新手的建议:

  • 形参:函数定义时的参数。
  • 实参:调用函数时传进去的具体值或表达式。

题目整理:

int a = 10, b = 15, c = 1, d = 2, e = 0;
(c == b) > e

答案:0

详细解析:

先算括号:

c == b
1 == 15

结果为假,也就是 0

再算:

0 > e
0 > 0

结果仍然为假,所以表达式值为:

0

容易踩坑点:

  • 忘记关系表达式的结果是 10

给新手的建议:

  • 表达式题先按括号分块计算。

题目功能:从名为 filea.dat 的文本文件中逐个读入字符并显示在屏幕上。

题目整理:

FILE *fp;
char ch;
fp = fopen(____);
ch = fgetc(fp);
while (!feof(fp))
{
putchar(ch);
ch = fgetc(fp);
}
putchar('\n');
fclose(fp);

答案:"filea.dat", "r"

完整写法:

fp = fopen("filea.dat", "r");

详细解析:

fopen 的两个参数是:

文件名
打开方式

本题要从文本文件中读取字符,所以打开方式是只读:

"r"

容易踩坑点:

  • 文件名和打开方式都要写成字符串。
  • 读取文件用 "r",写入文件用 "w",追加写入用 "a"

给新手的建议:

  • r 可以记成 read,表示读。

题目整理:

int a = 1, b;
scanf("%2d%2d", &a, &b);
printf("%d %d\n", a, b);

输入:

1234567

答案:12 34

详细解析:

%2d 表示最多读取 2 位数字。

第一次 %2d 读取:

12

赋给 a

第二次 %2d 继续读取后面的 2 位:

34

赋给 b

所以输出:

12 34

容易踩坑点:

  • 以为 %2d 会读取整个整数后保留两位。
  • 忘记第二个 %2d 会从剩余输入继续读。

给新手的建议:

  • scanf 的宽度是“最多读几个字符”,不是输出保留位数。

题目要求:把下面三目运算表达式改写成等价的 if 语句。

y = (x >= 10) ? 3 * x - 11 : (x < 1) ? x : 2 * x - 1;

题目给出的结构:

if (x < 10)
if (____)
y = 2 * x - 1;
else
y = x;
else
y = 3 * x - 11;

答案:x >= 11 <= x

详细解析:

原表达式可以分成三段:

x >= 10 -> y = 3 * x - 11
x < 1 -> y = x
1 <= x < 10 -> y = 2 * x - 1

题目外层已经写了:

if (x < 10)

x < 10 的前提下,要进入 y = 2 * x - 1,还需要:

x >= 1

容易踩坑点:

  • 三目运算符嵌套时看不清分支。

给新手的建议:

  • 遇到嵌套三目,先翻译成分段条件。

题目整理:

int a = 13, b = 6;
a / b

答案:2

详细解析:

ab 都是整数,整数除法会舍去小数部分。

13 / 6 = 2

不是 2.166...

容易踩坑点:

  • 忘记整数除法会截断小数。

给新手的建议:

  • 只要 / 两边都是整数,结果就是整数除法。

题目功能:利用指针指向三个整型变量,并通过指针运算找出三个数中的最大值。

题目整理:

int x, y, z, max, *px, *py, *pz, *pmax;
scanf("%d%d%d", &x, &y, &z);
px = &x;
py = &y;
pz = &z;
pmax = &max;
____;
if (*pmax < *py)
*pmax = *py;
if (*pmax < *pz)
*pmax = *pz;
printf("max=%d\n", max);

答案示例:

*pmax = *px;

也可以写:

max = x;

详细解析:

程序后面要拿 max 先和 yz 比较,所以空里应该先把 x 作为当前最大值。

由于:

pmax = &max;
px = &x;

所以:

*pmax = *px;

等价于:

max = x;

容易踩坑点:

  • pmax 是地址,*pmax 才是 max 这个变量本身。
  • 直接比较前必须先给 max 一个初值。

给新手的建议:

  • 指针题可以先把 *pmax 翻译成“max 这个盒子里的值”。

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

答案:p = fun;

详细解析:

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

如果 p 是函数指针,那么:

p = fun;

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

不要写成:

p = fun();

因为 fun() 表示调用函数,拿到的是函数返回值,不是函数地址。

容易踩坑点:

  • 函数名加括号就是调用。

给新手的建议:

  • 函数名不加 (),常常表示函数地址;加 (),就是调用函数。

题目整理:

int a[4][4] = {
{1, 2, -3, -4},
{0, -12, -13, 14},
{-21, 23, 0, -24},
{-31, 32, -33, 0}
};
int i, j, s = 0;
for (i = 0; i < 4; i++)
{
for (j = 0; j < 4; j++)
{
if (a[i][j] < 0)
continue;
if (a[i][j] == 0)
break;
s += a[i][j];
}
}
printf("%d\n", s);

答案:58

详细解析:

遇到负数执行 continue,跳过当前数字;遇到 0 执行 break,结束当前行的内层循环;遇到正数才累加。

逐行看:

数据累加情况
第0行1, 2, -3, -41 + 2
第1行0, -12, -13, 14第一个是 0,本行结束
第2行-21, 23, 0, -24跳过 -21,加 23,遇 0 结束
第3行-31, 32, -33, 0跳过 -31,加 32,跳过 -33,遇 0 结束

所以:

1 + 2 + 23 + 32 = 58

容易踩坑点:

  • continuebreak 作用不同。
  • 内层 break 只结束内层循环。

给新手的建议:

  • 这种题最好画二维表格,逐行模拟。

题目整理:

int a[2][3];

问:数组 a 中有多少个元素?

答案:6

详细解析:

二维数组元素个数等于:

行数 * 列数

所以:

2 * 3 = 6

容易踩坑点:

  • a[2][3] 误解成最大下标是 23,从而算成更多元素。

给新手的建议:

  • int a[2][3] 是 2 行 3 列。

题目整理:

int fun(int x)
{
static int t = 0;
return t += x;
}
int s, i;
for (i = 1; i <= 5; i++)
s = fun(i);
printf("%d\n", s);

答案:15

详细解析:

static int t = 0; 表示 t 是静态局部变量。它不会在每次函数调用时重新变成 0,而是会保留上一次调用后的值。

逐次调用:

调用t 原值t += x 后返回值
fun(1)011
fun(2)133
fun(3)366
fun(4)61010
fun(5)101515

最后一次返回 15,所以 s = 15

容易踩坑点:

  • 以为局部变量每次调用都会重新初始化,忽略了 static

给新手的建议:

  • static 局部变量会“记住上一次的值”。

题目:

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

判断:值为真,即为 1

答案:Y

解析:

先算:

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

再算:

1 == c -> 1 == 1 -> 真

所以结果为 1

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

答案:Y

解析:

共用体所有成员共用同一段内存空间,所以它至少要能放下最长的成员。基础教材里通常表述为:共用体变量所占内存长度等于最长成员的长度。

补充提醒:

实际编译器可能因为内存对齐,让大小略有差异,但这类考试题按教材表述判断。

题目整理:

printf("%f%%", 1.0 / 3);

判断:输出为 0.333333

答案:N

解析:

%f 默认输出 6 位小数:

0.333333

但格式串里还有:

%%

%% 会输出一个普通的百分号 %

所以实际输出应包含百分号:

0.333333%

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

答案:Y

解析:

按传统 16 位有符号 int 环境,取值范围通常是:

-32768 ~ 32767

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

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

答案:N

解析:

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

如果想让函数带回多个结果,通常使用指针参数或结构体。

题目整理:

int i = 10, j = 0;
if (j = 0)
i++;
else
i--;

判断:执行后 i 的值为 11

答案:N

解析:

这里:

j = 0

是赋值,不是判断相等。

赋值表达式的值是被赋进去的值,也就是 0。条件为假,所以执行 else

i--;

因此:

i = 9

不是 11

容易踩坑点:

  • = 看成 ==

题目整理:

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

判断:它的作用是将数组各行第一列的元素赋初值,其余元素值为 0

答案:Y

解析:

数组内容是:

1 0 0 0
5 0 0 0
9 0 0 0

每一行只给了第一个元素,没给的元素自动补 0

题目整理:

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

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

答案:N

解析:

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

只有真正使用宏并展开成代码时,如果没有定义 t,才会编译出错。

给新手的建议:

  • 宏不是函数,它更像“复制粘贴”。

题目:在程序中定义了一个结构体类型后,可以多次用它来定义具有该类型的变量。

答案:Y

解析:

例如:

struct Student
{
int id;
int age;
};
struct Student s1;
struct Student s2;

定义好结构体类型后,可以用它定义多个变量。

题目:字符处理函数 strcpy(str1, str2) 的功能是把字符串 1 接到字符串 2 的后面。

答案:N

解析:

strcpy(str1, str2) 是复制:

把 str2 复制到 str1 中

连接字符串用:

strcat(str1, str2);

容易踩坑点:

  • 混淆 strcpystrcat

功能:根据下面公式求 π 值,并作为函数值返回。

题目公式:

π / 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)

原因:当当前项 t 仍然大于精度 eps 时,才需要继续累加。

第二处:

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

应改为:

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

原因:要避免整数除法。比如 1 / 3 如果按整数除法,结果是 0

第三处:

return s;

应改为:

return 2 * s;

原因:公式左边是 π / 2,所以最终结果要乘以 2 才是 π

容易踩坑点:

  • 循环条件写反。
  • 整数除法导致小数被截断。
  • 忘记公式求的是 π / 2

给新手的建议:

  • 级数题先看“下一项如何由上一项得到”,再写循环。

功能:用下面的和式求圆周率的近似值,直到最后一项的绝对值小于等于 0.0001,输出结果显示 6 位小数。

题目公式:

π / 4 = 1 - 1/3 + 1/5 - 1/7 + ...

整理后的正确代码:

#include <stdio.h>
#include <math.h>
void fun(void)
{
int i = 1;
double s = 0.0, t = 1.0, p = 1.0;
while (fabs(t) > 1e-4)
{
s += t;
p = -p;
i += 2;
t = p / i;
}
printf("pi=%f\n", s * 4);
}
int main(void)
{
fun();
return 0;
}

四处错误说明:

第一处:

#include <stdlib.h>

应改为:

#include <math.h>

原因:fabs 是求浮点绝对值的函数,它在 <math.h> 中声明。

第二处:

int s = 0, t = 1, p = 1;

应改为:

double s = 0.0, t = 1.0, p = 1.0;

原因:级数求和涉及小数,不能使用 int 保存。

第三处:

while (fabs(t) <= 1e-4)

应改为:

while (fabs(t) > 1e-4)

原因:当当前项绝对值仍然大于 0.0001 时继续累加。

第四处:

printf("pi=%d\n", s * 4);

应改为:

printf("pi=%f\n", s * 4);

原因:s * 4 是浮点数,应使用 %f 输出。

容易踩坑点:

  • int 保存小数项。
  • 忘记 fabs 要包含 <math.h>
  • 输出浮点数却用了 %d

给新手的建议:

  • 这种交错级数可以用一个符号变量 p 控制正负号。

功能:判断 m 是否为素数,并输出 100 ~ 199 之间的所有素数。

参考答案:

#include <stdio.h>
int fun(int m)
{
int i;
if (m <= 1)
return 0;
for (i = 2; i * i <= m; i++)
{
if (m % i == 0)
return 0;
}
return 1;
}
int main(void)
{
int m, k = 0;
for (m = 100; m < 200; m++)
{
if (fun(m))
{
printf("%4d", m);
k++;
if (k % 5 == 0)
printf("\n");
}
}
printf("\nk=%d\n", k);
return 0;
}

详细解题过程:

素数的定义:

只能被 1 和它本身整除的大于 1 的整数

判断步骤:

  1. 如果 m <= 1,一定不是素数。
  2. 2 开始试除。
  3. 只要发现某个 i 能整除 m,就说明 m 不是素数。
  4. 如果一直没有找到因子,就返回 1,表示是素数。

为什么可以写:

i * i <= m

因为如果 m 有一个大于平方根的因子,那么一定也有一个小于平方根的配对因子。所以只需要检查到平方根附近即可。

如果按原卷红字的更基础写法,也可以写:

int i, k = 1;
if (m <= 1)
k = 0;
for (i = 2; i < m; i++)
{
if (m % i == 0)
k = 0;
}
return k;

这也能得到正确结果,只是效率低一些。

本题输出的素数个数:

100 到 199 之间共有 21 个素数

容易踩坑点:

  • 忘记 1 不是素数。
  • 判断整除应写 m % i == 0
  • 循环范围写错,漏掉某些因子。
  • 输出计数时忘记 k++

给新手的建议:

  • 素数题先写最朴素版本:从 2 试到 m - 1。理解后再优化到 i * i <= m

功能:编写函数判断一个整数能否同时被 35 整除。若能返回 1,否则返回 0。调用该函数求出 15 ~ 300 之间能同时被 35 整除的数的个数。

参考答案:

#include <stdio.h>
int sum(int n)
{
if (n % 3 == 0 && n % 5 == 0)
return 1;
return 0;
}
int main(void)
{
int i, s = 0;
for (i = 15; i <= 300; i++)
{
if (sum(i) == 1)
s = s + 1;
}
printf("s=%d\n", s);
return 0;
}

详细解题过程:

同时被 35 整除,要满足两个条件:

n % 3 == 0

并且:

n % 5 == 0

所以条件要用逻辑与:

n % 3 == 0 && n % 5 == 0

15300 之间,同时能被 35 整除的数,其实就是 15 的倍数:

15, 30, 45, ..., 300

一共有:

300 / 15 = 20

所以程序最终输出:

s=20

容易踩坑点:

  • && 写成 |||| 表示能被 3 或 5 整除,不是同时整除。
  • 循环边界要包含 300,所以写 i <= 300
  • 判断整除要用 %,不是 /

给新手的建议:

  • “同时满足”用 &&
  • “满足其中一个”用 ||

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

  • 函数调用必须带 ()
  • 函数调用可以作为实参,但不能作为形参。
  • 逗号表达式的值看最后一个表达式。
  • 数组初始化没写到的位置会自动补 0
  • 二维数组第一维可以省略,第二维不能省略。
  • 字符数组长度和字符串占用空间不是一回事。
  • 字符串用双引号时会自动包含 '\0'
  • 数组名作为函数实参时,传的是首地址。
  • strcpy 是复制,strcat 是连接,strcmp 是比较。
  • static 局部变量会保留上一次函数调用后的值。
  • 把选择题第 4、21、22、24 题放在一起复习,它们都在考字符数组和字符串长度。
  • 把选择题第 8、9、10、11、16、23 题放在一起复习,它们都在考数组定义和初始化。
  • 把选择题第 1、3、6、13、15、20、25 题放在一起复习,它们都和函数调用有关。
  • 填空题第 6 题建议手动画指针指向图,弄清楚 pmax*pmax 的区别。
  • 填空题第 10 题建议反复记:static 局部变量不会每次调用都重新初始化。
  • 两道改错题都和循环条件有关,复习时重点看“什么时候继续循环,什么时候停止”。
  • 程序设计第 1 题建议自己手写一遍素数判断函数,这是 C 语言入门必练题。
  • 程序设计第 2 题建议重点区分 &&||

第 82 套卷子整体难度不算特别高,但它很爱考“细小但致命”的基础点:

  • 少写函数调用的 (),意思就变了。
  • 数组长度和初值个数不匹配,会直接出错。
  • 字符数组能放 5 个字符,不代表它一定是合法字符串。
  • === 不能混。
  • int 除法会舍去小数。
  • return 一次只能直接返回一个值。
  • 宏定义不是函数,字符串函数也各有分工。

复习这套时,不要只背答案。更稳的方式是每题都问一句:它到底想让我踩哪个坑?能把这个坑说清楚,后面遇到变形题就不容易被绕进去。