跳转到内容

exam-85 试卷整理与详解

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

/Users/wff/Downloads/85.pdf

这套题主要复习函数调用、数组和字符串、二维数组、指针、static 局部变量、结构体、宏、字符串库函数、改错题和程序设计题。

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

特别提醒:

  • 本卷有传统 C 教材/考试口径,比如函数没有显式说明返回类型时,默认按 int 理解。现代 C 不建议这样写,实际代码请明确写返回类型。
  • gets() 在旧教材里常出现,但现代 C 已不推荐使用,真实写程序优先用 fgets()
  • int n = 5, a[n]; 在传统基础卷中通常判错;部分现代编译器支持变长数组,不影响本卷答案。
  • 第 6 题的 a[3,5] 按严格 C 语法会被解释为逗号表达式 a[5],但基础卷常按“错误数组引用形式”处理。做考试先按原卷答案记,理解时知道逗号表达式这个细节即可。
  • 程序设计第 1 题红字里出现了 a[i][j],但题目要求“主对角线元素的积”,正确逻辑应是 a[i][i]。我在程序设计解析中按题意修正。
题号答案
1B
2D
3A
4C
5C
6B
7B
8D
9A
10B
11B
12B
13C
14C
15D
16B
17D
18B
19B
20B
21D
22A
23A
24B
25C
题号答案
11217
210
31
4p=a
5How does she
6main
71
89
920
10x%2==0 等合法写法
题号答案
1N
2Y
3N
4N
5N
6Y
7Y
8N
9N
10Y

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

答案:B

错误说法:

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

详细解析:

有返回值的函数调用可以像一个值一样使用。

例如可以作为独立语句:

fun();

可以出现在表达式中:

x = fun() + 3;

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

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

但“形参”是函数定义或函数声明里的参数名,不是函数调用结果能充当的东西。

例如:

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

这里 ab 是形参,add(1, 2) 是函数调用。

容易踩坑点:

  • 把“形参”和“实参”混在一起。函数调用可以当实参,不能当形参。

给新手的建议:

  • 形参在函数定义处,实参在函数调用处。

题目问:下列说法中错误的是哪一个?

答案:D

错误说法:

如果数组初始化时给定的数据元素个数比数组元素个数少,多余的数组元素会被自动初始化为最后一个给定元素的值。

详细解析:

数组部分初始化时,没写到的元素会自动初始化为 0,不是初始化为最后一个给定元素。

例如:

int a[5] = {6, 7};

实际结果是:

a[0] = 6
a[1] = 7
a[2] = 0
a[3] = 0
a[4] = 0

不是:

6 7 7 7 7

容易踩坑点:

  • 把“补 0”误记成“补最后一个值”。

给新手的建议:

  • 数组部分初始化口诀:写几个算几个,没写到的补 0

题目问:C 语言中,每个字符串末尾的字符串结束符是哪一个?

答案:A

答案:

'\0'

详细解析:

C 字符串以空字符 '\0' 作为结束标志。

例如:

char s[] = "abc";

内存中实际是:

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

其他选项区别:

  • '0' 是字符数字 0,它的 ASCII 值不是 0。
  • "0" 是字符串,里面有字符 '0' 和结尾 '\0'
  • "\0" 是字符串字面量,不是一个字符常量。

容易踩坑点:

  • '\0''0' 不是一个东西。

给新手的建议:

  • 字符串结束符一定记成反斜杠零:'\0'

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

答案:C

错误选项:

float a;

详细解析:

数组名作为函数实参传递时,传递的是数组首地址。因此形参通常要写成数组形式或指针形式。

一维实型数组常见写法:

void fun(float a[])
{
...
}

或者:

void fun(float *a)
{
...
}

但:

float a;

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

容易踩坑点:

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

给新手的建议:

  • 数组传参时,形参一般写 类型名 数组名[]类型名 *指针名

题目整理:

