exam-84 试卷整理与详解
这份笔记是根据 84.pdf 整理出来的,原卷文件在本地路径:
/Users/wff/Downloads/84.pdf这套题继续集中考 C 语言基础:变量存储类别、函数调用、数组初始化、字符串、二维数组、结构体、宏、switch、素数判断、闰年判断和二维数组最大值。
PDF 的 OCR 对代码符号有一些误差,比如 '\0'、==、[]、{}、printf、strcpy、strcat 等容易识别错。我整理时按原卷红色答案和 C 语言语法做了校对。
特别提醒:
- 本卷多处是传统 C 教材/考试口径,比如函数没有写返回类型时默认
int。现代 C 里不要这样写,实际代码要明确写返回类型。 gets()在旧教材里常出现,但现代 C 已经不建议使用,真实写程序优先用fgets()。int n = 5, a[n];这类变长数组在部分现代编译器中可能支持,但本卷按传统基础考试口径判错。- 二维数组题最重要的口诀是:第一维可以省略,第二维不能省略。
- 字符串题一定记住结尾隐藏的
'\0',它也要占一个数组位置。
1. 答案速查
Section titled “1. 答案速查”1.1 选择题答案
Section titled “1.1 选择题答案”| 题号 | 答案 |
|---|---|
| 1 | C |
| 2 | B |
| 3 | A |
| 4 | C |
| 5 | D |
| 6 | D |
| 7 | D |
| 8 | B |
| 9 | D |
| 10 | B |
| 11 | D |
| 12 | D |
| 13 | B |
| 14 | B |
| 15 | B |
| 16 | B |
| 17 | D |
| 18 | A |
| 19 | A |
| 20 | D |
| 21 | C |
| 22 | C |
| 23 | D |
| 24 | D |
| 25 | A |
1.2 填空题答案
Section titled “1.2 填空题答案”| 题号 | 答案 |
|---|---|
| 1 | strcat |
| 2 | 1 |
| 3 | a*b*c*d |
| 4 | 1331 |
| 5 | 1 B |
| 6 | a=1.0;b=1.0;s=1.0; |
| 7 | 循环 或 重复 |
| 8 | struct DATE d={2006,10,1}; |
| 9 | 0 |
| 10 | n/=10 或 n=n/10 |
1.3 判断题答案
Section titled “1.3 判断题答案”| 题号 | 答案 |
|---|---|
| 1 | Y |
| 2 | N |
| 3 | Y |
| 4 | N |
| 5 | Y |
| 6 | N |
| 7 | Y |
| 8 | N |
| 9 | N |
| 10 | N |
2. 选择题详解
Section titled “2. 选择题详解”选择题第 1 题
Section titled “选择题第 1 题”题目问:以下叙述中正确的是哪一个?
答案:C
正确说法:
静态 static 类别变量的生存期贯穿于整个程序的运行期间。详细解析:
static 修饰的变量属于静态存储期,它不是每次进入函数才临时创建、离开函数就销毁,而是在程序运行期间一直存在。
例如:
void count(void){ static int n = 0; n++; printf("%d\n", n);}多次调用 count() 时,n 的值会保留下来。
其他选项的问题:
- 全局变量的作用域不一定比所有局部变量都大,因为全局变量也可能被限制在某个文件内。
- 函数形参属于局部变量,不是全局变量。
- 未初始化的
auto局部变量值不确定,但未初始化的static变量会自动初始化为0。
容易踩坑点:
- 把“作用域”和“生存期”混在一起。作用域说的是哪里能用这个名字,生存期说的是变量在内存里活多久。
给新手的建议:
static变量的关键词:只初始化一次、值会保留、生存期贯穿程序运行期间。
选择题第 2 题
Section titled “选择题第 2 题”题目问:用户定义的函数不可以调用的函数是哪一个?
答案:B
正确答案:
main 函数详细解析:
传统 C 语言考试中,main 被认为是程序入口,由系统调用,普通用户函数不应主动调用 main。
用户自定义函数可以调用:
- 返回值不是
int的函数。 - 本文件外已经正确声明的函数。
- 写在本函数下面、但提前声明过的函数。
例如下面这样是可以的:
int add(int a, int b);
int main(void){ printf("%d\n", add(1, 2)); return 0;}
int add(int a, int b){ return a + b;}容易踩坑点:
- 以为函数必须写在调用位置前面。其实提前写函数声明就可以。
给新手的建议:
main当作程序入口记,普通函数之间互相调用时,要注意函数声明。
选择题第 3 题
Section titled “选择题第 3 题”题目问:下列字符数组长度为 5 的是哪一个?
答案:A
正确选项:
char a[] = {'h', 'a', 'b', 'c', 'd'};详细解析:
这个初始化列表里正好有 5 个字符:
'h' 'a' 'b' 'c' 'd'没有写数组长度时,编译器会根据初始化元素个数推断:
数组长度 = 5其他选项的问题:
char c[10] = ...长度已经明确是10。- 带
'\0'的字符列表如果总共 6 个元素,长度就是 6。 char d[6] = ...长度已经明确是6。
容易踩坑点:
- 字符数组不一定是字符串。只有以
'\0'结尾,并按字符串方式使用时,才是常见意义上的 C 字符串。
给新手的建议:
- 问“数组长度”时,先看
[]里有没有数字;没有数字再数初始化列表里的元素个数。
选择题第 4 题
Section titled “选择题第 4 题”题目整理:
int a[][3] = {1, 2, 3, 4, 5, 6, 7};问:数组 a 第一维的大小是多少?
答案:C
答案:
3详细解析:
第二维是 3,表示每一行有 3 个元素。
初始化列表一共有 7 个元素:
1 2 3 4 5 6 7按每行 3 个元素分组:
第 1 行:1 2 3第 2 行:4 5 6第 3 行:7 0 0所以第一维需要 3 行。
容易踩坑点:
- 以为 7 个元素除以 3 得到 2,就选 2。这里最后剩下的 1 个元素也要单独占一行。
给新手的建议:
- 二维数组推断行数时,用“向上取整”的感觉理解:元素没有填满一整行,也要占一行。
选择题第 5 题
Section titled “选择题第 5 题”题目整理:
char c[] = "abc";int i = 0;
do ;while (c[i++] != '\0');
printf("%d", i - 1);答案:D
输出:
3详细解析:
字符串 "abc" 在数组中实际存储为:
c[0] = 'a'c[1] = 'b'c[2] = 'c'c[3] = '\0'循环条件每判断一次,都会执行 i++:
| 判断时访问 | 字符 | 判断后 i |
|---|---|---|
c[0] | 'a' | 1 |
c[1] | 'b' | 2 |
c[2] | 'c' | 3 |
c[3] | '\0' | 4 |
遇到 '\0' 后循环结束,此时 i = 4,所以:
i - 1 = 3容易踩坑点:
do ; while (...)的循环体是空语句,但条件里的i++仍然会执行。i++是先用当前值,再自增。
给新手的建议:
- 遇到
i++和字符串结束符题,最好画表,一步一步写出每次访问了哪个字符。
选择题第 6 题
Section titled “选择题第 6 题”题目整理:
int m = 13;
int fun2(int x, int y){ int m = 3; return x * y - m;}
int main(void){ int a = 7, b = 5; printf("%d\n", fun2(a, b / m)); return 0;}答案:D
输出:
-3详细解析:
调用函数前先计算实参:
fun2(a, b / m)这里的 m 用的是全局变量:
m = 13所以:
b / m = 5 / 13 = 0整数除法会舍去小数部分。
进入函数后:
x = 7y = 0函数内部局部变量 m = 3返回值:
x * y - m = 7 * 0 - 3 = -3容易踩坑点:
- 把函数内部的局部变量
m = 3拿去算实参b / m。 - 忘记整数除法
5 / 13的结果是0。
给新手的建议:
- 函数调用题先算实参,再进入函数。局部变量只在它所在的函数内部有效。
选择题第 7 题
Section titled “选择题第 7 题”题目问:以下定义语句中,错误的是哪一个?
答案:D
错误选项:
int n = 5, a[n];详细解析:
按本卷传统 C 教材口径,数组长度应该是常量表达式,不能用普通变量 n 来定义数组长度。
所以考试中判为错误。
其他选项说明:
int a = {1, 2};这种写法本身也不推荐,严格说标量初始化列表不能有多余初始化项,实际编译器通常会报警或报错。原卷红色答案给的是 D,所以这里按原卷答案整理。
补充提醒:
现代 C99 支持变长数组,部分编译器可能允许:
int n = 5;int a[n];但基础考试题通常不按这个口径出答案。
容易踩坑点:
- 把“考试口径”和“某些编译器能跑”混在一起。
给新手的建议:
- 做这类传统卷子时,数组长度先按“固定常量”理解,例如
int a[5];。
选择题第 8 题
Section titled “选择题第 8 题”题目问:以下不能对二维数组 a 进行正确初始化的语句是哪一个?
答案:B
错误选项:
int a[2][3] = {{1, 2}, {3, 4}, {5, 6}};详细解析:
a[2][3] 表示数组有 2 行、每行 3 列。
但是初始化列表写了 3 行:
{1, 2}{3, 4}{5, 6}行数超过了定义的 2 行,所以错误。
其他选项:
int a[2][3] = {0};合法,全部元素初始化为 0。
int a[][3] = {{1, 2}, {0}};合法,第一维可以由初始化列表推断。
int a[][3] = {1, 2, 3, 4, 5, 6};合法,按每 3 个元素一行排列。
容易踩坑点:
- 初值少可以补
0,初值行数超过定义行数不行。
给新手的建议:
- 二维数组初始化先看定义的行数,再数初始化列表给了几行。
选择题第 9 题
Section titled “选择题第 9 题”题目问:s1 和 s2 已分别指向两个字符串,若要求当 s1 所指字符串大于 s2 所指字符串时执行语句 S;,正确写法是哪一个?
答案:D
正确写法:
if (strcmp(s1, s2) > 0) S;详细解析:
比较字符串大小不能直接写:
if (s1 > s2)这样比较的是两个指针地址,不是字符串内容。
应该使用 strcmp:
strcmp(s1, s2) > 0 表示 s1 字符串大于 s2 字符串strcmp(s1, s2) = 0 表示两个字符串相等strcmp(s1, s2) < 0 表示 s1 字符串小于 s2 字符串容易踩坑点:
- 把字符串指针的地址比较当成字符串内容比较。
strcmp(s1, s2)非 0 只能表示不相等,不一定表示s1 > s2。
给新手的建议:
- 字符串比较记住三件套:
strcmp比较,strcpy复制,strcat连接。
选择题第 10 题
Section titled “选择题第 10 题”题目问:以下程序段中,不能正确赋字符串的是哪一个?
答案:B
错误选项:
char s[10];s = "abcdefg";详细解析:
数组名 s 不能作为普通变量被整体赋值。
也就是说,数组定义以后不能这样改指向:
s = "abcdefg"; // 错误正确做法可以是定义时初始化:
char s[10] = "abcdefg";或者定义后用 strcpy:
char s[10];strcpy(s, "abcdefg");容易踩坑点:
- 字符数组定义时可以用
=初始化,但定义以后不能再用=给整个数组赋字符串。
给新手的建议:
- 字符串复制用
strcpy,不是数组名直接赋值。
选择题第 11 题
Section titled “选择题第 11 题”题目问:以下能对二维数组 a 进行正确初始化的语句是哪一个?
答案:D
正确选项:
int a[][3] = {1, 2, 3, 4, 5};详细解析:
二维数组初始化时,第一维可以省略,第二维必须写。
这里第二维是 3,有 5 个初值,所以会排成:
1 2 34 5 0第一维由编译器推断为 2。
其他选项的问题:
int a[2][] = ...省略了第二维,错误。int a[][] = ...两维都省略,错误。int a[2][3] = {1,2,3,4,5,6,7};给了 7 个元素,但数组只能放 6 个,错误。
容易踩坑点:
- 二维数组第一维可以省,第二维不能省。
给新手的建议:
- 看到
a[][3]不要怕,它是合法写法;看到a[2][]就要警惕,第二维缺了。
选择题第 12 题
Section titled “选择题第 12 题”题目整理:
char a[10];问:以下语句中,不能从键盘上给 a 数组的所有元素输入值的是哪一个?
答案:D
错误选项:
a = getchar();详细解析:
a 是数组名,不能被整体赋值。
而且 getchar() 一次只读一个字符,它返回的是一个字符值,不是一整个数组。
其他选项的理解:
gets(a);旧教材中认为可读入一行字符串到字符数组。现代 C 不推荐使用。
scanf("%s", a);可以读入一个不含空白字符的字符串。
for (i = 0; i < 10; i++) a[i] = getchar();可以逐个字符读入数组元素。
容易踩坑点:
- 数组名不能放在赋值号左边表示“整个数组接收一个值”。
给新手的建议:
- 想给数组逐个元素赋值,用循环和下标;想给字符数组读字符串,用
scanf("%s", a)或更安全的fgets。
选择题第 13 题
Section titled “选择题第 13 题”题目问:数组名作为实参传递给函数时,数组名被处理为什么?
答案:B
答案:
该数组的首地址详细解析:
数组作为函数实参时,传递的是数组首元素的地址。
例如:
void print_arr(int a[], int n){ printf("%d\n", a[0]);}
int main(void){ int x[3] = {1, 2, 3}; print_arr(x, 3); return 0;}调用 print_arr(x, 3) 时,x 传过去的是 &x[0]。
容易踩坑点:
- 以为数组实参会把整个数组复制一份给函数。C 语言里不是这样。
给新手的建议:
- 数组传参时,函数内部改数组元素,外面的数组也会变,因为传的是地址。
选择题第 14 题
Section titled “选择题第 14 题”题目整理:
fff(float x){ return 5;}问:以下函数的类型是什么?
答案:B
答案:
int 类型详细解析:
这题按传统 C 教材口径:函数名前没有写返回类型时,默认返回类型是 int。
所以:
fff(float x)按老式 C 语境理解为:
int fff(float x)现代写法请明确写:
int fff(float x){ return 5;}容易踩坑点:
- 以为函数类型由形参
float x决定。 - 以为函数类型由
return 5;自动推断。C 语言不会这样推断函数返回类型。
给新手的建议:
- 函数返回类型看函数名前面的类型。实际写代码不要省略。
选择题第 15 题
Section titled “选择题第 15 题”题目整理:
int func(int a, int b){ int c; c = a + b; return c;}
int main(void){ int x = 6, y = 7, z = 8, r; r = func((x--, y++, x + y), z--); printf("%d\n", r); return 0;}答案:B
输出:
21详细解析:
第一个实参是逗号表达式:
(x--, y++, x + y)逗号表达式从左到右依次执行,最终值是最后一个表达式的值。
初始:
x = 6y = 7z = 8执行过程:
x-- 使用后 x 变为 5y++ 使用后 y 变为 8x + y 此时是 5 + 8 = 13所以第一个实参是 13。
第二个实参:
z--作为实参时取当前值 8,之后 z 自减。
函数返回:
13 + 8 = 21容易踩坑点:
- 忘记逗号表达式的值是最后一个表达式的值。
- 把
x--、y++的“先用后变”搞混。
给新手的建议:
- 逗号表达式题不要口算,按顺序写变量变化表最稳。
选择题第 16 题
Section titled “选择题第 16 题”题目整理:
char array[] = "China";问:数组 array 所占的空间是多少?
答案:B
答案:
6 个字节详细解析:
字符串 "China" 有 5 个有效字符:
C h i n aC 字符串结尾还会自动加一个 '\0':
C h i n a \0所以字符数组一共占:
6 个 char = 6 个字节容易踩坑点:
- 忘记字符串结尾隐藏的
'\0'。
给新手的建议:
- 双引号字符串占用空间 = 有效字符数 + 1。
选择题第 17 题
Section titled “选择题第 17 题”题目整理:
char a[20];问:正确的输入语句是哪一个?
答案:D
正确选项:
gets(a);详细解析:
按旧教材口径,gets(a) 可以把键盘输入的一行字符串读入字符数组 a。
其他选项的问题:
scanf("%s", &a);a 本身作为表达式时已经代表数组首地址,通常应写 scanf("%s", a);,不写 &a。
gets(a[20]);a[20] 是第 21 个元素,已经越界,而且它是一个字符,不是字符数组首地址。
scanf("%s", a[]);语法错误。
补充提醒:
现代 C 不推荐使用 gets,真实代码中更建议写:
fgets(a, sizeof a, stdin);容易踩坑点:
a和a[20]完全不是一回事。a是数组首地址,a[20]是一个越界元素。
给新手的建议:
- 读字符串时,数组名直接传进去,不要写成某个下标。
选择题第 18 题
Section titled “选择题第 18 题”题目整理:
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]越界,因为长度为 10 的数组合法下标是0 ~ 9。a(1)不是数组下标写法。
容易踩坑点:
- 长度为 10,不代表可以访问
a[10]。
给新手的建议:
- 数组下标从
0开始,长度为n的数组最后一个元素是a[n - 1]。
选择题第 19 题
Section titled “选择题第 19 题”题目整理:
exce((v1, v2), (v3, v4, v5), v6);问:函数调用语句中实参的个数是多少?
答案:A
答案:
3详细解析:
最外层函数调用是:
exce(参数1, 参数2, 参数3)三个实参分别是:
(v1, v2)(v3, v4, v5)v6前两个括号内部的逗号属于逗号表达式,不是函数实参分隔符。
容易踩坑点:
- 看到全部逗号就数成 6 个实参。
给新手的建议:
- 数函数实参时,只数“最外层括号里的逗号”。
选择题第 20 题
Section titled “选择题第 20 题”题目整理:
int x[10] = {0, 2, 4};假定 int 类型变量占用 2 个字节,问数组 x 在内存中所占字节数是多少?
答案:D
答案:
20详细解析:
数组长度是 10:
x[0] ~ x[9]每个 int 占 2 个字节,所以总字节数:
10 * 2 = 20初始化只写了 3 个值,不影响数组整体大小。
容易踩坑点:
- 以为只初始化 3 个元素,所以只占
3 * 2 = 6字节。
给新手的建议:
- 数组占空间看“定义的长度”,不是看“初始化时写了几个值”。
选择题第 21 题
Section titled “选择题第 21 题”题目问:以下能对二维数组 a 进行正确初始化的语句是哪一个?
答案:C
正确选项:
int a[][3] = {{1, 2, 3}, {4, 5, 6}};详细解析:
第一维省略,第二维写了 3,合法。
初始化内容刚好两行:
1 2 34 5 6所以编译器可以推出第一维是 2。
其他选项的问题:
int a[2][] = ...第二维省略,错误。- 初始化列表中花括号配对或元素数量不符合语法。
- 空的
{}在传统 C 初始化中不是标准写法。
容易踩坑点:
- 第一维可以让编译器推断,第二维必须告诉编译器。
给新手的建议:
- 二维数组传参和定义时,第二维尤其重要,因为编译器要靠它计算地址。
选择题第 22 题
Section titled “选择题第 22 题”题目整理:
int a[10];问:给数组 a 的所有元素分别赋值为 1、2、3、... 的语句是哪一个?
答案:C
正确写法:
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刚好给 10 个元素赋值。
容易踩坑点:
- 写成
a[i] = i会跳过a[0],最后访问a[10]越界。
给新手的建议:
- 看到数组长度为 10,就在脑子里默念:合法下标是
0到9。
选择题第 23 题
Section titled “选择题第 23 题”题目整理:
int a[3][4] = {0};问:下面正确的叙述是哪一个?
答案:D
正确叙述:
数组 a 中每个元素均可得到初值 0。详细解析:
= {0} 是数组全 0 初始化的常见写法。
二维数组 a[3][4] 可以理解为:
0 0 0 00 0 0 00 0 0 0题目 OCR 里可能把 {0} 识别成了 (0),这里按原卷红色答案和数组初始化语义整理为 {0}。
容易踩坑点:
- 以为只有
a[0][0]是 0,其余没有初值。
给新手的建议:
int a[3][4] = {0};可以作为“二维数组全部清零”的模板记。
选择题第 24 题
Section titled “选择题第 24 题”题目整理:
static char str[10] = "China";问:数组元素个数是多少?
答案:D
答案:
10详细解析:
数组定义中已经写明:
str[10]所以数组元素个数就是 10。
字符串 "China" 本身占 6 个位置:
C h i n a \0但数组总长度仍然是 10。
容易踩坑点:
- 把字符串有效长度
5或字符串占用空间6当成数组元素个数。
给新手的建议:
- 题目问“数组元素个数”,先看
[]里的数字。
选择题第 25 题
Section titled “选择题第 25 题”题目整理:
#include <stdio.h>
int main(void){ int a[3][3] = {{1, 2}, {3, 4}, {5, 6}}; int i, j, s = 0;
for (i = 1; i < 3; i++) for (j = 0; j <= i; j++) s += a[i][j];
printf("%d\n", s); return 0;}答案:A
输出:
18详细解析:
二维数组初始化后,没写到的元素补 0:
a[0]: 1 2 0a[1]: 3 4 0a[2]: 5 6 0循环从 i = 1 开始:
当 i = 1 时:
j = 0, 1s += a[1][0] + a[1][1] = 3 + 4当 i = 2 时:
j = 0, 1, 2s += a[2][0] + a[2][1] + a[2][2] = 5 + 6 + 0所以总和:
3 + 4 + 5 + 6 + 0 = 18容易踩坑点:
- 忘记每行第三个元素自动补
0。 j <= i包含j = i。
给新手的建议:
- 二维数组求和题先把数组表格画出来,再标出循环访问了哪些格子。
3. 填空题详解
Section titled “3. 填空题详解”填空题第 1 题
Section titled “填空题第 1 题”题目问:连接两个字符串的库函数是什么?
答案:
strcat详细解析:
strcat 用于把第二个字符串连接到第一个字符串后面:
char s[20] = "hello";strcat(s, " C");printf("%s\n", s); // hello C容易踩坑点:
strcpy是复制字符串。strcmp是比较字符串。strcat是连接字符串。
给新手的建议:
- 三个函数放在一起背:copy 复制、compare 比较、concatenate 连接。
填空题第 2 题
Section titled “填空题第 2 题”题目整理:
int a = 3, b = 4, c = 5;!(a + b) + c - 1 && b + c / 2答案:
1详细解析:
先算:
a + b = 3 + 4 = 7!(a + b) = !7 = 0再算左侧算术表达式:
0 + c - 1 = 0 + 5 - 1 = 4再算右侧:
c / 2 = 5 / 2 = 2b + c / 2 = 4 + 2 = 6最后:
4 && 6 = 1在 C 语言里,逻辑与 && 两边只要都非 0,结果就是 1。
容易踩坑点:
5 / 2是整数除法,结果是2。&&的结果不是左边或右边的数值,而是0或1。
给新手的建议:
- 逻辑表达式题先把每一边算成“0 或非 0”,再判断整体真假。
填空题第 3 题
Section titled “填空题第 3 题”题目整理:
void insert(char str[]){ int i; i = strlen(str); while (i > 0) { str[2 * i] = str[i]; str[2 * i - 1] = '*'; i--; } printf("%s\n", str);}输入:
abcd答案:
a*b*c*d详细解析:
原字符串:
a b c d \0strlen(str) 的结果是 4,然后从后往前移动字符并插入 *。
关键过程:
| i | 操作 |
|---|---|
| 4 | 把 '\0' 放到 str[8],str[7] = '*' |
| 3 | 把 'd' 放到 str[6],str[5] = '*' |
| 2 | 把 'c' 放到 str[4],str[3] = '*' |
| 1 | 把 'b' 放到 str[2],str[1] = '*' |
str[0] 原来的 'a' 不动,最终变成:
a * b * c * d \0所以输出:
a*b*c*d容易踩坑点:
- 从后往前处理是为了避免覆盖还没来得及移动的字符。
strlen不统计结尾的'\0',但这里i = 4时会把str[4]里的'\0'也移动过去。
给新手的建议:
- 字符串插入题,先画出下标和字符,尤其要画出
'\0'。
填空题第 4 题
Section titled “填空题第 4 题”题目问:杨辉三角程序运行时,输出到屏幕的结果第四行是什么?
答案:
1331详细解析:
杨辉三角前几行是:
11 11 2 11 3 3 1所以第四行是:
1 3 3 1题目答案写成连续形式就是:
1331容易踩坑点:
- 把第四行看成从第 0 行开始数。这里按题目输出行数,从第一行
1开始数。
给新手的建议:
- 杨辉三角每一行两边都是 1,中间等于上一行相邻两个数相加。
填空题第 5 题
Section titled “填空题第 5 题”题目整理:
char a, b;a = getchar();scanf("%d", &b);a = a - 'A' + '0';b = b * 2;printf("%c %c\n", a, b);输入:
B33答案:
1 B详细解析:
getchar() 先读取字符:
a = 'B'然后:
a = 'B' - 'A' + '0'因为 'B' - 'A' = 1,所以:
a = '1'scanf("%d", &b) 读入数字 33,然后:
b = 33 * 2 = 66ASCII 码中 66 对应字符 'B',所以按 %c 输出为:
B最终输出:
1 B补充提醒:
原题中 b 定义为 char,却用 %d 输入,这在真实 C 语言里不规范,严格说应该把 b 定义成 int。本题按原卷传统考试答案理解。
容易踩坑点:
%c输出的是字符,不是数字本身。- 字符也可以参与整数运算,本质用的是 ASCII 编码。
给新手的建议:
- 字符计算题先把字符换成 ASCII 数值,再看最后用
%c还是%d输出。
填空题第 6 题
Section titled “填空题第 6 题”题目要求:给函数中的各变量正确赋初值,计算级数:
S = 1 + x + x^2/2! + x^3/3! + ... + x^n/n!答案:
a = 1.0;b = 1.0;s = 1.0;详细解析:
函数核心循环:
for (i = 1; i <= n; i++){ a = a * x; b = b * i; s = s + a / b;}变量含义:
a 表示当前项的分子 x^ib 表示当前项的分母 i!s 表示累加和初始项是 1,所以:
s = 1.0为了第一次循环得到:
a = xb = 1所以循环前:
a = 1.0b = 1.0容易踩坑点:
- 忘记级数最前面已经有一个
1。 - 把
a和b初始化成0,这样后面乘法就永远是0。
给新手的建议:
- 递推类题目要先想“第一次循环希望得到什么”,再倒推初值。
填空题第 7 题
Section titled “填空题第 7 题”题目问:C 语言程序的三种基本结构是什么?
答案:
循环结构完整说法:
顺序结构、选择结构、循环结构详细解析:
三种基本结构对应程序控制流程:
- 顺序结构:一条一条按顺序执行。
- 选择结构:根据条件选择执行哪个分支,比如
if、switch。 - 循环结构:重复执行某段代码,比如
for、while、do while。
容易踩坑点:
- 把“函数结构”“数组结构”这类概念混进来。
给新手的建议:
- 以后写任何程序,本质上都是这三种结构的组合。
填空题第 8 题
Section titled “填空题第 8 题”题目整理:
struct DATE{ int year; int month; int day;};要求定义结构体变量 d,并依次赋初值 2006、10、1。
答案:
struct DATE d = {2006, 10, 1};详细解析:
结构体变量定义格式:
struct 结构体类型名 变量名 = {成员1初值, 成员2初值, 成员3初值};这里成员顺序是:
year -> month -> day所以初始化顺序也要对应:
2006 -> 10 -> 1容易踩坑点:
- 少写
struct。 - 初始化顺序和成员定义顺序对不上。
给新手的建议:
- 结构体整体初始化时,花括号里的值按成员定义顺序依次对应。
填空题第 9 题
Section titled “填空题第 9 题”题目整理:
int x;x = 3 * 4 % -5 / 6;答案:
0详细解析:
*、%、/ 优先级相同,按从左到右计算。
先算:
3 * 4 = 12再算:
12 % -5 = 2最后:
2 / 6 = 0因为是整数除法,所以结果是 0。
容易踩坑点:
- 忘记同一优先级按从左到右结合。
2 / 6在整数运算里不是小数,而是0。
给新手的建议:
- 看到
%和/混在一起,先加括号还原计算顺序:((3 * 4) % -5) / 6。
填空题第 10 题
Section titled “填空题第 10 题”题目功能:将输入的正整数按逆序输出。例如输入 135,输出 531。
答案:
n /= 10;或:
n = n / 10;详细解析:
程序核心是:
do{ s = n % 10; printf("%d", s); n /= 10;} while (n != 0);以 135 为例:
| n | n % 10 输出 | n /= 10 后 |
|---|---|---|
| 135 | 5 | 13 |
| 13 | 3 | 1 |
| 1 | 1 | 0 |
输出就是:
531容易踩坑点:
- 只写
n / 10,但没有把结果赋回给n。 - 忘记
do while至少执行一次。
给新手的建议:
- 取个位用
% 10,去掉个位用/ 10,这是数字拆位题的固定套路。
4. 判断题详解
Section titled “4. 判断题详解”判断题第 1 题
Section titled “判断题第 1 题”题目:若 a = 3, b = 2, c = 1,则关系表达式 (a > b) == c 的值为真,即为 1。
答案:Y
详细解析:
先算:
a > b -> 3 > 2 -> 1再算:
1 == c -> 1 == 1 -> 1所以表达式为真。
容易踩坑点:
- 关系表达式成立时结果是
1,不成立时结果是0。
判断题第 2 题
Section titled “判断题第 2 题”题目:C 语言所有函数都是外部函数。
答案:N
详细解析:
C 语言函数可以有外部函数,也可以用 static 修饰成内部函数。
例如:
static void helper(void){ ...}这个函数只在当前源文件内可见。
容易踩坑点:
- 看到函数默认是外部的,就误以为所有函数都只能是外部函数。
判断题第 3 题
Section titled “判断题第 3 题”题目:在程序中定义了一个结构体类型后,可以多次用它来定义具有该类型的变量。
答案:Y
详细解析:
例如:
struct DATE{ int year; int month; int day;};
struct DATE d1;struct DATE d2;定义了结构体类型以后,可以定义多个该类型变量。
容易踩坑点:
- 把“定义结构体类型”和“定义结构体变量”混为一谈。
判断题第 4 题
Section titled “判断题第 4 题”题目整理:
int i = 20;
switch (i / 10){case 2: printf("A");case 1: printf("B");}题目说输出结果为 A。
答案:N
详细解析:
i / 10 = 20 / 10 = 2所以进入 case 2,输出:
A但 case 2 后面没有 break,会继续向下执行 case 1,再输出:
B实际输出是:
AB容易踩坑点:
- 忘记
switch中没有break会继续往下执行。
判断题第 5 题
Section titled “判断题第 5 题”题目:如果函数值的类型和 return 语句中表达式的值不一致,则以函数类型为准。
答案:Y
详细解析:
例如:
int fun(void){ return 3.14;}函数返回类型是 int,所以返回值会按 int 类型处理,结果类似 3。
容易踩坑点:
- 以为
return后面是什么类型,函数就自动变成什么类型。
判断题第 6 题
Section titled “判断题第 6 题”题目整理:
int i = 10, j = 0;if (j = 0) i++;else i--;题目说执行后 i 的值为 11。
答案:N
详细解析:
这里是赋值:
j = 0不是判断相等:
j == 0赋值表达式 j = 0 的值是 0,所以 if 条件为假,执行 else:
i--;因此:
i = 9容易踩坑点:
- 把
=当成==。
给新手的建议:
- 条件判断相等一定写两个等号:
==。
判断题第 7 题
Section titled “判断题第 7 题”题目:整数 -32100 可以赋值给 int 型和 long int 型变量。
答案:Y
详细解析:
按传统教材常见环境,int 至少可以表示到 -32768,-32100 在范围内;long int 范围更大,也可以保存。
容易踩坑点:
- 只记正数范围,忘记有符号整数也有负数范围。
判断题第 8 题
Section titled “判断题第 8 题”题目整理:
#define S(a,b) t=a; a=b; b=t题目说:由于变量 t 没定义,所以此宏定义是错误的。
答案:N
详细解析:
宏定义本身只是文本替换,不要求在定义宏时就已经有变量 t。
只要在使用宏的地方有合适的 t,就可能通过编译。
例如:
int a = 1, b = 2, t;S(a, b);不过这个宏仍然不够安全,真实代码中更推荐写成带 do while 的宏或直接写函数。
容易踩坑点:
- 宏定义阶段不做完整语义检查,它更像“文本模板”。
判断题第 9 题
Section titled “判断题第 9 题”题目:C 语言的函数可以嵌套定义。
答案:N
详细解析:
C 语言可以嵌套调用函数,但不能嵌套定义函数。
可以这样调用:
printf("%d\n", fun(x));不可以这样定义:
int main(void){ int fun(void) { return 1; }}容易踩坑点:
- 把“嵌套调用”和“嵌套定义”混在一起。
判断题第 10 题
Section titled “判断题第 10 题”题目:结构体类型只有一种。
答案:N
详细解析:
程序中可以定义很多种结构体类型。
例如:
struct DATE{ int year; int month; int day;};
struct STUDENT{ char name[20]; int age;};这就是两个不同的结构体类型。
容易踩坑点:
- 把“结构体这种语法类别”误解成“只能有一个结构体类型”。
5. 改错题详解
Section titled “5. 改错题详解”改错题 1:判断素数
Section titled “改错题 1:判断素数”题目功能:判断 m 是否为素数,若是返回 1,否则返回 0。
原题主要错误点:
- 函数需要返回
1或0,返回类型不能写void。 - 判断素数时,不应该从
1开始试除,因为任何整数都能被1整除。 m % i = 0是错误写法,判断相等要写m % i == 0。- 最后应该返回标志变量
k,不是返回m。
完整修正代码:
#include <stdio.h>
int fun(int m){ int i, k = 1;
if (m <= 1) k = 0;
for (i = 2; i < m; i++) if (m % i == 0) k = 0;
return k;}
int main(void){ int m, k = 0;
for (m = 1; m < 100; m++) if (fun(m) == 1) { printf("%4d", m); k++; if (k % 5 == 0) printf("\n"); }
return 0;}过程解释:
- 先假设
m是素数,所以k = 1。 - 如果
m <= 1,它不是素数,令k = 0。 - 从
2到m - 1逐个试除。 - 只要存在某个
i能整除m,说明m不是素数,令k = 0。 - 返回
k,调用者根据1或0判断是否输出。
容易踩坑点:
=是赋值,==才是判断相等。return m;返回的是原数,不是“是否为素数”的结果。- 从
1开始试除会把所有大于 1 的数都判成非素数。
给新手的建议:
- 判断素数的基础模板先背住:小于等于 1 不是素数,从 2 开始试除。
- 后面熟练以后,可以把循环优化为
i * i <= m,但刚开始先把逻辑写对。
改错题 2:计算某年某月有几天
Section titled “改错题 2:计算某年某月有几天”题目功能:输入年份和月份,输出该月天数,注意区分闰年。
原题主要错误点:
switch(yy)错,应根据月份分支,所以应写switch(mm)。break后面缺少分号。default后面缺少冒号。prntf应写成printf。
完整修正代码:
#include <stdio.h>
int main(void){ int yy, mm, len;
printf("year,month="); scanf("%d%d", &yy, &mm);
switch (mm) { case 1: case 3: case 5: case 7: case 8: case 10: case 12: len = 31; break; case 4: case 6: case 9: case 11: len = 30; break; case 2: if ((yy % 4 == 0 && yy % 100 != 0) || yy % 400 == 0) len = 29; else len = 28; break; default: printf("input error!\n"); return 0; }
printf("The length of %d %d is %d\n", yy, mm, len); return 0;}闰年判断规则:
能被 4 整除且不能被 100 整除,或者能被 400 整除。对应代码:
(yy % 4 == 0 && yy % 100 != 0) || yy % 400 == 0容易踩坑点:
switch应该对月份mm判断,不是对年份yy判断。case和default后面是冒号:,不是分号。break;后面必须有分号。
给新手的建议:
- 月份天数题可以按三类记:31 天月份、30 天月份、2 月。
- 写
switch时,先把case、break、default的框架搭好,再填逻辑。
6. 程序设计题详解
Section titled “6. 程序设计题详解”程序设计 1:判断 100 到 199 的素数
Section titled “程序设计 1:判断 100 到 199 的素数”题目功能:判断 m 是否为素数,并输出 100 ~ 199 之间的素数。
参考答案:
#include <stdio.h>
int fun(int m){ int i, k = 1;
if (m <= 1) k = 0;
for (i = 2; i < m; i++) if (m % i == 0) k = 0;
return k;}
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("k=%d\n", k); return 0;}过程解释:
fun(m)负责判断一个数是不是素数。- 主函数从
100循环到199。 - 如果
fun(m)返回非 0,说明m是素数,就输出。 k统计已经输出了多少个素数。- 每输出 5 个素数,换一行。
关于原卷中的 wwjt():
原卷代码里带有 wwjt() 或类似函数名,一般是考试系统用于保存结果、判题或辅助测试的函数。自己本地练习时可以删掉,不影响核心算法。
容易踩坑点:
if (fun(m))等价于if (fun(m) != 0)。- 判断素数时要排除
m <= 1。 - 输出计数
k和循环变量m不是同一个含义。
给新手的建议:
- 先把
fun单独测几个数,比如2、3、4、9、11,确认判断对了,再放进大循环。
程序设计 2:求二维数组最大元素及其行列坐标
Section titled “程序设计 2:求二维数组最大元素及其行列坐标”题目功能:求 N x M 整型数组的最大元素及其所在的行坐标、列坐标。如果最大元素不唯一,选择位置在最前面的一个。
参考答案:
#include <stdio.h>
#define N 4#define M 3
int Row, Col;
int fun(int array[N][M]){ int max, i, j;
max = array[0][0]; Row = 0; Col = 0;
for (i = 0; i < N; i++) for (j = 0; j < M; j++) if (max < array[i][j]) { max = array[i][j]; Row = i; Col = j; }
return max;}
int main(void){ int a[N][M], i, j, max;
printf("input a array:\n"); for (i = 0; i < N; i++) for (j = 0; j < M; j++) scanf("%d", &a[i][j]);
for (i = 0; i < N; i++) { for (j = 0; j < M; j++) printf("%4d", a[i][j]); printf("\n"); }
max = fun(a); printf("max=%d,row=%d,col=%d\n", max, Row, Col);
return 0;}示例数组:
1 2 34 15 612 18 910 11 2下标表:
| 行 i | 列 0 | 列 1 | 列 2 |
|---|---|---|---|
| 0 | 1 | 2 | 3 |
| 1 | 4 | 15 | 6 |
| 2 | 12 | 18 | 9 |
| 3 | 10 | 11 | 2 |
最大值是:
18它的位置是:
Row = 2Col = 1为什么相等时不更新:
题目要求“如果最大元素不唯一,选择位置在最前面的一个”。所以判断条件要写:
if (max < array[i][j])不能写:
if (max <= array[i][j])如果写 <=,遇到相同最大值时会更新到后面的位置,就不符合“最前面”的要求。
容易踩坑点:
- 二维数组下标从
0开始,所以第三行第二列的位置是Row = 2, Col = 1。 scanf读数组元素时要写&a[i][j]。- 函数形参要写第二维:
int array[N][M],不能只写成int array[][]。 - 找最大值时,应先用
array[0][0]初始化max,不要随便写max = 0,因为数组中可能全是负数。
给新手的建议:
- 二维数组题先画成表格,然后在表格旁边标行下标和列下标。
- 找最大值模板可以固定记:先假设第一个元素最大,再从头到尾扫描,遇到更大的就更新。
7. 本卷复盘建议
Section titled “7. 本卷复盘建议”这套卷的高频坑很集中,建议你复盘时重点抓住下面几条:
- 数组下标永远从
0开始,长度为n的数组最后一个元素是下标n - 1。 - 字符串比普通字符数组多一个隐藏的
'\0',算空间时一定要加 1。 - 二维数组第一维可以省略,第二维不能省略。
- 数组名传参时会被当作首地址,不会复制整个数组。
- 判断相等用
==,赋值用=。 switch里没有break会继续向下执行。strcmp比较字符串内容,s1 > s2比较的是地址。- 老教材中的
gets、默认int、void main等写法,考试能见到,但现代 C 里不推荐照着写。
如果只挑三类题反复练,这套卷最值得练的是:
- 字符串和字符数组题。
- 二维数组初始化和下标题。
- 素数、闰年、最大值这类小程序设计题。