跳转到内容

exam-84 试卷整理与详解

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

/Users/wff/Downloads/84.pdf

这套题继续集中考 C 语言基础:变量存储类别、函数调用、数组初始化、字符串、二维数组、结构体、宏、switch、素数判断、闰年判断和二维数组最大值。

PDF 的 OCR 对代码符号有一些误差,比如 '\0'==[]{}printfstrcpystrcat 等容易识别错。我整理时按原卷红色答案和 C 语言语法做了校对。

特别提醒:

  • 本卷多处是传统 C 教材/考试口径,比如函数没有写返回类型时默认 int。现代 C 里不要这样写,实际代码要明确写返回类型。
  • gets() 在旧教材里常出现,但现代 C 已经不建议使用,真实写程序优先用 fgets()
  • int n = 5, a[n]; 这类变长数组在部分现代编译器中可能支持,但本卷按传统基础考试口径判错。
  • 二维数组题最重要的口诀是:第一维可以省略,第二维不能省略。
  • 字符串题一定记住结尾隐藏的 '\0',它也要占一个数组位置。
题号答案
1C
2B
3A
4C
5D
6D
7D
8B
9D
10B
11D
12D
13B
14B
15B
16B
17D
18A
19A
20D
21C
22C
23D
24D
25A
题号答案
1strcat
21
3a*b*c*d
41331
51 B
6a=1.0;b=1.0;s=1.0;
7循环重复
8struct DATE d={2006,10,1};
90
10n/=10n=n/10
题号答案
1Y
2N
3Y
4N
5Y
6N
7Y
8N
9N
10N

题目问:以下叙述中正确的是哪一个?

答案:C

正确说法:

静态 static 类别变量的生存期贯穿于整个程序的运行期间。

详细解析:

static 修饰的变量属于静态存储期,它不是每次进入函数才临时创建、离开函数就销毁,而是在程序运行期间一直存在。

例如:

void count(void)
{
static int n = 0;
n++;
printf("%d\n", n);
}

多次调用 count() 时,n 的值会保留下来。

其他选项的问题:

  • 全局变量的作用域不一定比所有局部变量都大,因为全局变量也可能被限制在某个文件内。
  • 函数形参属于局部变量,不是全局变量。
  • 未初始化的 auto 局部变量值不确定,但未初始化的 static 变量会自动初始化为 0

容易踩坑点:

  • 把“作用域”和“生存期”混在一起。作用域说的是哪里能用这个名字,生存期说的是变量在内存里活多久。

给新手的建议:

  • static 变量的关键词:只初始化一次、值会保留、生存期贯穿程序运行期间。

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

答案: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 当作程序入口记,普通函数之间互相调用时,要注意函数声明。

题目问:下列字符数组长度为 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 字符串。

给新手的建议:

  • 问“数组长度”时,先看 [] 里有没有数字;没有数字再数初始化列表里的元素个数。

题目整理:

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 个元素也要单独占一行。

给新手的建议:

  • 二维数组推断行数时,用“向上取整”的感觉理解:元素没有填满一整行,也要占一行。

题目整理:

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++ 和字符串结束符题,最好画表,一步一步写出每次访问了哪个字符。

题目整理:

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 = 7
y = 0
函数内部局部变量 m = 3

返回值:

x * y - m = 7 * 0 - 3 = -3

容易踩坑点:

  • 把函数内部的局部变量 m = 3 拿去算实参 b / m
  • 忘记整数除法 5 / 13 的结果是 0

给新手的建议:

  • 函数调用题先算实参,再进入函数。局部变量只在它所在的函数内部有效。

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

答案:D

错误选项:

int n = 5, a[n];

详细解析:

按本卷传统 C 教材口径,数组长度应该是常量表达式,不能用普通变量 n 来定义数组长度。

所以考试中判为错误。

其他选项说明:

int a = {1, 2};

这种写法本身也不推荐,严格说标量初始化列表不能有多余初始化项,实际编译器通常会报警或报错。原卷红色答案给的是 D,所以这里按原卷答案整理。

补充提醒:

现代 C99 支持变长数组,部分编译器可能允许:

int n = 5;
int a[n];

但基础考试题通常不按这个口径出答案。

容易踩坑点:

  • 把“考试口径”和“某些编译器能跑”混在一起。

给新手的建议:

  • 做这类传统卷子时,数组长度先按“固定常量”理解,例如 int a[5];

题目问:以下不能对二维数组 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,初值行数超过定义行数不行。

给新手的建议:

  • 二维数组初始化先看定义的行数,再数初始化列表给了几行。

题目问:s1s2 已分别指向两个字符串,若要求当 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 连接。

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

答案:B

错误选项:

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

详细解析:

数组名 s 不能作为普通变量被整体赋值。