int fun3(int x)
{
static int a = 3;
a += x;
return a;
}
int main(void)
{
int k = 2, m = 1, n;
n = fun3(k);
n = fun3(m);
printf("%d\n", n);
return 0;
}

答案:C

输出:

6

详细解析:

static int a = 3; 只初始化一次,并且函数调用结束后值不会丢失。

第一次调用:

fun3(k) -> fun3(2)
a = 3 + 2 = 5
返回 5

第二次调用:

fun3(m) -> fun3(1)
a 不是重新变成 3,而是保留上次的 5
a = 5 + 1 = 6
返回 6

所以最终 n = 6

容易踩坑点:

  • 以为 static int a = 3; 每次调用都会重新执行初始化。

给新手的建议:

  • static 局部变量记住:只初始化一次,值会保留。

题目整理:

int a[10];

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

答案:B

正确选项:

a[10 - 10]

详细解析:

计算下标:

10 - 10 = 0

所以:

a[10 - 10]

等价于:

a[0]

这是合法元素。

其他选项:

  • a[10] 越界,长度为 10 的数组合法下标是 0 ~ 9
  • a(5) 不是数组元素引用语法。
  • a[3,5] 在基础考试口径中不作为正确数组引用形式。

补充理解:

严格 C 语法里,a[3,5] 会被解释为:

a[(3, 5)]

逗号表达式 (3, 5) 的值是 5,所以它等价于 a[5]。但这类写法非常不推荐,也不是基础题期望你使用的数组引用形式。

容易踩坑点:

  • 长度为 10 的数组不能访问 a[10]

给新手的建议:

  • 正常写数组下标时,不要在 [] 里乱写逗号,按清晰的整数下标写。

题目整理:

int a[4][5];

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

答案:B

正确选项:

a[4 - 1][5 - 5]

详细解析:

计算下标:

4 - 1 = 3
5 - 5 = 0

所以:

a[4 - 1][5 - 5]

等价于:

a[3][0]

数组 a[4][5] 的合法行下标是 0 ~ 3,合法列下标是 0 ~ 4,所以 a[3][0] 合法。

其他选项:

  • a[4][1] 行下标 4 越界。
  • a[4][5] 行和列都越界。
  • a[2,1] 不是二维数组元素的正常引用形式。

容易踩坑点:

  • 二维数组 a[4][5] 不是最大下标 45,而是行数 4、列数 5。

给新手的建议:

  • 二维数组下标范围:第一维 0 ~ 行数-1,第二维 0 ~ 列数-1

题目整理:

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

问:正确理解是哪一个?

答案:D

正确理解:

将 5 个初值依次赋给 a[0] 至 a[4]。

详细解析:

数组初始化从下标 0 开始:

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

其余元素自动补 0

a[5] ~ a[9] = 0

容易踩坑点:

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

给新手的建议:

  • 初始化列表永远从数组第一个元素开始放,也就是从下标 0 开始。

题目整理:

char a[] = "This is a program.";

问:输出前 5 个字符的语句是哪一个?

答案:A

正确选项:

printf("%.5s", a);

详细解析:

%s 用于输出字符串,.5 表示最多输出 5 个字符。

所以:

printf("%.5s", a);

会输出:

This

注意第 5 个字符是空格。

容易踩坑点:

  • printf("%s", a) 会输出整个字符串。
  • puts(a) 也会输出整个字符串并自动换行。

给新手的建议:

  • 想限制字符串输出长度,可以使用 printf 的精度格式:%.Ns

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

答案:B

错误选项按题意整理为:

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

详细解析:

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

错误点在:

a[2][]

编译器必须知道每一行有多少列,才能计算 a[i][j] 的地址。

正确写法:

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

容易踩坑点:

  • 以为初始化列表已经写了两列,第二维就可以省略。C 语言不允许这样。

给新手的建议:

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

题目整理:

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

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

答案:B

错误说法:

有元素 a[0][0] 和 a[0][1] 可得到初值 0,其余元素均得不到初值 0。

