exam-91 试卷整理与详解
这份笔记是根据 91.pdf 整理出来的,原卷文件在本地路径:
/Users/wff/Downloads/91.pdf这套卷子的重点依旧是 C 语言基础细节:数组、字符串、函数、指针、递归、宏、循环控制、文件指针、共用体、程序改错和简单程序设计。PDF 的 OCR 对代码里的逗号、分号、花括号、引号、[]、== 等符号有一些识别误差,我整理时按原卷红色标注和 C 语言语法一起校对。
特别提醒:
- 选择题第 8 题题干写的是“一维数组”,但选项实际都是二维数组定义,应该按“二维数组”来理解。
- 选择题第 20 题按传统教材和 Turbo C 考试口径判断,
int n = 5, a[n];是错误的。现代部分编译器可能支持变长数组,但做这类卷子要按教材标准答。 - 程序设计第 1 题原卷红字代码里
double x, y = 0;后直接写while (x >= 1e-6),x没初始化就参与判断,这在真实代码里有问题。我在解析里给出更稳的do...while写法。
1. 答案速查
Section titled “1. 答案速查”1.1 选择题答案
Section titled “1.1 选择题答案”| 题号 | 答案 |
|---|---|
| 1 | A |
| 2 | A |
| 3 | D |
| 4 | D |
| 5 | D |
| 6 | A |
| 7 | A |
| 8 | A |
| 9 | A |
| 10 | A |
| 11 | D |
| 12 | A |
| 13 | C |
| 14 | A |
| 15 | B |
| 16 | B |
| 17 | B |
| 18 | C |
| 19 | D |
| 20 | A |
| 21 | B |
| 22 | A |
| 23 | A |
| 24 | A |
| 25 | B |
1.2 填空题答案
Section titled “1.2 填空题答案”| 题号 | 答案 |
|---|---|
| 1 | x |
| 2 | 1 |
| 3 | 1000 10 |
| 4 | 58 |
| 5 | 6、4、2 |
| 6 | 6102 |
| 7 | p = fun 或 p = fun; |
| 8 | 2 |
| 9 | 0 |
| 10 | 2 或 两 |
1.3 判断题答案
Section titled “1.3 判断题答案”| 题号 | 答案 |
|---|---|
| 1 | N |
| 2 | N |
| 3 | Y |
| 4 | N |
| 5 | N |
| 6 | Y |
| 7 | N |
| 8 | N |
| 9 | N |
| 10 | N |
2. 选择题详解
Section titled “2. 选择题详解”选择题第 1 题
Section titled “选择题第 1 题”题目整理:
int a[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, *p = a;问:不能表示 a 数组元素的表达式是哪一个?
答案:A
错误表达式:
a[*p - a]详细解析:
初始化后:
p 指向 a[0]*p 的值是 1a 表示数组首地址,也就是 &a[0]表达式 *p - a 左边是整数 1,右边是地址 a。整数和地址不能这样相减,所以这个表达式不能正确表示数组元素。
其他选项:
a[9]表示第 10 个元素,合法。
*p表示 p 当前指向的元素,也就是 a[0]。
*p++等价于:
*(p++)先取出当前 p 指向的元素,再让 p 指向下一个元素。
容易踩坑点:
*p是元素值。p或a才是地址。*p++会改变指针p的指向。
给新手的建议:
- 看到数组和指针混合题,先分清“值”和“地址”,不要急着算。
选择题第 2 题
Section titled “选择题第 2 题”题目问:对于 void 类型函数,调用时不可作为哪一种?
答案:A
正确理解:
void 函数没有返回值,所以不能作为表达式使用。例如:
void print_hello(void){ printf("hello\n");}可以这样调用:
print_hello();但不能这样写:
int x = print_hello(); // 错误详细解析:
void 函数调用可以作为一条语句出现在循环体、if 的分支语句、自定义函数体中,但它本身没有结果值,不能参与赋值、加减乘除、比较等表达式运算。
容易踩坑点:
- 以为只要是函数调用就一定有返回值。
给新手的建议:
- 函数名前面的返回类型是
void,就记住:调用它是为了“做事情”,不是为了“拿结果”。
选择题第 3 题
Section titled “选择题第 3 题”题目整理:
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);答案:D
详细解析:
调用:
max(45, 27)函数里:
x = 45y = 27因为:
45 > 27所以:
z = 45函数返回 45,所以输出:
45容易踩坑点:
- 看函数题时只盯着
return,没代入实参。
给新手的建议:
- 函数调用题最好写出“形参对应实参”的表格。
选择题第 4 题
Section titled “选择题第 4 题”题目整理:
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(3) = fib(2) + fib(1) = 2 + 2 = 4所以输出:
4容易踩坑点:
- 看到
fib就套普通斐波那契数列,忘记这题的前两项都是2。
给新手的建议:
- 递归题先看出口值,不要凭记忆套公式。
选择题第 5 题
Section titled “选择题第 5 题”题目问:C 语言中字符串结束符是哪一个?
答案:D
正确写法:
'\0'详细解析:
字符串 "abc" 在内存中实际是:
'a' 'b' 'c' '\0'其中 '\0' 是空字符,ASCII 值为 0,用来表示字符串结束。
其他选项:
'0'是字符零,ASCII 值不是 0。"0"是字符串,里面有字符'0'和结尾'\0'。"\0"是字符串形式,不是单个字符形式。
容易踩坑点:
- 把字符
'0'和空字符'\0'混淆。
给新手的建议:
- 记忆方式:字符串结束符是“反斜杠零”,写成
'\0'。
选择题第 6 题
Section titled “选择题第 6 题”题目问:以下能正确定义数组并正确赋初值的是哪一个?
答案:A
正确写法:
int d[3][2] = {{1, 2}, {3, 4}};详细解析:
d[3][2] 表示 3 行 2 列。初始化给了前两行:
1 23 40 0没有显式给出的元素自动补 0。
其他选项的问题:
int a[1][2] = {{1}, {3}};只有 1 行,却给了 2 行初值。int c[2]0 = ...语法错误。int N = 5, b[N][N];按传统教材口径,数组长度不能使用普通变量。
容易踩坑点:
- 二维数组少给初值可以补
0,但不能多给超出行数的初值。
给新手的建议:
- 二维数组初始化题,先看“定义了几行几列”,再看初值有没有越界。
选择题第 7 题
Section titled “选择题第 7 题”题目整理:
int a[][4] = {0, 0};问:下面不正确的叙述是哪一个?
答案:A
错误说法:
有元素 a[0][0] 和 a[0][1] 可得到初值 0,其余元素均得不到初值 0。详细解析:
二维数组第二维是 4,初值一共有 2 个:
0, 0因此编译器推出第一维是 1,数组等价于:
int a[1][4] = {0, 0};数组实际内容是:
a[0][0] = 0a[0][1] = 0a[0][2] = 0a[0][3] = 0前两个显式给了 0,后两个也会自动补 0。
容易踩坑点:
- 以为只有写出来的元素才初始化为
0。
给新手的建议:
- 数组只要做了部分初始化,没写到的元素会自动补
0。
选择题第 8 题
Section titled “选择题第 8 题”题目问:以下不能正确定义二维数组的选项是哪一个?
答案:A
错误写法:
int a[2][] = {{1, 2}, {3, 4}};详细解析:
二维数组定义时,第二维不能省略。
可以省略第一维:
int a[][2] = {1, 2, 3, 4};但不能省略第二维:
int a[2][] = {{1, 2}, {3, 4}}; // 错误因为编译器需要知道每一行有几个元素,才能计算 a[i][j] 的位置。
容易踩坑点:
- 以为有初始化列表就可以省略任意一维。
给新手的建议:
- 二维数组口诀:第一维可以省,第二维必须写。
选择题第 9 题
Section titled “选择题第 9 题”题目整理:
int a[3][4];问:非法引用是哪一个?
答案:A
非法引用:
a[0][4]详细解析:
a[3][4] 表示:
行下标:0、1、2列下标:0、1、2、3所以:
a[0][4]列下标 4 越界。
其他选项:
a[1][3] // 合法a[4 - 2][0] // 等价于 a[2][0],合法a[0][2 * 1] // 等价于 a[0][2],合法容易踩坑点:
- 数组长度是
4,最后一个合法下标是3。
给新手的建议:
- 每次看到
a[m][n],先写出合法下标范围:第一维0 ~ m - 1,第二维0 ~ n - 1。
选择题第 10 题
Section titled “选择题第 10 题”题目问:s1 和 s2 指向两个字符串,若要求当 s1 所指字符串大于 s2 所指字符串时执行语句 S;,正确写法是哪一个?
答案:A
正确写法:
if (strcmp(s1, s2) > 0) S;详细解析:
字符串不能用:
s1 > s2这样比较,因为这比较的是两个地址大小,不是字符串内容大小。
字符串比较要用 strcmp:
strcmp(s1, s2) > 0 表示 s1 大于 s2strcmp(s1, s2) == 0 表示 s1 等于 s2strcmp(s1, s2) < 0 表示 s1 小于 s2容易踩坑点:
- 用
s1 > s2比较字符串内容。 - 只写
strcmp(s1, s2),这只能判断“不相等”,不能判断“大于”。
给新手的建议:
- 字符串比较题,第一反应找
strcmp,再看返回值和0的关系。
选择题第 11 题
Section titled “选择题第 11 题”题目整理:
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后面”。
选择题第 12 题
Section titled “选择题第 12 题”题目整理:
static char str[10] = "China";问:数组元素个数是多少?
答案:A
详细解析:
数组长度已经写明:
str[10]所以数组元素个数就是 10。
字符串 "China" 的有效字符长度是 5,再加上结束符 '\0' 占 6 个位置,但数组整体空间仍然是 10。
容易踩坑点:
- 把字符串长度
5当成数组元素个数。 - 把字符串占用空间
6当成数组元素个数。
给新手的建议:
- 题目问“数组元素个数”,优先看方括号里的数字。
选择题第 13 题
Section titled “选择题第 13 题”题目问:以下程序段中,不能正确赋字符串的是哪一个?
答案:C
错误写法:
char s[10];s = "abcdefg";详细解析:
数组名不能作为赋值号左边整体赋值。
定义时可以这样初始化:
char s[10] = "abcdefg";定义后要复制字符串,应写:
strcpy(s, "abcdefg");容易踩坑点:
- 把字符数组名当成普通变量。
给新手的建议:
- 字符数组定义时可以用
=初始化。 - 定义之后再放字符串,要用
strcpy。
选择题第 14 题
Section titled “选择题第 14 题”题目整理:
strcat(strcpy(str1, str2), str3)问:该函数调用的功能是什么?
答案:A
详细解析:
先看内层:
strcpy(str1, str2)作用是把 str2 复制到 str1 中。
strcpy 的返回值是目标字符串 str1 的地址,所以外层等价于:
strcat(str1, str3)作用是把 str3 连接到 str1 后面。
整体功能:
先将 str2 复制到 str1 中,再将 str3 连接到 str1 后面。容易踩坑点:
- 不知道
strcpy会返回目标字符串地址。 - 字符串函数嵌套时从外往里看,导致顺序错乱。
给新手的建议:
- 函数嵌套题从最里面开始分析。
选择题第 15 题
Section titled “选择题第 15 题”题目整理:
int a[10];问:给数组 a 的所有元素分别赋值为 1、2、3、... 的语句是哪一个?
答案:B
正确写法:
for (i = 1; i < 11; i++) a[i - 1] = i;详细解析:
当 i = 1 时:
a[i - 1] = a[0] = 1当 i = 2 时:
a[i - 1] = a[1] = 2一直到 i = 10:
a[i - 1] = a[9] = 10刚好给 a[0] ~ a[9] 赋值。
容易踩坑点:
- 写成
a[i] = i,会跳过a[0],并访问到a[10]越界。
给新手的建议:
- 数组长度是 10,循环赋值时一定要覆盖
0 ~ 9。
选择题第 16 题
Section titled “选择题第 16 题”题目整理:
int a[10] = {6, 7, 8, 9, 10};答案:B
详细解析:
数组初始化从下标 0 开始:
a[0] = 6a[1] = 7a[2] = 8a[3] = 9a[4] = 10没有显式初始化的元素自动补 0:
a[5] ~ a[9] = 0容易踩坑点:
- 以为第一个初值赋给
a[1]。 - 以为初值
6会对应下标6。
给新手的建议:
- 初始化列表永远从第一个元素
a[0]开始依次放。
选择题第 17 题
Section titled “选择题第 17 题”题目整理:
int i, k, a[10], p[3];
k = 5;for (i = 0; i < 10; i++) a[i] = i;
for (i = 0; i < 3; i++) p[i] = a[i * (i + 1)];
for (i = 0; i < 3; i++) k += p[i] * 2;
printf("%d\n", k);答案:B
详细解析:
第一段循环后:
a[0]=0, a[1]=1, a[2]=2, a[3]=3, a[4]=4, ...第二段循环:
| i | i * (i + 1) | p[i] |
|---|---|---|
| 0 | 0 | p[0] = a[0] = 0 |
| 1 | 2 | p[1] = a[2] = 2 |
| 2 | 6 | p[2] = a[6] = 6 |
第三段循环:
k = 5 + 0 * 2 + 2 * 2 + 6 * 2 = 5 + 0 + 4 + 12 = 21容易踩坑点:
i * (i + 1)不是i + 1。- 忘记
k初始值是5。
给新手的建议:
- 多个循环连续出现时,分段列变量表,不要脑算硬扛。
选择题第 18 题
Section titled “选择题第 18 题”题目整理:
int x[3][3] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
for (i = 0; i < 3; i++) printf("%d,", x[i][2 - i]);答案:C
详细解析:
二维数组内容是:
第0列 第1列 第2列第0行 1 2 3第1行 4 5 6第2行 7 8 9循环取值:
| i | 2 - i | 访问元素 | 值 |
|---|---|---|---|
| 0 | 2 | x[0][2] | 3 |
| 1 | 1 | x[1][1] | 5 |
| 2 | 0 | x[2][0] | 7 |
所以输出:
3,5,7,选项中对应 3,5,7。
容易踩坑点:
- 二维数组按行存放,初值不是按列填。
2 - i每次都在变。
给新手的建议:
- 遇到二维数组输出题,先画表格,再按下标找位置。
选择题第 19 题
Section titled “选择题第 19 题”题目整理:
exce((v1, v2), (v3, v4, v5), v6);问:实参个数是多少?
答案:D
详细解析:
函数调用的最外层实参是:
(v1, v2)(v3, v4, v5)v6所以实参个数是 3。
括号里面的逗号属于逗号表达式,不是函数参数分隔符。
容易踩坑点:
- 一看到逗号就全数进去。
给新手的建议:
- 数函数实参时,只数最外层逗号。
选择题第 20 题
Section titled “选择题第 20 题”题目问:以下定义语句中错误的是哪一个?
答案:A
错误写法:
int n = 5, a[n];详细解析:
按传统 C 语言教材和 Turbo C 考试口径,数组长度应该是常量表达式。
n 是变量,不是常量表达式,所以这题判错。
其他选项按原卷应理解为:
char *a[3];char s[10] = "test";int a[] = {1, 2};这些写法是合法的。
补充提醒:
现代一些 C 编译器支持变长数组,例如 GCC 在某些模式下可能允许 int a[n];。但这类教材卷子通常不按这个标准来判。
容易踩坑点:
- 把“编译器可能支持”和“考试标准答案”混在一起。
给新手的建议:
- 做基础考试题时,数组长度优先按“常量表达式”理解。
选择题第 21 题
Section titled “选择题第 21 题”题目整理:
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;}答案:B
详细解析:
fun 函数中使用的 w 是全局变量:
int w = 3;因为 main 里的:
int w = 10;只在 main 函数内部有效,不能影响 fun 函数里的 w。
递归计算:
fun(0) = 3fun(1) = fun(0) * 1 = 3fun(2) = fun(1) * 2 = 6fun(3) = fun(2) * 3 = 18fun(4) = fun(3) * 4 = 72fun(5) = fun(4) * 5 = 360最后在 main 中:
fun(5) * w = 360 * 10 = 3600容易踩坑点:
- 混淆全局变量
w = 3和局部变量w = 10。 - 递归题没有先找到出口。
给新手的建议:
- 同名变量出现时,先看它在哪个函数、哪个作用域里生效。
选择题第 22 题
Section titled “选择题第 22 题”题目整理:
int a[10];问:对 a 数组元素的正确引用是哪一个?
答案:A
正确写法:
a[10 - 10]详细解析:
先算下标:
10 - 10 = 0所以:
a[10 - 10]等价于:
a[0]这是合法元素。
其他选项:
a[3,5]中3,5是逗号表达式,结果为5,虽然某些编译器会把它当成a[5],但教材题通常不作为规范数组引用写法。a(5)不是数组元素引用。a[10]越界,合法下标是0 ~ 9。
容易踩坑点:
- 数组长度是 10,最后一个合法下标是 9。
给新手的建议:
- 数组引用最稳写法就是
数组名[整数下标]。
选择题第 23 题
Section titled “选择题第 23 题”题目问:以下能对二维数组 a 进行正确初始化的语句是哪一个?
答案:A
按原卷选项应理解为:
int a[][3] = {1, 2, 3, 4, 5};详细解析:
二维数组初始化时,第一维可以省略,第二维必须写出来。
这里第二维是 3,初值一共有 5 个,可以按每行 3 个来放:
1 2 34 5 0最后一个位置自动补 0。
其他选项的问题:
int a[2][3] = {1, 2, 3, 4, 5, 6, 7};初值太多。int a[][] = ...第二维省略,错误。int a[2][] = ...第二维省略,错误。
容易踩坑点:
- 第一维可以省略,第二维不能省略。
- 初值少可以补
0,初值多会出错。
给新手的建议:
- 二维数组初始化只记一句:让编译器必须知道“每行几个元素”。
选择题第 24 题
Section titled “选择题第 24 题”题目整理:
char a[10];问:以下语句中不能从键盘上给 a 数组的所有元素输入值的是哪一个?
答案:A
错误写法:
a = getchar();详细解析:
a 是数组名,不能作为赋值号左边整体赋值。
而且:
getchar()一次只能读一个字符,不可能直接把整个数组填满。
如果要逐个输入 10 个字符,可以写:
for (i = 0; i < 10; i++) a[i] = getchar();如果输入字符串,可以写:
scanf("%s", a);容易踩坑点:
- 把字符数组当成一个字符变量。
- 忘记数组名不能整体赋值。
给新手的建议:
- 给数组元素赋值,要么逐个
a[i] = ...,要么用字符串输入函数。
选择题第 25 题
Section titled “选择题第 25 题”题目问:下列不能正确进行字符串赋值的语句是哪一个?
答案:B
错误写法:
char s[5] = {'a', 'b', 'c', 'd', 'e'};详细解析:
这只是一个包含 5 个普通字符的字符数组:
'a' 'b' 'c' 'd' 'e'没有字符串结束符:
'\0'所以不能作为正常字符串使用。
其他选项:
char s[] = "abcde";编译器会自动分配 6 个字符空间,保存:
'a' 'b' 'c' 'd' 'e' '\0'char s[6] = "abcde";刚好 6 个位置,合法。
char s[5] = "abc";实际占 4 个位置,剩余位置补 0,合法。
容易踩坑点:
- 把普通字符数组和字符串混为一谈。
给新手的建议:
- 判断是不是字符串,要看最后有没有
'\0'。
3. 填空题详解
Section titled “3. 填空题详解”填空题第 1 题
Section titled “填空题第 1 题”题目功能:求 x 的 y 次方。
题目整理:
double fun(double x, int y){ int i; double z;
for (i = 1, z = x; i < y; i++) z = z * ____;
return z;}答案:x
完整语句:
z = z * x;详细解析:
初始:
z = x循环每执行一次,就再乘一个 x。
如果 y = 3:
开始:z = x第 1 次:z = x * x第 2 次:z = x * x * x所以空里应填 x。
容易踩坑点:
- 把
z初始值已经是x这件事忘掉。
给新手的建议:
- 幂运算循环题可以拿
x^3手动走一遍。
填空题第 2 题
Section titled “填空题第 2 题”题目整理:
int x, y, z;x = y = z = 2;x = y == z;问:x 的值是多少?
答案:1
详细解析:
先执行:
x = y = z = 2;三个变量都是 2。
再执行:
x = y == z;因为:
y == z2 == 2结果为真,C 语言中真用 1 表示。
所以:
x = 1容易踩坑点:
- 把
==看成赋值号。 - 不知道关系表达式的结果是
1或0。
给新手的建议:
=是赋值,==是判断相等。写题时可以故意读出来:“把y == z的判断结果赋给x”。
填空题第 3 题
Section titled “填空题第 3 题”题目整理:
#define N 10#define s(x) x * x#define f(x) (x * x)
int i1, i2;i1 = 1000 / s(N);i2 = 1000 / f(N);printf("%d %d\n", i1, i2);答案:1000 10
详细解析:
宏只是文本替换。
先看:
i1 = 1000 / s(N);s(N) 展开为:
N * N再替换 N = 10:
i1 = 1000 / 10 * 10;/ 和 * 同级,从左到右:
1000 / 10 * 10 = 100 * 10 = 1000再看:
i2 = 1000 / f(N);f(N) 展开为:
(N * N)替换后:
i2 = 1000 / (10 * 10);所以:
i2 = 1000 / 100 = 10容易踩坑点:
- 以为
s(N)和f(N)都等价于10 * 10。 - 忘记宏展开没有自动加括号。
给新手的建议:
- 写宏时尽量写成:
#define S(x) ((x) * (x))填空题第 4 题
Section titled “填空题第 4 题”题目整理:
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,结束当前这一行的内层循环。 - 遇到正数:加到
s里。
逐行看:
| 行 | 数据 | 累加情况 |
|---|---|---|
| 第0行 | 1, 2, -3, -4 | 加 1 + 2,负数跳过 |
| 第1行 | 0, -12, -13, 14 | 第一个就是 0,本行直接结束 |
| 第2行 | -21, 23, 0, -24 | -21 跳过,加 23,遇 0 结束 |
| 第3行 | -31, 32, -33, 0 | -31 跳过,加 32,-33 跳过,遇 0 结束 |
所以:
s = 1 + 2 + 23 + 32 = 58容易踩坑点:
continue只是跳过本次循环,不是结束整个内层循环。break才会结束当前内层循环。- 这里的
break只跳出内层for(j...),不会跳出外层for(i...)。
给新手的建议:
- 遇到
continue和break混合题,最好按每一行单独模拟。
填空题第 5 题
Section titled “填空题第 5 题”题目整理:
a = (b = 4) + (c = 2);答案:a = 6,b = 4,c = 2
详细解析:
赋值表达式本身也有值。
(b = 4)执行后 b 为 4,这个表达式的值也是 4。
(c = 2)执行后 c 为 2,这个表达式的值也是 2。
所以:
a = 4 + 2 = 6容易踩坑点:
- 只看到
b和c被赋值,忘记整个表达式还要计算给a。
给新手的建议:
- 赋值表达式的值就是被赋进去的那个值。
填空题第 6 题
Section titled “填空题第 6 题”题目整理:
int n1, n2;
scanf("%d", &n2);while (n2 != 0){ n1 = n2 % 10; n2 = n2 / 10; printf("%d", n1);}输入:
2016答案:6102
详细解析:
每次循环:
n2 % 10取个位。n2 / 10去掉个位。- 输出刚取到的个位。
过程:
| 循环 | n2 原值 | n1 = n2 % 10 | n2 = n2 / 10 | 输出 |
|---|---|---|---|---|
| 1 | 2016 | 6 | 201 | 6 |
| 2 | 201 | 1 | 20 | 1 |
| 3 | 20 | 0 | 2 | 0 |
| 4 | 2 | 2 | 0 | 2 |
所以输出:
6102容易踩坑点:
201 / 10是整数除法,结果是20,不是20.1。- 中间的
0也会输出。
给新手的建议:
- “反向输出整数”题,核心就是
% 10取个位、/ 10去个位。
填空题第 7 题
Section titled “填空题第 7 题”题目问:将函数 fun 的入口地址赋给指针变量 p 的语句是什么?
答案:p = fun;
详细解析:
函数名 fun 可以表示函数入口地址。
如果 p 是已经定义好的函数指针,那么:
p = fun;就是把函数 fun 的入口地址赋给 p。
注意不要写成:
p = fun();fun() 表示调用函数,拿到函数返回值,不是函数入口地址。
容易踩坑点:
- 函数名后面加括号就变成“调用函数”了。
给新手的建议:
- 函数名不加
(),常常表示函数地址;加(),就是调用函数。
填空题第 8 题
Section titled “填空题第 8 题”题目整理:
int a = 3, b = 4, c = 5, d = 2;
if (a > b) if (b > c) printf("%d", d++ + 1); else printf("%d", ++d + 1);
printf("%d\n", d);答案:2
详细解析:
先判断外层:
a > b3 > 4结果为假,所以整个内层 if (b > c) ... else ... 都不会执行。
因此 d 没有变化,仍然是:
d = 2最后执行:
printf("%d\n", d);输出:
2容易踩坑点:
- 以为
else会和外层if (a > b)配对。 - 忽略“外层条件为假,内层根本不会执行”。
给新手的建议:
- 悬挂
else题记住:else总是和离它最近且尚未匹配的if配对。
填空题第 9 题
Section titled “填空题第 9 题”题目整理:
a = 12;(0 < a) && (a < 2)答案:0
详细解析:
先看左边:
0 < 12 -> 真 -> 1再看右边:
12 < 2 -> 假 -> 0逻辑与 && 要求两边都为真才是真:
1 && 0 -> 0容易踩坑点:
- 把数学写法
0 < a < 2直接搬到 C 语言里。
给新手的建议:
- C 语言里判断范围,要写成
(0 < a) && (a < 2)。
填空题第 10 题
Section titled “填空题第 10 题”题目问:unsigned int 定义无符号基本整型变量,在 Turbo C 教材环境中分配几个字节?
答案:2 或 两
详细解析:
按传统 Turbo C 教材环境:
unsigned int 占 2 个字节补充提醒:
现代很多编译器中,unsigned int 通常占 4 个字节。考试题如果没有特别说明,通常按教材环境答。
容易踩坑点:
- 把现代电脑上的结果和教材考试环境混在一起。
给新手的建议:
- 做题按教材环境;实际写代码时可以用
sizeof(unsigned int)查看。
4. 判断题详解
Section titled “4. 判断题详解”判断题第 1 题
Section titled “判断题第 1 题”题目:C 语言所有函数都是外部函数。
答案:N
解析:
C 语言函数可以分为外部函数和内部函数。
例如,用 static 修饰的函数具有内部链接属性,只能在当前源文件中使用:
static void helper(void){ printf("helper\n");}所以“所有函数都是外部函数”是错误的。
判断题第 2 题
Section titled “判断题第 2 题”题目整理:
file *fp;fp = fopen("a.txt", "r");判断:在 Turbo C 中是否合法?
答案:N
解析:
文件指针类型应写成大写:
FILE *fp;file 不是标准类型名。
容易踩坑点:
- C 语言区分大小写,
FILE和file不是同一个东西。
判断题第 3 题
Section titled “判断题第 3 题”题目:整数 -32100 可以赋值给 int 型和 long int 型变量。
答案:Y
解析:
按传统 16 位有符号 int 环境,取值范围通常是:
-32768 ~ 32767-32100 在这个范围内,所以可以赋给 int,也可以赋给 long int。
补充提醒:
不同编译器中 int 的字节数可能不同,但这道题按传统教材环境判断。
判断题第 4 题
Section titled “判断题第 4 题”题目整理:
int i = 10, j = 2;i *= j + 8;判断:执行后 i 的值为 28。
答案:N
解析:
复合赋值:
i *= j + 8;等价于:
i = i * (j + 8);所以:
i = 10 * (2 + 8) = 100不是 28。
容易踩坑点:
- 错算成
i = i * j + 8。
判断题第 5 题
Section titled “判断题第 5 题”题目:
strlen("ASDFG\n")判断:值是 7。
答案:N
解析:
字符串 "ASDFG\n" 的有效字符是:
A S D F G \n一共 6 个字符。
strlen 不统计字符串结束符 '\0'。
所以结果是:
6容易踩坑点:
- 把
'\0'也算进strlen。 - 忘记
\n是一个字符,不是两个字符。
判断题第 6 题
Section titled “判断题第 6 题”题目:共用体变量所占的内存长度等于最长成员的长度。
答案:Y
解析:
共用体所有成员共用同一段内存空间,所以它的大小至少要能放下最长的成员。
例如:
union Data{ char c; int i; double d;};这个共用体的大小通常由最大的成员 double d 决定。
补充提醒:
实际编译器还可能因为内存对齐,让共用体大小略大于最长成员,但基础教材里通常按“等于最长成员长度”判断。
判断题第 7 题
Section titled “判断题第 7 题”题目:
int a[10] = {1 * 10};判断:这样可以使数组中全部元素的值为 1。
答案:N
解析:
1 * 10 是一个表达式,结果是 10。
所以数组初始化为:
a[0] = 10a[1] ~ a[9] = 0不是全部元素都为 1。
容易踩坑点:
- 以为
{1 * 10}表示 10 个1。
判断题第 8 题
Section titled “判断题第 8 题”题目整理:
#define S(a, b) t = a; a = b; b = t判断:由于变量 t 没定义,所以此宏定义是错误的。
答案:N
解析:
宏定义本身只是文本替换规则,不会在定义时检查 t 有没有定义。
但是使用宏时,如果展开后的代码里没有定义 t,才会编译报错。
例如:
int t;S(x, y);这种前提下才有可能正常展开使用。
容易踩坑点:
- 以为宏定义阶段会做完整语法检查。
给新手的建议:
- 宏不是函数,它更像“复制粘贴替换”。
判断题第 9 题
Section titled “判断题第 9 题”题目:
char *p = "girl";判断:含义是定义字符型指针变量 p,p 的值是字符串 "girl"。
答案:N
解析:
这句话前半部分对,后半部分不严谨。
p 的值不是整个字符串,而是字符串常量首字符的地址。
可以理解为:
p 指向字符串 "girl" 的第一个字符 'g'容易踩坑点:
- 把指针变量的值理解成它指向的整段内容。
给新手的建议:
- 指针变量里存的是地址,不是它指向的全部数据。
判断题第 10 题
Section titled “判断题第 10 题”题目:循环结构中的 continue 语句是使整个循环终止执行。
答案:N
解析:
continue 的作用是结束本次循环,直接进入下一次循环判断。
真正终止整个循环的是:
break;区别:
continue:跳过本次,继续下次break:结束循环,直接出来容易踩坑点:
- 把
continue和break混淆。
5. 改错题详解
Section titled “5. 改错题详解”功能:一个已排好序的一维数组,输入一个数 number,要求按原来的排序规律将它插入数组中。
整理后的正确代码:
#include <stdio.h>
int main(void){ int a[11] = {1, 4, 6, 9, 13, 16, 19, 28, 40, 100}; int temp1, temp2, number, end, i, j;
for (i = 0; i < 10; i++) printf("%5d", a[i]);
printf("\n"); scanf("%d", &number);
end = a[9];
if (number > end) { a[10] = number; } else { for (i = 0; i < 10; i++) { if (a[i] > number) { temp1 = a[i]; a[i] = number;
for (j = i + 1; j < 11; j++) { temp2 = a[j]; a[j] = temp1; temp1 = temp2; }
break; } } }
for (i = 0; i < 11; i++) printf("%6d", a[i]);
return 0;}主要错误说明:
第一处:
for (i = 0; i <= 10; i++)应改为:
for (i = 0; i < 10; i++)原因:原数组只有 10 个已有元素,打印原数组时只能打印 a[0] ~ a[9]。a[10] 是预留给新插入元素的位置。
第二处:
end = a[10];应改为:
end = a[9];原因:原数组最后一个有效元素是 a[9],不是 a[10]。
第三处:
a[11] = number;应改为:
a[10] = number;原因:数组 a[11] 的合法下标是 0 ~ 10,a[11] 越界。
第四处:
if (a[i] < number)应改为:
if (a[i] > number)原因:数组是从小到大排序,应该找到第一个比 number 大的位置,把 number 插到它前面。
补充修正:
原卷代码里还有一些符号错误,上机时也要改:
scanf("%d" &number);应写为:
scanf("%d", &number);以及:
a[i] = number,应写为:
a[i] = number;详细解题过程:
假设原数组是:
1 4 6 9 13 16 19 28 40 100如果输入 20:
20不大于最后一个元素100。- 从前往后找第一个比
20大的数。 - 找到
28,把20放到28的位置。 - 原来的
28, 40, 100依次后移。
结果:
1 4 6 9 13 16 19 20 28 40 100容易踩坑点:
- 数组
a[11]的最后一个合法下标是10。 - 原数组已有 10 个元素,最后一个已有元素是
a[9]。 - 插入排序题要找第一个“大于插入值”的位置。
给新手的建议:
- 这种题不要先看代码,先拿一个具体数字,比如
20,在纸上模拟插入过程。
功能:求 0 ~ 7 这 8 个数字所能组成的奇数个数。
整理后的正确代码:
#include <stdio.h>
int main(void){ long sum = 4, s = 4; int j;
for (j = 2; j <= 8; j++) { printf("\n%ld", sum);
if (j <= 2) s *= 7; else s *= 8;
sum += s; }
printf("\nsum=%ld", sum); return 0;}三处错误说明:
第一处:
long sum = 4; s = 4;应改为:
long sum = 4, s = 4;原因:s 也需要声明为 long 类型。用分号断开后,s = 4; 就变成使用一个还没声明的变量。
第二处:
printf("\n%d", sum);应改为:
printf("\n%ld", sum);原因:sum 是 long 类型,输出格式应使用 %ld。
第三处:
if (j < 2)应改为:
if (j < 3)也可以写成:
if (j <= 2)原因:当统计两位奇数时,最高位不能为 0,所以最高位只有 1 ~ 7 共 7 种选择;从三位数开始,中间位才可以有 8 种选择。
详细解题过程:
组成奇数,个位必须是奇数:
1、3、5、7个位有 4 种选择,所以一位奇数有 4 个。
两位奇数:
十位不能为 0,有 7 种选择个位必须奇数,有 4 种选择所以新增两位奇数:
7 * 4 = 28三位及以上:
- 最高位不能为 0。
- 中间位可以是
0 ~ 7任意数字。 - 个位必须是
1、3、5、7。
这段程序用 s 表示当前位数新增的奇数个数,用 sum 累加总数。
容易踩坑点:
long输出写成%d。- 多个变量声明时把逗号写成分号。
- 最高位不能为
0,个位必须是奇数。
给新手的建议:
- 计数题先把“个位、最高位、中间位”分开想,别一上来就看循环。
6. 程序设计题详解
Section titled “6. 程序设计题详解”程序设计 1
Section titled “程序设计 1”题目:按公式计算 y 的值,精度为 1e-6,即最后一项的绝对值小于 1e-6 时结束。
从卷面代码看,本题要累加的项是:
1 / (r * r + 1)也就是:
y = 1/(1*1+1) + 1/(2*2+1) + 1/(3*3+1) + ...参考答案:
#include <stdio.h>#include <math.h>
int main(void){ int r = 1; double x, y = 0.0;
do { x = 1.0 / (r * r + 1);
if (fabs(x) >= 1e-6) { printf("x=%f\n", x); y += x; }
r++; } while (fabs(x) >= 1e-6);
printf("y=%f\n", y); return 0;}详细解题过程:
第一步,用 r 表示当前是第几项:
int r = 1;第二步,计算当前项:
x = 1.0 / (r * r + 1);注意这里必须写 1.0,不要写 1。写 1.0 可以保证进行浮点运算。
第三步,如果当前项的绝对值还不小于 1e-6,就把它加到 y 中:
if (fabs(x) >= 1e-6) y += x;第四步,r++,准备计算下一项。
为什么这里推荐 do...while?
原卷红字代码大致是:
double x, y = 0;
while (x >= 1e-6){ x = 1.0 / (r * r + 1); y = y + x; r++;}问题在于:x 没有初始化,就直接在 while (x >= 1e-6) 里使用了。未初始化变量的值是不确定的,程序可能直接不进入循环,也可能出现奇怪结果。
用 do...while 更适合这种“先算出一项,再判断这一项是否达到精度”的题。
容易踩坑点:
x未初始化就参与while判断。- 写成
1 / (r * r + 1),容易产生整数除法问题。 - 最后一项已经小于
1e-6时,不应该再累加进去。 - 题目说“绝对值”,更稳妥的写法是用
fabs(x)。
给新手的建议:
- 级数求和题先确定三件事:每一项怎么算、什么时候加、什么时候停。
- 如果停止条件依赖“刚刚算出来的这一项”,优先考虑
do...while。
程序设计 2
Section titled “程序设计 2”题目:输入正整数 m 和 n,设 100 <= m <= n <= 999,输出 m 到 n 之间满足条件的三位数。
条件:
个位数的立方 + 十位数的平方 + 百位数 = 该数本身例如:
135 = 1 + 3 * 3 + 5 * 5 * 5参考答案:
#include <stdio.h>
int main(void){ int m, n; int i, a, b, c;
scanf("%d%d", &m, &n);
for (i = m; i <= n; i++) { a = i % 10; b = i / 10 % 10; c = i / 100 % 10;
if (i == c + b * b + a * a * a) printf("%d ", i); }
printf("\n"); return 0;}详细解题过程:
第一步,遍历 m ~ n:
for (i = m; i <= n; i++)第二步,拆出三位数的各个位:
a = i % 10; // 个位b = i / 10 % 10; // 十位c = i / 100 % 10; // 百位例如 i = 135:
a = 135 % 10 = 5b = 135 / 10 % 10 = 13 % 10 = 3c = 135 / 100 % 10 = 1 % 10 = 1第三步,判断是否满足条件:
if (i == c + b * b + a * a * a)对 135:
c + b * b + a * a * a= 1 + 3 * 3 + 5 * 5 * 5= 1 + 9 + 125= 135所以输出 135。
如果输入:
135 600输出:
135 175 518 598容易踩坑点:
- 忘记声明
m和n。 - 个位、十位、百位拆错。
- 把
a * a * a写成a ^ 3。C 语言里^不是乘方,而是按位异或。 - 循环边界要包含
n,所以写i <= n。
给新手的建议:
- 拆三位数模板一定要背熟:
ge = x % 10;shi = x / 10 % 10;bai = x / 100;7. 这套卷子最容易暴露的问题
Section titled “7. 这套卷子最容易暴露的问题”这套 exam-91 很适合检查下面这些基础:
- 数组下标从
0开始,长度为10的数组合法下标是0 ~ 9。 - 二维数组第一维可以省略,第二维不能省略。
- 字符串结束符是
'\0',不是'0'。 - 字符数组不等于字符串,字符串必须有结束符。
- 数组名不能整体赋值。
- 字符串比较、复制、连接分别用
strcmp、strcpy、strcat。 void函数没有返回值,不能放进表达式里用。- 函数名不加括号可以表示入口地址。
continue和break的作用不同。- 宏定义只是文本替换,括号非常重要。
8. 给新手的复习建议
Section titled “8. 给新手的复习建议”- 把选择题第 6、7、8、9、15、16、18、22、23、24 题放在一起复习,它们都和数组有关。
- 把选择题第 5、10、11、12、13、14、25 题放在一起复习,它们都和字符串有关。
- 把选择题第 2、3、4、19、21 题放在一起复习,它们都和函数调用、递归、实参个数有关。
- 填空题第 3 题一定要手动展开宏,别直接心算。
- 填空题第 4 题建议画二维表格,分别标出
continue和break的效果。 - 改错题第 1 题建议自己输入一个
number = 20,在纸上模拟数组后移过程。 - 程序设计第 1 题重点记住:循环条件依赖当前项时,不要使用未初始化变量。
- 程序设计第 2 题重点练
% 10和/ 10拆位。
9. 最后总结
Section titled “9. 最后总结”第 91 套卷子看起来题目很多,但核心其实还是几条主线:
- 数组:下标范围、初始化、二维数组列数不能省略。
- 字符串:
'\0'、字符数组空间、strcpy、strcat、strcmp。 - 函数:返回值、
void、递归、函数指针、实参个数。 - 表达式:赋值表达式、关系表达式、逻辑表达式、宏展开。
- 循环:
break、continue、边界条件、未初始化变量。
复习时不要只背答案。更好的做法是每题都问自己一句:它到底想考哪个坑?能说出这个坑,下一次遇到变形题就不容易慌了。