也就是说,数组定义以后不能这样改指向:

s = "abcdefg"; // 错误

正确做法可以是定义时初始化:

char s[10] = "abcdefg";

或者定义后用 strcpy

char s[10];
strcpy(s, "abcdefg");

容易踩坑点:

  • 字符数组定义时可以用 = 初始化,但定义以后不能再用 = 给整个数组赋字符串。

给新手的建议:

  • 字符串复制用 strcpy,不是数组名直接赋值。

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

答案:D

正确选项:

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

详细解析:

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

这里第二维是 3,有 5 个初值,所以会排成:

1 2 3
4 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][] 就要警惕,第二维缺了。

题目整理:

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

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

答案: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 语言里不是这样。

给新手的建议:

  • 数组传参时,函数内部改数组元素,外面的数组也会变,因为传的是地址。

题目整理:

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 语言不会这样推断函数返回类型。

给新手的建议:

  • 函数返回类型看函数名前面的类型。实际写代码不要省略。

题目整理:

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 = 6
y = 7
z = 8

执行过程:

x-- 使用后 x 变为 5
y++ 使用后 y 变为 8
x + y 此时是 5 + 8 = 13

所以第一个实参是 13

第二个实参:

z--

作为实参时取当前值 8,之后 z 自减。

函数返回:

13 + 8 = 21

容易踩坑点:

  • 忘记逗号表达式的值是最后一个表达式的值。
  • x--y++ 的“先用后变”搞混。

给新手的建议:

  • 逗号表达式题不要口算,按顺序写变量变化表最稳。

题目整理:

char array[] = "China";

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

答案:B

答案:

6 个字节

详细解析:

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

C h i n a

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

C h i n a \0

所以字符数组一共占:

6 个 char = 6 个字节

容易踩坑点:

  • 忘记字符串结尾隐藏的 '\0'

给新手的建议:

  • 双引号字符串占用空间 = 有效字符数 + 1。

题目整理:

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);

容易踩坑点:

  • aa[20] 完全不是一回事。a 是数组首地址,a[20] 是一个越界元素。

给新手的建议:

  • 读字符串时,数组名直接传进去,不要写成某个下标。

题目整理:

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]

题目整理:

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

问:函数调用语句中实参的个数是多少?

答案:A

答案:

3

详细解析:

最外层函数调用是:

exce(参数1, 参数2, 参数3)

三个实参分别是:

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

前两个括号内部的逗号属于逗号表达式,不是函数实参分隔符。

容易踩坑点:

  • 看到全部逗号就数成 6 个实参。

给新手的建议:

  • 数函数实参时,只数“最外层括号里的逗号”。

题目整理:

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 字节。

给新手的建议:

  • 数组占空间看“定义的长度”,不是看“初始化时写了几个值”。

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

答案:C

正确选项:

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

详细解析:

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

初始化内容刚好两行:

1 2 3
4 5 6

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

其他选项的问题:

  • int a[2][] = ... 第二维省略,错误。
  • 初始化列表中花括号配对或元素数量不符合语法。
  • 空的 {} 在传统 C 初始化中不是标准写法。

容易踩坑点:

  • 第一维可以让编译器推断,第二维必须告诉编译器。

给新手的建议:

  • 二维数组传参和定义时,第二维尤其重要,因为编译器要靠它计算地址。

题目整理:

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,就在脑子里默念:合法下标是 09

题目整理:

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

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

答案:D

正确叙述:

数组 a 中每个元素均可得到初值 0。

详细解析:

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

二维数组 a[3][4] 可以理解为:

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

题目 OCR 里可能把 {0} 识别成了 (0),这里按原卷红色答案和数组初始化语义整理为 {0}

容易踩坑点:

  • 以为只有 a[0][0] 是 0,其余没有初值。

给新手的建议:

  • int a[3][4] = {0}; 可以作为“二维数组全部清零”的模板记。

题目整理:

static char str[10] = "China";

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

答案:D

答案:

10

详细解析:

数组定义中已经写明:

str[10]

所以数组元素个数就是 10

字符串 "China" 本身占 6 个位置:

C h i n a \0

但数组总长度仍然是 10。

容易踩坑点:

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

给新手的建议:

  • 题目问“数组元素个数”,先看 [] 里的数字。

题目整理:

#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 0
a[1]: 3 4 0
a[2]: 5 6 0

循环从 i = 1 开始:

i = 1 时:

j = 0, 1
s += a[1][0] + a[1][1] = 3 + 4

i = 2 时:

j = 0, 1, 2
s += a[2][0] + a[2][1] + a[2][2] = 5 + 6 + 0

所以总和:

3 + 4 + 5 + 6 + 0 = 18

容易踩坑点:

  • 忘记每行第三个元素自动补 0
  • j <= i 包含 j = i