详细解析:

a[][4] 表示每行 4 个元素。初始化列表有两个 0,所以第一维推断为 1

等价于:

int a[1][4] = {0, 0};

数组实际是:

0 0 0 0

没有显式写到的元素也会自动补 0

容易踩坑点:

  • 以为只有写出来的两个元素有初值。

给新手的建议:

  • 数组一旦部分初始化,后面没写到的元素会自动补 0

题目整理:

char a[10];

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

答案:B

错误选项按题意整理为:

a = getchar();

详细解析:

a 是数组名,不能被整体赋值。

而且 getchar() 一次只读取一个字符,不能直接给整个数组赋值。

其他写法:

gets(a);

旧教材中认为可以读入一行字符串。

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

可以逐个字符读入。

scanf("%s", a);

可以读入一个不含空白字符的字符串。

容易踩坑点:

  • 数组名不能出现在赋值号左边表示“整个数组接收一个值”。

给新手的建议:

  • 给数组元素赋值,用下标;给字符数组读字符串,用数组名作为地址。

题目整理:

#include <stdio.h>
int main(void)
{
int i;
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]);
return 0;
}

答案:C

输出:

3,5,7,

详细解析:

数组按行填充:

x[0]: 1 2 3
x[1]: 4 5 6
x[2]: 7 8 9

循环访问:

i2 - i访问元素
02x[0][2]3
11x[1][1]5
20x[2][0]7

所以输出:

3,5,7,

容易踩坑点:

  • 没有先把二维数组画成表格。

给新手的建议:

  • 二维数组输出题先画行列表,再代入下标。

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

答案:C

正确选项:

int a[10] = {0};

详细解析:

{0} 会让第一个元素显式初始化为 0,其余元素自动补 0

所以整个数组都是 0

0 0 0 0 0 0 0 0 0 0

其他选项的问题:

  • 初始化数组要用花括号 {},不能用圆括号 ()
  • 传统 C 考试中数组长度不能用普通变量 n
  • int a[10] = 0; 不是数组初始化的正确形式。

容易踩坑点:

  • 误以为 = 0 可以初始化整个数组。数组初始化要写 = {0}

给新手的建议:

  • 数组清零模板:int a[10] = {0};

题目整理:

int b;
char c[10];

问:正确的输入语句是哪一个?

答案:D

正确写法:

scanf("%d%s", &b, c);

详细解析:

%d 要读入一个整数,需要传入变量地址:

&b

%s 要读入字符串,字符数组名 c 本身就代表数组首地址,所以写:

c

不需要写 &c

容易踩坑点:

  • %d 忘记写 &
  • %s 对字符数组名又多写一个 &

给新手的建议:

  • 普通变量输入一般加 &,字符数组用 %s 输入时直接写数组名。

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

答案:B

错误选项按题意整理为:

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

详细解析:

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

错误点:

d[3][]

编译器不知道每行有多少个元素,就无法计算元素地址。

可以改成:

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

容易踩坑点:

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

给新手的建议:

  • 看到二维数组 [][列数] 可以,看到 [行数][] 要警惕。

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

答案:D

错误选项:

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

详细解析:

这句作为“字符数组初始化”是可以的,它定义了 5 个字符:

a b c d e

但它不是一个正确的 C 字符串,因为没有结尾的 '\0'

如果要作为字符串 "abcde",至少需要 6 个位置:

char s[6] = "abcde";

或者:

char s[] = "abcde";

容易踩坑点:

  • 字符数组合法,不代表它一定是合法字符串。

给新手的建议:

  • 只要题目说“字符串”,就要检查有没有空间放 '\0'

题目整理:

int func(int a, int b)
{
return a + b;
}
int main(void)
{
int x = 2, y = 5, z = 8, r;
r = func(func(x, y), z);
printf("%d\n", r);
return 0;
}

答案:B

输出:

15

详细解析:

先算内层:

func(x, y) = func(2, 5) = 7

再算外层:

func(7, z) = func(7, 8) = 15

所以输出 15

容易踩坑点:

  • 看到函数嵌套调用就乱,其实从最里面开始算就行。

给新手的建议:

  • 函数嵌套调用题,用“由内向外”的顺序计算。

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

答案:B

错误选项:

int n = 5, a[n];

详细解析:

按传统 C 基础考试口径,数组长度要使用常量表达式,不能使用普通变量。

所以本题判:

int n = 5, a[n]; // 错误

补充提醒:

部分现代 C 编译器可能支持变长数组,但考试题按传统教材答案处理。

容易踩坑点:

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

给新手的建议:

  • 做这类卷子时,数组长度优先写成明确数字,比如 int a[5];

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

答案:B

答案:

表达式

详细解析:

void 函数没有返回值,所以不能把它当成一个值来参与表达式。

例如:

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

可以这样调用:

show();

不能这样写:

int x = show() + 1; // 错误

因为 show() 没有值。

容易踩坑点:

  • 以为所有函数调用都能拿来计算。

给新手的建议:

  • void 表示“没有返回值”,只能做动作,不能当值用。

题目问:若二维数组 am 列,则在 a[i][j] 前的元素个数是多少?

答案:D

答案:

i * m + j

详细解析:

C 语言二维数组按行存储。

a[i][j] 前面有:

  • 前面完整的 i 行,每行 m 个元素,共 i * m 个。
  • 当前第 i 行中,a[i][j] 前面还有 j 个元素。

所以总数是:

i * m + j

容易踩坑点:

  • 忘记下标从 0 开始。
  • ij 写反。

给新手的建议:

  • 二维数组位置题就想:前面几整行,加上本行前面几个。

题目整理:

int max(int x, int y)
{
int z;
if (x > y)
z = x;
else
z = y;
return z;
}
int main(void)
{
int a = 45, b = 27, c = 0;
c = max(a, b);
printf("%d\n", c);
return 0;
}

答案:A

输出:

45

详细解析:

调用:

max(45, 27)

因为:

45 > 27

所以 z = 45,返回 45

容易踩坑点:

  • 原卷 OCR 中 main 和变量声明有缺失,按题意应理解为比较两个数取较大值。

给新手的建议:

  • 函数返回题先看函数体最终 return 什么。

题目整理:

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

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

答案:A

正确写法:

strcat(a2, a1);

详细解析:

strcat(目标字符串, 追加的字符串) 会把第二个字符串连接到第一个字符串后面。

执行后:

a2 = "1234abc"

容易踩坑点:

  • strcpy 是复制,会覆盖原字符串。
  • strcat(a1, a2) 是把 a2 追加到 a1 后面,方向反了。

给新手的建议:

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

题目整理:

static char str[10] = "China";

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

答案:B

答案:

10

详细解析:

数组定义中已经写明:

str[10]

所以数组元素个数就是 10

字符串 "China" 有 5 个有效字符,加上 '\0' 占 6 个位置,但数组整体仍然有 10 个元素。

容易踩坑点:

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

给新手的建议:

  • 问数组元素个数,看方括号里的长度。

题目问:C 语言程序中,若对函数类型未加显式说明,则函数的隐含说明类型是什么?

答案:C

答案:

int

详细解析:

按传统 C 教材口径,函数未显式写返回类型时,默认返回 int

例如老式写法:

fun(void)
{
return 1;
}

会按:

int fun(void)

理解。

现代 C 不推荐这样写,实际代码应明确写返回类型:

int fun(void)
{
return 1;
}

容易踩坑点:

  • 以为 C 会根据 return 自动推断函数返回类型。

给新手的建议:

  • 实际写代码时,函数名前面一定明确写 intvoiddouble 等返回类型。

题目整理:

int x;
scanf("%d", &x);
if (x > 15)
printf("%d", x - 5);
if (x > 10)
printf("%d", x);
if (x > 5)
printf("%d", x + 5);

