跳转到内容

exam-83 试卷整理与详解

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

/Users/wff/Downloads/83.pdf

这套题继续围绕 C 语言基础展开:函数调用、函数返回类型、数组定义与初始化、二维数组行列位置、字符串结束符、局部变量作用域、位运算、宏定义、switch、改错题和程序设计题。

PDF 的 OCR 对代码符号有一些误差,比如 '\0'||&&==、数组方括号和花括号容易识别错。我整理时按原卷红色标注和 C 语言语法一起校对。

特别提醒:

  • 选择题第 14 题,原卷按老式 C 语言口径:函数名前没有写返回类型时,默认返回 int。现代 C 不推荐也不应这样写,实际代码请明确写返回类型。
  • 选择题第 19 题,原卷红字标的是 [整型常量]。更严谨的教材表达常见为 [常量表达式],但这套卷子按原卷答案整理为 B,解析里会说明。
  • 填空题第 7 题的 a | b 是按位或,不是逻辑或 ||,这里很容易看错。
题号答案
1C
2A
3D
4B
5A
6B
7A
8D
9D
10D
11B
12B
13C
14C
15A
16C
17A
18B
19B(原卷口径)
20D
21B
22C
23B
24B
25C
题号答案
10
20
33
40
54
65.0,4,c=3
715
820
9x % 2 == 0 等合法写法
105
题号答案
1N
2N
3Y
4Y
5Y
6N
7Y
8Y
9N
10N

题目整理:

func((e1, e2), (e3, e4, e5))

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

答案:C

详细解析:

这个函数调用最外层只有两个实参:

(e1, e2)
(e3, e4, e5)

括号里的逗号属于逗号表达式,不是函数参数分隔符。

所以实参个数是:

2

容易踩坑点:

  • 一看到逗号就直接数成 5 个。
  • 忘记只有最外层逗号才分隔函数实参。

给新手的建议:

  • 数实参个数时,先找到函数调用最外层的小括号,再只数最外层逗号。

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

答案:A

正确说法:

C 语言的函数不可以嵌套定义。

详细解析:

C 语言可以嵌套调用函数:

printf("%d\n", max(a, b));

但不能在一个函数内部再定义另一个函数:

int main(void)
{
int fun(void) // 错误
{
return 1;
}
}

其他选项的问题:

  • C 语言不是所有函数都是外部函数,static 函数可以是内部函数。
  • C 语言编译时当然会检查语法。
  • C 语言中通常把子程序统一称为函数,没有“过程”和“函数”两类的语法区分。

容易踩坑点:

  • 把“函数嵌套调用”和“函数嵌套定义”混为一谈。

给新手的建议:

  • 调用可以嵌套,定义不可以嵌套。

题目整理:

int a[10];

问:给数组 a 的所有元素分别赋值为 1、2、3、... 的语句是哪一个?

答案:D

正确写法:

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

给新手的建议:

  • 给长度为 10 的数组赋值时,最终一定要覆盖 a[0]a[9]

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

答案:B

错误写法:

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

详细解析:

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

错误点在:

d[3][]

编译器必须知道每一行有几个元素,才能计算 d[i][j] 的地址。

可以这样写:

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

容易踩坑点:

  • 以为有初始化列表就可以省略第二维。

给新手的建议:

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

题目问:在 C 语言的函数中,正确的说法是哪一个?

答案:A

正确说法:

函数可以有形参,也可以没有形参。

详细解析:

有形参的函数:

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

没有形参的函数:

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

数组名也可以作形参,例如:

void print_array(int a[], int n)
{
...
}

容易踩坑点:

  • 以为函数必须有参数。
  • 以为数组不能作为函数参数。

给新手的建议:

  • 不需要参数时,建议写 void,比如 int main(void)

题目整理:

int a[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, *p = a;

问:不能表示 a 数组元素的表达式是哪一个?

答案:B

错误表达式:

a[*p - a]

详细解析:

初始化后:

p 指向 a[0]
*p 的值是 1
a 表示数组首地址

表达式:

*p - a

左边是整数 1,右边是地址 a,整数和地址不能这样相减,所以不能正确表示数组下标。

其他选项:

*p++

等价于:

*(p++)

它可以表示当前 p 指向的元素,只是取完后会让 p 后移。

*p

表示 p 当前指向的元素。

a[9]

表示第 10 个元素。

容易踩坑点:

  • *p 是元素值,pa 才是地址。
  • *p++ 会改变 p 的指向。

给新手的建议:

  • 数组和指针混合题,先区分“值”和“地址”。

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

答案:A

正确说法:

函数可以返回一个值,也可以什么值也不返回。

详细解析:

有返回值的函数:

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

无返回值的函数:

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

其他选项的问题:

  • 空函数虽然不做操作,但可以作为占位函数、调试用函数或后续扩展接口。
  • 用户自定义函数在使用前通常需要声明,但老式 C 语境中题目不会把“必须加以声明”作为最稳正确项。
  • 老式 C 中函数声明不一定都写完整参数类型和返回类型;现代写法应明确写。

容易踩坑点:

  • 把考试中的老式 C 口径和现代规范写法混在一起。

给新手的建议:

  • 实际写代码时,函数返回类型和参数类型都明确写出来。

题目问:若二维数组 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 开始。

给新手的建议:

  • 看到二维数组位置题,就想“前面有几整行,再加本行前面几个”。

题目整理:

char s[] = "ab\0cd";
printf("%s", s);

答案:D

输出:

ab

详细解析:

字符串输出 %s 遇到 '\0' 就停止。

字符串实际存储为:

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

printf("%s", s) 从开头输出,输出到第一个 '\0' 前停止,所以只输出:

ab

容易踩坑点:

  • 以为字符串中间的 '\0' 会被当作普通字符输出。

给新手的建议:

  • %s 来说,第一个 '\0' 就是字符串结束。

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

答案:D

按原卷选项应理解为:

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

详细解析:

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

这里第二维是 3,每一行 3 个元素:

1 2 3
4 5 6

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

其他选项的问题:

  • int a[2][4] = {{1,2,3},{4,5},{6}}; 给了 3 行初值,但数组只有 2 行。
  • int a[][3]={{1,0,1}{},{1,1}}; 语法不正确,行之间缺少逗号。
  • int a[2][] = ... 第二维省略,错误。

容易踩坑点:

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

给新手的建议:

  • 二维数组初始化先看第二个 [] 里有没有数字。

题目整理:

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,后两个是自动补的 0

容易踩坑点:

  • 以为只有写出来的元素才得到初值。

给新手的建议:

  • 数组只要进行了部分初始化,没写到的元素会自动补 0

题目整理:

static char str[10] = "China";

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

答案:B

详细解析:

数组长度已经写明:

str[10]

所以数组元素个数是:

10

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

容易踩坑点:

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

给新手的建议:

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

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

答案:C

错误写法:

char str[5] = "good!";

详细解析:

字符串 "good!" 有 5 个有效字符:

g o o d !

还需要一个字符串结束符:

'\0'

所以一共需要 6 个位置。

str[5] 只有 5 个位置,放不下 '\0',因此错误。

其他选项:

char *str = "good!";

指针指向字符串常量,合法。

char str[5] = {'g', 'o', 'o', 'd', 0};

这里最后一个 0 相当于 '\0',合法,表示字符串 "good"

char str[] = "good!";

编译器自动分配 6 个位置,合法。

容易踩坑点:

  • 只数有效字符,忘记 '\0'

给新手的建议:

  • 双引号字符串初始化字符数组时,空间至少要是“有效字符数 + 1”。

题目整理:

fun(float x)
{
float y;
y = 3 * x - 4;
return y;
}

问:fun 函数的返回值类型是什么?

答案:C

按原卷和老式 C 口径:

int

详细解析:

函数名前没有显式写返回类型。

在老式 C 语言教材中,如果函数没有写返回类型,默认返回类型是 int

因此这题选:

int

补充提醒:

现代 C 语言不推荐也不应这样写。实际写代码时应明确写:

float fun(float x)
{
float y;
y = 3 * x - 4;
return y;
}

容易踩坑点:

  • 以为返回类型由 return y;y 的类型决定。

给新手的建议:

  • 函数返回类型看函数名前面写了什么类型,不是看 return 后面的表达式。

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

答案:A

正确答案:

()

详细解析:

函数调用基本格式:

函数名(实参列表)

即使没有实参,也要写小括号:

fun();

容易踩坑点:

  • 无参函数调用时省略 ()

给新手的建议:

  • 函数名后面带 (),才是调用函数。

题目问:下列说法不正确的是哪一个?

答案:C

错误说法:

主函数 main 中定义的变量在整个文件或程序中有效。

详细解析:

main 函数中定义的变量是 main 的局部变量,只在 main 函数内部有效。

例如:

int main(void)
{
int x = 10;
return 0;
}
void fun(void)
{
printf("%d", x); // 错误,x 在这里不可见
}

其他说法:

  • 形参是局部变量。
  • 复合语句中定义的变量只在该复合语句中有效。
  • 不同函数中可以使用同名变量,它们互不影响。

容易踩坑点:

  • 以为 main 是主函数,所以里面的变量全程序可见。

给新手的建议:

  • 变量在哪个 {} 里定义,通常就只在那个范围内有效。

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

答案:A

错误写法:

char s[5] = "abcde";

详细解析:

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

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

s[5] 只有 5 个位置,所以放不下字符串结束符 '\0'

其他选项:

char s[] = "abcde";

编译器自动分配 6 个位置,合法。

char s[5] = "abc";

需要 4 个位置,剩余位置自动补 0,合法。

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

作为字符数组初始化是合法的,只是它没有 '\0',不能当普通字符串使用。

容易踩坑点:

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

给新手的建议:

  • 双引号字符串会自动带 '\0'
  • 单引号字符列表不会自动多加 '\0'

题目问:C 语言中函数调用的方式有哪些?

答案:B

正确说法:

函数调用可以作为语句、函数表达式或函数参数三种。

详细解析:

作为独立语句:

fun();

作为表达式的一部分:

x = fun() + 3;

作为函数实参:

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

容易踩坑点:

  • 忘记函数调用可以作为另一个函数的实参。

给新手的建议:

  • 有返回值的函数调用,可以像一个值一样参与表达式。

题目问:在 C 语言中,一维数组的定义方式为:类型说明符 数组名后面接什么?

答案:B(原卷口径)

原卷答案:

[整型常量]

详细解析:

这套卷子按传统基础题口径,强调数组长度应写成固定整数,例如:

int a[10];

所以原卷选 B

补充理解:

更严谨的教材表达常见是:

[常量表达式]

比如:

int a[2 + 3];

这里 2 + 3 也是常量表达式。

容易踩坑点:

  • 把普通变量也当成数组长度常量。
  • 只背 [整型常量],不知道 [2 + 3] 这种常量表达式也常见。

给新手的建议:

  • 做本卷按红字答案记 B;真正理解时,记“数组长度通常应是常量表达式”。

题目整理:

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

答案:D

详细解析:

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

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

后面的元素没有显式初值,会自动补 0

a[5] ~ a[9] = 0

容易踩坑点:

  • 以为初值 6 会赋给 a[6]
  • 以为第一个初值从 a[1] 开始。

给新手的建议:

  • 初始化列表永远从第一个元素 a[0] 开始依次放。

题目整理:

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

答案:B

详细解析:

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

可以理解为:

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

所以正确叙述是:

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

容易踩坑点:

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

给新手的建议:

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

题目整理:

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

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

答案:C

正确写法:

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

详细解析:

printf 输出字符串时,可以用精度控制最多输出几个字符:

%.5s

表示最多输出字符串前 5 个字符。

所以会输出:

This

注意第 5 个字符是空格。

容易踩坑点:

  • 以为 %s 只能输出整个字符串。
  • 忘记空格也算一个字符。

给新手的建议:

  • printf("%.Ns", 字符串) 可以输出前 N 个字符。

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

答案:B

错误写法:

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

详细解析:

二维数组第二维不能省略。

可以写:

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

但不能写:

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

容易踩坑点:

  • 以为有初始化列表就能省略第二维。

给新手的建议:

  • 二维数组定义时,必须让编译器知道每行几个元素。

题目整理:

char a[10];

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

答案:B

错误写法:

a = getchar();

详细解析:

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

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

可以逐个输入:

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

也可以按字符串输入:

scanf("%s", a);

容易踩坑点:

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

给新手的建议:

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

题目问:凡是函数中未指定存储类别的局部变量,其隐含的存储类别是什么?

答案:C

答案:

auto

详细解析:

函数内部普通局部变量默认存储类别是自动变量:

void fun(void)
{
int x; // 默认等价于 auto int x;
}

auto 局部变量通常在进入函数或代码块时创建,离开函数或代码块后失效。

容易踩坑点:

  • 把普通局部变量默认理解成 static

给新手的建议:

  • 普通局部变量默认是 auto,如果想让它保留上次调用后的值,才需要写 static

题目整理:

int x = 2, y = 3, z = 4;
!x + y > z

答案:0

详细解析:

先算逻辑非:

!x = !2 = 0

因为非 0 为真,逻辑非后为 0

再算:

0 + 3 > 4
3 > 4

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

0

容易踩坑点:

  • 忘记 ! 的优先级高于 +
  • 忘记 C 语言中假是 0

给新手的建议:

  • 逻辑表达式题先把每个真/假换成 10

题目整理:

3 && 0

答案:0

详细解析:

逻辑与 && 要求两边都为真,结果才为真。

在 C 语言中:

3 是真
0 是假

所以:

真 && 假 -> 假 -> 0

容易踩坑点:

  • && 当成普通乘法。

给新手的建议:

  • && 的结果只可能是 10

题目整理:

int x = 2;
z = x++ - 1;

问:x 的值是多少?

答案:3

详细解析:

x++ 是后置自增:

先使用 x 的旧值 2
再让 x 加 1 变成 3

所以:

z = 2 - 1 = 1
x = 3

容易踩坑点:

  • x++ 当成先加再用。

给新手的建议:

  • x++ 先用后加,++x 先加后用。

题目整理:

int x = 15, n = 2;
x %= (n + 3);

答案:0

详细解析:

复合赋值:

x %= (n + 3);

等价于:

x = x % (n + 3);

先算:

n + 3 = 2 + 3 = 5

再算:

15 % 5 = 0

所以 x 的值为 0

容易踩坑点:

  • 忘记 %= 是取余后再赋值。

给新手的建议:

  • 复合赋值可以先展开成普通赋值再算。

题目整理:

int x = 2;
z = ++x + 1;

答案:4

详细解析:

++x 是前置自增:

先让 x 变成 3
再使用 x 的新值 3

所以:

z = 3 + 1 = 4

容易踩坑点:

  • ++xx++ 混淆。

给新手的建议:

  • 前置先加再用,后置先用再加。

题目整理:

int b, c;
float a;
scanf("%f,%d,c=%d", &a, &b, &c);

要求输入后:

a = 5.0
b = 4
c = 3

答案:5.0,4,c=3

详细解析:

scanf 格式串里除了格式说明符,普通字符也必须按原样输入。

格式串:

"%f,%d,c=%d"

表示输入数据必须长这样:

浮点数,整数,c=整数

所以应输入:

5.0,4,c=3

容易踩坑点:

  • 忘记逗号和 c= 也要原样输入。

给新手的建议:

  • scanf 格式串里不是 %... 的普通字符,输入时通常要照着敲。

题目整理:

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

答案:15

详细解析:

这里的 | 是按位或,不是逻辑或 ||

把两个数写成二进制:

13 = 1101
6 = 0110

逐位做“或”运算:

1101
| 0110
------
1111

二进制 1111 转成十进制就是:

15

容易踩坑点:

  • | 看成 ||
  • 不会把十进制数转成二进制看位运算。

给新手的建议:

  • 单个 | 是按位或,两个 || 才是逻辑或。

题目整理:

int x = 2, y = 2;
x *= y + 8;

答案:20

详细解析:

复合赋值:

x *= y + 8;

等价于:

x = x * (y + 8);

所以:

x = 2 * (2 + 8) = 20

容易踩坑点:

  • 错算成 x = x * y + 8

给新手的建议:

  • 复合赋值右侧整体参与运算。

题目:设 xint 型变量,写出描述“x 是偶数”的表达式。

推荐答案:

x % 2 == 0

详细解析:

偶数除以 2 的余数为 0,所以用取余判断:

x % 2 == 0

原卷还给出了一些等价写法,例如:

!(x % 2)

容易踩坑点:

  • 写成 x / 2 == 0,这不是判断偶数。
  • 忘记判断相等要写 ==

给新手的建议:

  • 判断整除或奇偶,优先想到 %

题目整理:

(k = a = 5, b = 3, a * b)

问:k 的值是多少?

答案:5

详细解析:

逗号表达式会从左到右依次执行。

第一项:

k = a = 5

执行后:

a = 5
k = 5

第二项:

b = 3

执行后:

b = 3

第三项:

a * b

只是计算:

5 * 3 = 15

但没有把结果赋给 k,所以 k 仍然是:

5

容易踩坑点:

  • 以为逗号表达式最后一个值会自动赋给 k

给新手的建议:

  • 只有写了赋值号,变量值才会变。

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

答案:N

解析:

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

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

题目:C 程序中有调用关系的所有函数必须放在同一个源程序文件中。

答案:N

解析:

C 程序可以由多个源文件组成。

一个文件中的函数可以通过函数声明调用另一个文件中的函数,最后链接到一起。

题目:

a = (b = 4) + (c = 6)

判断:这是一个合法的赋值表达式。

答案:Y

解析:

赋值表达式本身也有值。

(b = 4)

的值是 4(c = 6) 的值是 6,所以整个表达式合法。

题目整理:

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

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

答案:Y

解析:

数组内容是:

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

没有显式初始化的元素会自动补 0

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

答案:Y

解析:

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

-32768 ~ 32767

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

题目整理:

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

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

答案:N

解析:

宏定义本身只是文本替换规则,不会在定义阶段检查 t 有没有定义。

使用宏展开后,如果代码中没有提前定义 t,才会产生编译错误。

给新手的建议:

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

题目整理:

int c;
while (c = getchar())
;

判断:这是正确的 C 语句。

答案:Y

解析:

从语法上看,这是合法的 C 语句。

它的含义是:不断从键盘读取字符并赋给 c,只要赋值表达式的值不为 0,循环就继续。

补充提醒:

这并不是推荐写法。实际读取字符时,通常会判断 EOF

while ((c = getchar()) != EOF)
{
...
}

容易踩坑点:

  • 看到 = 就认为一定语法错误。这里是赋值表达式,语法上合法。

题目:C 语言中只能逐个引用整型数组元素,而不能一次引用整个数组。

答案:Y

解析:

数组不能作为整体直接赋值或整体输出。

例如:

int a[3] = {1, 2, 3};
int b[3];
b = a; // 错误

通常要逐个处理:

for (i = 0; i < 3; i++)
b[i] = a[i];

题目整理:

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

题目:如果有一个字符串,其中第 10 个字符为 '\n',则此字符串的有效字符为 9 个。

答案:N

解析:

'\n' 是换行字符,它仍然是一个普通有效字符。

真正表示字符串结束的是:

'\0'

如果第 10 个字符是 '\0',那么有效字符才是前 9 个。

但题目说的是第 10 个字符为 '\n',所以不能说明字符串有效字符一定是 9 个。

容易踩坑点:

  • '\n''\0' 混淆。

给新手的建议:

  • '\n' 是换行,'\0' 是字符串结束。

功能:一个整数,它加上 100 后是一个完全平方数,再加上 168 后又是一个完全平方数,求这个整数。

整理后的正确代码:

#include <stdio.h>
#include <math.h>
int main(void)
{
long int i;
long int x, y;
for (i = 1; i < 100000; i++)
{
x = (long int)sqrt(i + 100);
y = (long int)sqrt(i + 268);
if (x * x == i + 100 && y * y == i + 268)
printf("\n%ld\n", i);
}
return 0;
}

原卷要求修改的错误:

第一处:

for (i == 1; i < 100000; i++)

应改为:

for (i = 1; i < 100000; i++)

原因:= 是赋初值,== 是判断相等。

第二处:

x = sqrt(i + 100)

应改为:

x = sqrt(i + 100);

原因:语句末尾缺少分号。

第三处:

if (x * x == i + 100 || y * y == i + 268)

应改为:

if (x * x == i + 100 && y * y == i + 268)

原因:题目要求两个条件同时满足:

i + 100 是完全平方数
i + 268 也是完全平方数

所以要用逻辑与 &&,不能用逻辑或 ||

详细解题过程:

对每个 i,先求:

sqrt(i + 100)
sqrt(i + 268)

如果平方根取整后再平方还能等于原数,说明它是完全平方数。

容易踩坑点:

  • === 混用。
  • &&|| 混用。
  • 漏写分号。

给新手的建议:

  • 题目里出现“并且”“同时”,通常用 &&
  • 题目里出现“或者”,通常用 ||

功能:用下面的和式求圆周率的近似值,直到最后一项的绝对值小于等于 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 输出。

容易踩坑点:

  • 用整型变量保存小数项。
  • 循环条件写反。
  • 使用 fabs 却没有包含 <math.h>
  • 浮点数输出格式写成 %d

给新手的建议:

  • 级数题先看每一项怎么变,再看什么时候停止。

功能:若 xy 为奇数,求 xy 之间的奇数和;若 xy 为偶数,则求 xy 之间的偶数和。

参考答案:

#include <stdio.h>
int fun(int x, int y)
{
int i, s = 0;
for (i = x; i <= y; i += 2)
s += i;
return s;
}
int main(void)
{
int s;
s = fun(1, 1999) - fun(2, 1998);
printf("s=%d\n", s);
return 0;
}

详细解题过程:

这道题默认传入的 xy 同奇偶:

  • fun(1, 1999):从 1 开始每次加 2,求所有奇数和。
  • fun(2, 1998):从 2 开始每次加 2,求所有偶数和。

核心循环是:

for (i = x; i <= y; i += 2)
s += i;

为什么每次加 2

因为奇数到下一个奇数差 2

1, 3, 5, 7, ...

偶数到下一个偶数也差 2

2, 4, 6, 8, ...

本题主函数最终计算:

1 到 1999 的奇数和 - 2 到 1998 的偶数和

结果是:

1000

容易踩坑点:

  • 循环写成 i++,这样会把奇偶都加进去。
  • 忘记初始化 s = 0
  • 忘记 return s;

给新手的建议:

  • 如果要只遍历奇数或偶数,循环变量通常每次加 2

功能:编写函数 fun,将两个两位正整数 ab 合并形成一个整数放在 c 中。

合并规则:

  • a 的十位放到 c 的个位。
  • a 的个位放到 c 的十位。
  • b 的十位放到 c 的百位。
  • b 的个位放到 c 的千位。

例如:

a = 16
b = 35
c = 5361

参考答案:

#include <stdio.h>
void fun(int a, int b, long *c)
{
*c = (b % 10) * 1000 + (b / 10) * 100 + (a % 10) * 10 + a / 10;
}
int main(void)
{
int a = 16, b = 35;
long c;
fun(a, b, &c);
printf("c=%ld\n", c);
return 0;
}

详细解题过程:

先拆 a = 16

a / 10 = 1 // 十位
a % 10 = 6 // 个位

再拆 b = 35

b / 10 = 3 // 十位
b % 10 = 5 // 个位

按照题目要求组合:

来源放到 c 的位置对应权值
b % 10千位*1000
b / 10百位*100
a % 10十位*10
a / 10个位*1

所以:

*c = (b % 10) * 1000 + (b / 10) * 100 + (a % 10) * 10 + a / 10;

代入 a = 16, b = 35

*c = 5 * 1000 + 3 * 100 + 6 * 10 + 1
= 5000 + 300 + 60 + 1
= 5361

为什么写 *c

因为形参 c 是指针:

long *c

要修改它指向的变量,必须写:

*c = ...

容易踩坑点:

  • 拆个位要用 % 10,拆十位要用 / 10
  • 忘记乘以 100010010 放到对应位。
  • 形参是指针时,赋值要写 *c = ...

给新手的建议:

  • 数字拆位组合题,一定先画表:哪个数字的哪一位,放到结果的哪一位。

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

  • 函数调用的实参个数只数最外层逗号。
  • 函数可以嵌套调用,但不能嵌套定义。
  • 老式 C 中函数不写返回类型默认 int,但现代写法要明确返回类型。
  • 数组下标从 0 开始,初始化列表从 a[0] 开始。
  • 二维数组第二维不能省略。
  • 字符串遇到第一个 '\0' 就结束。
  • '\n' 是换行字符,不是字符串结束符。
  • | 是按位或,|| 是逻辑或。
  • && 表示两个条件同时满足,|| 表示满足其中一个。
  • 普通局部变量默认存储类别是 auto
  • 把选择题第 1、2、5、7、14、15、18、25 题放在一起复习,它们都和函数有关。
  • 把选择题第 3、4、8、10、11、19、20、21、23、24 题放在一起复习,它们都和数组有关。
  • 把选择题第 9、12、13、17、22 题放在一起复习,它们都和字符串、字符数组有关。
  • 填空题第 7 题建议手写二进制位运算过程,特别区分 |||
  • 判断题第 9 题建议自己写一段 switch 程序,分别试试有 break 和没有 break 的区别。
  • 改错题第 1 题重点看 ===&&|| 的区别。
  • 程序设计第 2 题建议你自己换几个两位数,比如 a=24, b=68,手算再跑代码验证。

第 83 套卷子虽然题目看起来分散,但核心还是基础细节:

  • 函数:调用形式、返回类型、形参、实参、作用域。
  • 数组:定义、初始化、下标范围、二维数组行列计算。
  • 字符串:'\0'、空间大小、前 N 个字符输出。
  • 表达式:自增、自减、复合赋值、位运算、逗号表达式。
  • 程序题:拆位、组合、奇偶求和、完全平方数判断。

复习时不要只背红字答案。你可以把每道题后面的“容易踩坑点”当作检查清单:如果能说清楚它想考你哪个坑,这套题就真正吃透了。