给新手的建议:

  • 二维数组求和题先把数组表格画出来,再标出循环访问了哪些格子。

题目问:连接两个字符串的库函数是什么?

答案:

strcat

详细解析:

strcat 用于把第二个字符串连接到第一个字符串后面:

char s[20] = "hello";
strcat(s, " C");
printf("%s\n", s); // hello C

容易踩坑点:

  • strcpy 是复制字符串。
  • strcmp 是比较字符串。
  • strcat 是连接字符串。

给新手的建议:

  • 三个函数放在一起背:copy 复制、compare 比较、concatenate 连接。

题目整理:

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 = 2
b + c / 2 = 4 + 2 = 6

最后:

4 && 6 = 1

在 C 语言里,逻辑与 && 两边只要都非 0,结果就是 1

容易踩坑点:

  • 5 / 2 是整数除法,结果是 2
  • && 的结果不是左边或右边的数值,而是 01

给新手的建议:

  • 逻辑表达式题先把每一边算成“0 或非 0”,再判断整体真假。

题目整理:

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 \0

strlen(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'

题目问:杨辉三角程序运行时,输出到屏幕的结果第四行是什么?

答案:

1331

详细解析:

杨辉三角前几行是:

1
1 1
1 2 1
1 3 3 1

所以第四行是:

1 3 3 1

题目答案写成连续形式就是:

1331

容易踩坑点:

  • 把第四行看成从第 0 行开始数。这里按题目输出行数,从第一行 1 开始数。

给新手的建议:

  • 杨辉三角每一行两边都是 1,中间等于上一行相邻两个数相加。

题目整理:

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 = 66

ASCII 码中 66 对应字符 'B',所以按 %c 输出为:

B

最终输出:

1 B

补充提醒:

原题中 b 定义为 char,却用 %d 输入,这在真实 C 语言里不规范,严格说应该把 b 定义成 int。本题按原卷传统考试答案理解。

容易踩坑点:

  • %c 输出的是字符,不是数字本身。
  • 字符也可以参与整数运算,本质用的是 ASCII 编码。

给新手的建议:

  • 字符计算题先把字符换成 ASCII 数值,再看最后用 %c 还是 %d 输出。

题目要求:给函数中的各变量正确赋初值,计算级数:

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^i
b 表示当前项的分母 i!
s 表示累加和

初始项是 1,所以:

s = 1.0

为了第一次循环得到:

a = x
b = 1

所以循环前:

a = 1.0
b = 1.0

容易踩坑点:

  • 忘记级数最前面已经有一个 1
  • ab 初始化成 0,这样后面乘法就永远是 0

给新手的建议:

  • 递推类题目要先想“第一次循环希望得到什么”,再倒推初值。

题目问:C 语言程序的三种基本结构是什么?

答案:

循环结构

完整说法:

顺序结构、选择结构、循环结构

详细解析:

三种基本结构对应程序控制流程:

  • 顺序结构:一条一条按顺序执行。
  • 选择结构:根据条件选择执行哪个分支,比如 ifswitch
  • 循环结构:重复执行某段代码,比如 forwhiledo while

容易踩坑点:

  • 把“函数结构”“数组结构”这类概念混进来。

给新手的建议:

  • 以后写任何程序,本质上都是这三种结构的组合。

题目整理:

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
  • 初始化顺序和成员定义顺序对不上。

给新手的建议:

  • 结构体整体初始化时,花括号里的值按成员定义顺序依次对应。

题目整理:

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

题目功能:将输入的正整数按逆序输出。例如输入 135,输出 531

答案:

n /= 10;

或:

n = n / 10;

详细解析:

程序核心是:

do
{
s = n % 10;
printf("%d", s);
n /= 10;
} while (n != 0);

135 为例:

nn % 10 输出n /= 10
135513
1331
110

输出就是:

531

容易踩坑点:

  • 只写 n / 10,但没有把结果赋回给 n
  • 忘记 do while 至少执行一次。

给新手的建议:

  • 取个位用 % 10,去掉个位用 / 10,这是数字拆位题的固定套路。

题目:若 a = 3, b = 2, c = 1,则关系表达式 (a > b) == c 的值为真,即为 1

答案:Y

详细解析:

先算:

a > b -> 3 > 2 -> 1

再算:

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

所以表达式为真。

容易踩坑点:

  • 关系表达式成立时结果是 1,不成立时结果是 0

题目:C 语言所有函数都是外部函数。

答案:N

详细解析:

C 语言函数可以有外部函数,也可以用 static 修饰成内部函数。

例如:

static void helper(void)
{
...
}

这个函数只在当前源文件内可见。

容易踩坑点:

  • 看到函数默认是外部的,就误以为所有函数都只能是外部函数。

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

答案:Y

详细解析:

例如:

struct DATE
{
int year;
int month;
int day;
};
struct DATE d1;
struct DATE d2;

定义了结构体类型以后,可以定义多个该类型变量。

容易踩坑点:

  • 把“定义结构体类型”和“定义结构体变量”混为一谈。

题目整理:

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 会继续往下执行。

题目:如果函数值的类型和 return 语句中表达式的值不一致,则以函数类型为准。

答案:Y

详细解析:

例如:

int fun(void)
{
return 3.14;
}

函数返回类型是 int,所以返回值会按 int 类型处理,结果类似 3

容易踩坑点:

  • 以为 return 后面是什么类型,函数就自动变成什么类型。

题目整理:

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

容易踩坑点:

  • = 当成 ==

给新手的建议:

  • 条件判断相等一定写两个等号:==

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

答案:Y

详细解析:

按传统教材常见环境,int 至少可以表示到 -32768-32100 在范围内;long int 范围更大,也可以保存。

容易踩坑点:

  • 只记正数范围,忘记有符号整数也有负数范围。

题目整理:

#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 的宏或直接写函数。

容易踩坑点:

  • 宏定义阶段不做完整语义检查,它更像“文本模板”。

题目:C 语言的函数可以嵌套定义。

答案:N

详细解析:

C 语言可以嵌套调用函数,但不能嵌套定义函数。

可以这样调用:

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

不可以这样定义:

int main(void)
{
int fun(void)
{
return 1;
}
}

容易踩坑点:

  • 把“嵌套调用”和“嵌套定义”混在一起。

题目:结构体类型只有一种。

答案:N

详细解析:

程序中可以定义很多种结构体类型。

例如:

struct DATE
{
int year;
int month;
int day;
};
struct STUDENT
{
char name[20];
int age;
};

这就是两个不同的结构体类型。

容易踩坑点:

  • 把“结构体这种语法类别”误解成“只能有一个结构体类型”。

题目功能:判断 m 是否为素数,若是返回 1,否则返回 0

原题主要错误点:

  • 函数需要返回 10,返回类型不能写 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
  • 2m - 1 逐个试除。
  • 只要存在某个 i 能整除 m,说明 m 不是素数,令 k = 0
  • 返回 k,调用者根据 10 判断是否输出。

容易踩坑点:

  • = 是赋值,== 才是判断相等。
  • return m; 返回的是原数,不是“是否为素数”的结果。
  • 1 开始试除会把所有大于 1 的数都判成非素数。

给新手的建议:

  • 判断素数的基础模板先背住:小于等于 1 不是素数,从 2 开始试除。
  • 后面熟练以后,可以把循环优化为 i * i <= m,但刚开始先把逻辑写对。

题目功能:输入年份和月份,输出该月天数,注意区分闰年。

原题主要错误点:

  • 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 判断。
  • casedefault 后面是冒号 :,不是分号。
  • break; 后面必须有分号。

给新手的建议:

  • 月份天数题可以按三类记:31 天月份、30 天月份、2 月。
  • switch 时,先把 casebreakdefault 的框架搭好,再填逻辑。

程序设计 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;
}