输入:

12

答案:

1217

详细解析:

x = 12

三个 if 是彼此独立的,不是 else if

依次判断:

12 > 15 假,不输出
12 > 10 真,输出 12
12 > 5 真,输出 17

所以连在一起输出:

1217

容易踩坑点:

  • 把三个独立 if 当成 if...else if...else

给新手的建议:

  • 没有 else 的多个 if 会一个一个独立判断。

题目整理:

for (i = 0, j = 10; i <= j; i++, j--)
k = i + j;

答案:

10

详细解析:

每轮循环中:

i + j

始终是 10

变化过程:

iji <= jk = i + j
01010
1910
2810
3710
4610
5510
64停止

最终 k = 10

容易踩坑点:

  • 忘记 i = 5, j = 5 时条件仍然成立。

给新手的建议:

  • 双变量 for 循环题,画表最稳。

题目整理:

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

答案:

1

详细解析:

初始:

a = 3
b = 2
c = 1

第一个 if

a > b -> 3 > 2 为真
a = b -> a = 2

第二个 if

b > c -> 2 > 1 为真
b = c -> b = 1

所以最终:

b = 1

容易踩坑点:

  • else 只和离它最近、尚未匹配的 if 配对。

给新手的建议:

  • 变量变化题每执行一条赋值语句,就立刻更新变量值。

题目问:将数组 a 的首地址赋给指针变量 p 的语句是什么?

答案:

p = a;

详细解析:

数组名 a 在表达式中通常代表数组首元素地址,也就是:

&a[0]

所以可以写:

p = a;

等价理解为:

p = &a[0];

容易踩坑点:

  • 写成 *p = a,这是给 p 指向的变量赋值,不是让 p 指向数组。

给新手的建议:

  • 指针保存地址,数组名可以当首地址用。

题目整理:

#include <string.h>
int main(void)
{
char str1[] = "How do you do", *p1 = str1;
strcpy(str1 + strlen(str1) / 2, "es she");
printf("%s\n", p1);
return 0;
}

答案:

How does she

详细解析:

原字符串:

How do you do

长度是 13,所以:

strlen(str1) / 2 = 13 / 2 = 6

str1 + 6 指向下标 6 的位置。

原字符串下标:

0:H 1:o 2:w 3:' ' 4:d 5:o 6:' ' 7:y 8:o 9:u 10:' ' 11:d 12:o

从下标 6 开始复制 "es she",会把后面的内容覆盖掉:

How do + es she -> How does she

容易踩坑点:

  • strcpy 会从目标位置开始覆盖原内容。
  • strlen(str1) / 2 是整数除法。

给新手的建议:

  • 字符串指针偏移题,一定把每个字符的下标标出来。

题目问:一个 C 源程序中至少应包括一个什么函数?

答案:

main

或:

主函数

详细解析:

C 程序从 main 函数开始执行。

标准写法:

int main(void)
{
return 0;
}

容易踩坑点:

  • main 当成普通函数名随便改。程序入口必须是 main

题目整理:

int x = 72;
x = (x >= 60);

答案:

1

详细解析:

关系表达式:

x >= 60 -> 72 >= 60 -> 真

在 C 语言中,关系表达式为真时值为 1,为假时值为 0

所以:

x = 1

表达式的值也是 1

容易踩坑点:

  • 以为 x 仍然是 72。赋值后 x 已经变成 1

题目整理:

int f(int x, int y)
{
return (y - x) * x;
}
int main(void)
{
int a = 3, b = 4, c = 5, d;
d = f(f(3, 4), f(3, 5));
printf("%d\n", d);
return 0;
}

答案:

9

详细解析:

先算内层:

f(3, 4) = (4 - 3) * 3 = 3
f(3, 5) = (5 - 3) * 3 = 6

再算外层:

f(3, 6) = (6 - 3) * 3 = 9

所以输出 9

容易踩坑点:

  • 函数嵌套调用时,没有先从最里面开始算。

给新手的建议:

  • 遇到 f(f(...), f(...)),先把两个内层函数分别算出来。

题目问:逗号表达式 (a=3*5,a*4),a+5 的值是多少?

答案:

20

详细解析:

先看前半部分:

(a = 3 * 5, a * 4)

执行后:

a = 15
a * 4 = 60

但整个题目后面还有:

, a + 5

逗号表达式的值是最后一个表达式的值,所以最终值是:

a + 5 = 15 + 5 = 20

容易踩坑点:

  • 只算到 a * 4 = 60 就停了。

给新手的建议:

  • 逗号表达式从左到右执行,但最终值看最后一个表达式。

题目问:设 xint 型变量,描述“x 是偶数”的表达式是什么?

答案示例:

x % 2 == 0

详细解析:

偶数除以 2 的余数为 0,所以用求余运算:

x % 2

判断余数是否等于 0:

x % 2 == 0

容易踩坑点:

  • 判断相等要写 ==,不能写 =

给新手的建议:

  • 整除判断固定模板:x % n == 0

题目整理:

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

题目说执行后 i 的值为 11

答案:N

详细解析:

j = 0 是赋值表达式,不是判断相等。

赋值表达式的值是被赋的值,也就是 0

所以 if 条件为假,执行:

i--;

最终:

i = 9

容易踩坑点:

  • = 看成 ==

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

答案:Y

详细解析:

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

容易踩坑点:

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

题目整理:

int i = 3;
printf("%d", -i++);

题目说输出值为 -4

答案:N

详细解析:

后置自增 i++ 先使用原值,再自增。

表达式:

-i++

按优先级理解为:

-(i++)

所以输出的是:

-3

输出之后,i 才变成 4

容易踩坑点:

  • 把后置自增当成先加后用。

题目整理:

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

题目说在 Turbo C 中合法。

答案:N

详细解析:

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

FILE *fp;

并且需要包含头文件:

#include <stdio.h>

file 不是标准文件类型名。

容易踩坑点:

  • C 语言大小写敏感,FILEfile 不是同一个东西。

题目:

char *p = "girl";

题目说它的含义是定义字符型指针变量 pp 的值是字符串 "girl"

答案:N

详细解析:

p 是字符指针,它保存的是字符串常量首字符的地址。

更准确地说:

p 的值是地址,不是字符串本身。

*p 才是这个地址处的第一个字符:

*p == 'g'

容易踩坑点:

  • 指针变量里存的是地址,不是整段字符串内容。

题目整理:

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

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

答案:Y

详细解析:

初始化结果是:

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

每个内部花括号只给该行第一个元素赋值,其余自动补 0

容易踩坑点:

  • 忘记部分初始化时其余元素会自动补 0

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

答案:Y

详细解析:

先算:

a > b -> 3 > 2 -> 1

再算:

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

所以为真。

容易踩坑点:

  • 关系表达式成立时值为 1,不成立时值为 0

题目整理:

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

题目说:由于变量 t 没定义,所以此宏定义是错误的。

答案:N

详细解析:

宏定义本身只是文本替换,不要求在宏定义处就已经定义 t

只要使用宏时当前作用域里有 t,就可能通过。

例如:

int a = 1, b = 2, t;
S(a, b);

不过这个宏写法并不安全,真实代码中不推荐这样写。

容易踩坑点:

  • 把宏当成普通函数。宏更像预处理阶段的文本替换。

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

答案:N

详细解析:

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

例如:

struct Date
{
int year;
int month;
int day;
};
struct Student
{
char name[20];
int age;
};

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

容易踩坑点:

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

题目:

char c[] = "Very Good";

题目说这是一个合法的为字符数组赋值的语句。

答案:Y

详细解析:

这是一条字符数组初始化语句,合法。

编译器会自动根据字符串长度分配空间:

V e r y 空格 G o o d \0