过程解释:

  1. fun(m) 负责判断一个数是不是素数。
  2. 主函数从 100 循环到 199
  3. 如果 fun(m) 返回非 0,说明 m 是素数,就输出。
  4. k 统计已经输出了多少个素数。
  5. 每输出 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 3
4 15 6
12 18 9
10 11 2

下标表:

行 i列 0列 1列 2
0123
14156
212189
310112

最大值是:

18

它的位置是:

Row = 2
Col = 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,因为数组中可能全是负数。

给新手的建议:

  • 二维数组题先画成表格,然后在表格旁边标行下标和列下标。
  • 找最大值模板可以固定记:先假设第一个元素最大,再从头到尾扫描,遇到更大的就更新。

这套卷的高频坑很集中,建议你复盘时重点抓住下面几条:

  • 数组下标永远从 0 开始,长度为 n 的数组最后一个元素是下标 n - 1
  • 字符串比普通字符数组多一个隐藏的 '\0',算空间时一定要加 1。
  • 二维数组第一维可以省略,第二维不能省略。
  • 数组名传参时会被当作首地址,不会复制整个数组。
  • 判断相等用 ==,赋值用 =
  • switch 里没有 break 会继续向下执行。
  • strcmp 比较字符串内容,s1 > s2 比较的是地址。
  • 老教材中的 gets、默认 intvoid main 等写法,考试能见到,但现代 C 里不推荐照着写。

如果只挑三类题反复练,这套卷最值得练的是:

  • 字符串和字符数组题。
  • 二维数组初始化和下标题。
  • 素数、闰年、最大值这类小程序设计题。