容易踩坑点:

  • 定义时可以用字符串初始化字符数组;定义之后不能用数组名整体赋值。

改错题 1:提取长整数中的偶数字位

Section titled “改错题 1:提取长整数中的偶数字位”

题目功能:将长整型数中每一位上为偶数的数字取出,构成一个新的数。高位仍在高位,低位仍在低位。

例如:

s = 87654
t = 864

原题主要错误点:

  • if (d % 2 = 0) 错,判断相等要写 ==
  • *t = d * s1 + t; 错,t 是指针地址,应使用 *t 取得结果变量的值。
  • S /= 10; 错,C 语言大小写敏感,应该更新小写变量 s

完整修正代码:

#include <stdio.h>
void fun(long s, long *t)
{
int d;
long s1 = 1;
*t = 0;
while (s > 0)
{
d = s % 10;
if (d % 2 == 0)
{
*t = d * s1 + *t;
s1 *= 10;
}
s /= 10;
}
}
int main(void)
{
long s, t;
printf("Please enter s: ");
scanf("%ld", &s);
fun(s, &t);
printf("The result is: %ld\n", t);
return 0;
}

过程解释:

87654 为例,从低位开始取:

s 当前值d = s % 10是否偶数新 ts1
876544410
87655410
876664100
87764100
888641000

最终得到:

864

为什么要用 s1

每找到一个偶数字位,就要把它放到当前结果的更高一位,所以 s1110100 这样逐步变大。

容易踩坑点:

  • = 是赋值,== 才是判断相等。
  • t 是指针,*t 才是它指向的那个变量。
  • C 语言区分大小写,Ss 是两个不同标识符。

给新手的建议:

  • 指针改错题先问自己:这里要操作“地址”还是“地址里的值”?如果要改外面的变量,就通常要写 *t

改错题 2:按 ASCII 值合并两个字符串

Section titled “改错题 2:按 ASCII 值合并两个字符串”

题目功能:逐个比较 ab 两个字符串对应位置中的字符,把 ASCII 值大或相等的字符依次存放到 c 数组中,形成一个新字符串。

例如:

a = "aBCDeFgH"
b = "ABcd"
c = "aBcdeFgH"

原题主要错误点:

  • k=1; 错,应从 c[0] 开始存,所以 k=0;
  • while(*p!=*q) 错,不能只在两个字符不相等时循环,应在两个字符串还没都结束时循环。
  • fun(a[10], b[10], c[10]); 错,数组传参应传数组名 a, b, c,不是传越界元素。

完整修正代码:

#include <stdio.h>
#include <string.h>
void fun(char *p, char *q, char *c)
{
int k = 0;
while (*p != '\0' || *q != '\0')
{
if (*p < *q)
c[k] = *q;
else
c[k] = *p;
if (*p)
p++;
if (*q)
q++;
k++;
}
c[k] = '\0';
}
int main(void)
{
char a[10] = "aBCDeFgH";
char b[10] = "ABcd";
char c[10] = {'\0'};
fun(a, b, c);
printf("The string a: ");
puts(a);
printf("The string b: ");
puts(b);
printf("The result: ");
puts(c);
return 0;
}

过程解释:

逐位比较:

位置a 字符b 字符取较大者
0aAa
1BBB
2Ccc
3Ddd
4e\0e
5F\0F
6g\0g
7H\0H

所以结果是:

aBcdeFgH

为什么循环条件是 ||

while (*p != '\0' || *q != '\0')

意思是:只要两个字符串中还有一个没结束,就继续处理。

容易踩坑点:

  • 数组下标从 0 开始,所以 k 应从 0 开始。
  • 字符串结束符是 '\0',不是字符 '0'
  • 数组名传参写 fun(a, b, c),不要写 a[10]
  • || 是逻辑或,OCR 里容易看成 II

给新手的建议:

  • 字符串处理题一定要考虑两个问题:循环什么时候结束、结果字符串最后有没有 '\0'

程序设计 1:求 3x3 矩阵主对角线元素的积

Section titled “程序设计 1:求 3x3 矩阵主对角线元素的积”

题目功能:输入一个 33 列矩阵的各个元素值,然后输出主对角线元素的积,并在 fun() 函数中求出。

主对角线位置:

a[0][0]
a[1][1]
a[2][2]

参考答案:

#include <stdio.h>
int fun(int a[3][3])
{
int sum = 1;
int i;
for (i = 0; i < 3; i++)
sum = sum * a[i][i];
return sum;
}
int main(void)
{
int i, j, s;
int a[3][3];
for (i = 0; i < 3; i++)
for (j = 0; j < 3; j++)
scanf("%d", &a[i][j]);
s = fun(a);
printf("sum=%d\n", s);
return 0;
}

示例:

输入矩阵:
1 2 3
4 5 6
7 8 9

主对角线是:

1, 5, 9

乘积:

1 * 5 * 9 = 45

关于原卷红字:

原卷红色代码里能看到类似:

sum = sum * a[i][j];

fun() 函数里只控制了 i,并没有正确控制 j;而且题目明确要求“主对角线元素的积”,所以正确写法应是:

sum = sum * a[i][i];

容易踩坑点:

  • 主对角线不是每一行所有元素,而是行下标和列下标相等的元素。
  • 求乘积时初值应为 1,不是 0。如果初值为 0,乘出来永远是 0
  • 输入二维数组元素时要写 &a[i][j]

给新手的建议:

  • 矩阵题先画表,主对角线就是左上到右下那条线。

程序设计 2:计算 1 + 12 + 123 + … 的前 n 项和

Section titled “程序设计 2:计算 1 + 12 + 123 + … 的前 n 项和”

题目功能:计算并输出:

1 + 12 + 123 + 1234 + ...

的前 n 项和,其中 0 < n < 10n 从键盘输入。

例如:

n = 3
sum = 1 + 12 + 123 = 136

参考答案:

#include <stdio.h>
long sum(int n)
{
int i;
long k, sum;
sum = 0;
k = 0;
for (i = 1; i <= n; i++)
{
k = 10 * k + i;
sum += k;
}
return sum;
}
int main(void)
{
int n;
printf("Input n: ");
scanf("%d", &n);
printf("sum=%ld\n", sum(n));
return 0;
}

过程解释:

变量 k 表示当前项:

ik = 10 * k + i当前项 ksum
110*0+111
210*1+21213
310*12+3123136
410*123+412341370

如果 n = 6

1 + 12 + 123 + 1234 + 12345 + 123456 = 137171

容易踩坑点:

  • ksum 不是同一个变量。k 是当前项,sum 是累计和。
  • 每一项不是简单的 i,而是上一项乘 10 再加当前数字。
  • %ld 用来输出 long 类型。

给新手的建议:

  • 这种“1、12、123”题目固定套路是:k = k * 10 + i

这套卷最容易丢分的点集中在数组、字符串和函数调用上。建议你复盘时重点抓住下面这些:

  • 有返回值的函数调用可以当表达式、实参或独立语句,但不能当形参。
  • 数组部分初始化时,没写到的元素补 0
  • 字符串结尾是 '\0',不是 '0'
  • 数组名传参传的是首地址,函数里可能修改原数组元素。
  • 二维数组第二维不能省略。
  • 字符数组不一定是字符串,能作为字符串使用必须有 '\0'
  • void 函数没有返回值,不能放进需要值的表达式中。
  • 字符串连接用 strcat,字符串复制用 strcpy,字符串比较用 strcmp
  • 判断相等用 ==,赋值用 =
  • 指针变量保存地址,*p 才表示地址里的值。

如果只练三类题,建议优先练:

  • 字符数组和字符串结束符题。
  • 二维数组下标、初始化和主对角线题。
  • 函数调用、返回值、static 局部变量和指针传参题。