C语言笔试 最终版

题型分布

一、选择题(共20小题,每小题1分,共20分)

二、填空题(共10空,每空1分,共10分)

三、判断题(共10小题,每小题1分,共10分)

四、程序阅读题(共5小题,每小题4分,共20分)

五、算法设计题(共2小题,每小题8分,共16分)

六、程序设计题(共2小题,每小题12分,共24分)

一、选择题(共20小题,每小题1分,共20分)

1、考察基础知识

题目:下列叙述中正确的是______。

A、C程序的基本组成单位是语句

B、C语句必须以分号结束

C、C程序中的每一行只能写一条语句

D、C程序必须在一行内写完

  • 解析:
    • C语言中,程序基本组成单位是 函数 **,**基本执行单位是 语句 ,以分号结尾。

题目:可以将用高级语言编写的源程序转换为目标程序的程序是______。

A. 汇编程序 B. 解释程序 C. 编辑程序 D. 编译程序

  • 解析:
    • c语言是编译型语言,python是解释型语言。

2、考察标识符

题目下列选项中,不能用作c语言标识符的是_____

A、print B、FOR C、&a D、_00

  • 解析:
    • 只能是字母数字下划线,"&"不合法。

3、考察格式化输出与 ASCII 码

题目:已知字符 a 的 ASCII 码为 97,执行语句 printf("%c%d", 'b', 'b'+1);后输出结果是________

A、b99 B、98c C、9899 D、bc

  • 解析
    • 'b' 的 ASCII 码是 98(因为 'a' 是 97,'b' 紧随其后)。
    • 第一个格式符 %c 对应参数 'b':输出字符形式 b
    • 第二个格式符 %d 对应参数 'b'+1:即 98 + 1 = 99%d 要求输出整数形式,所以输出 99
    • 拼接起来就是 b99

**题目:**已知字符 a 的 ASCII 码为 97,执行语句 printf("%d,%c", 'b', 'b'+1);后输出结果是________

A、98,b B、语句不合法 C、98,99 D、98,c

4、考察字符串常量

**题目:**表达式sizeof("key")的值是________

A、1 B、2 C、3 D、4

  • 解析
    • 0 1 2 3
    • k e y \0

5、考察数学库

**题目:**要调用数学函数时,在#include命令行应该包含 ________

A、stdio.h B、string.h C、math.h D、ctype.h

6、考察运算符

**题目:**若变量已正确定义,则语句if(a>b) k=0; else k=1和_______ 等价。

A、k=(a>b)?1:0;

B、k=a>b;

C、k=a≤b;

D、a≤b?0:1;

  • 解析
    • 列出一个逻辑表
      • 情况 1:当 a > b 成立(真)时 \(\rightarrow\) k 被赋值为 0
      • 情况 2:当 a > b 不成立(假)时 \(\rightarrow\) k 被赋值为 1
    • 逐个分析选项
      • 选项 A:k=(a>b)?1:0;
        • 含义:这是三目运算符。如果 a>b 为真,取冒号前面的值(1);否则取冒号后面的值(0)。
        • 结果:真 \(\rightarrow\) 1,假 \(\rightarrow\) 0。
        • 结论:这与原题目的逻辑刚好相反
      • 选项 B:k=a>b;
        • 含义:直接将关系表达式的结果赋值给 k。
        • C语言规则:关系运算(如 >)的结果只有两种:真为 1,假为 0。
        • 结果
          • 如果 a > b 为真,表达式值为 1 \(\rightarrow\) k = 1
          • 如果 a > b 为假,表达式值为 0 \(\rightarrow\) k = 0
        • 结论:这也与原题目逻辑相反(同 A 选项)。
      • 选项 C:k=a<=b;
        • 含义:判断 a 是否小于等于 b,并将结果(1或0)赋值给 k
        • 逻辑转换
          • 当原题条件 a > b 成立(真)时 \(\rightarrow\) 它的反面 a <= b就不成立(假,值为 0)。此时 k = 0(符合原题)
          • 当原题条件 a > b 不成立(假)时 \(\rightarrow\) 它的反面 a <= b就成立(真,值为 1)。此时 k = 1(符合原题)
        • 结论:完全等价。
      • 选项 D:a<=b?0:1;
        • 含义:这是一个合法的 C 语言表达式,计算出了结果。
        • 致命伤没有赋值动作! 也就是没有写 k = ...。算出结果后,没有把值给变量 k,所以这条语句执行完,k 的值不会发生改变。
        • 结论:不等价。

**题目:**表达式 x&&1 等价于______。

A. x0 B. x1 C. x!=0 D. x!=1

  • 解析:
    • 运算符&& 是逻辑与运算符。
    • 规则“同真才真,一假则假”。只有当两个操作数都为真(非0)时,结果才为 1;否则为 0。
    • 操作数分析
      • 右边的操作数是 1:这是真值(True)。
      • 左边的操作数是 x:这是变量。
    • 因为右边已经是了,所以整个表达式的结果完全取决于 x
    • 如果 x 是真(非0),则 真 && 真 \(\rightarrow\) 结果为 1 (真)
    • 如果 x 是假(0),则 假 && 真 \(\rightarrow\) 结果为 0 (假)
    • 结论: 这个表达式的作用就是判断 “x 是不是真的”(即 x 是不是非 0)。
image-3160d407

7、考察自增自减

**题目:**已知有声明int x,a=3,b=2;,则执行赋值语句 x=a>b++?a++:b++后,变量x、a、b的值分别为_____ 。

A、3 4 3

B、3 3 4

C、3 3 3

D、4 3 4

  • 解析
    • x=a>b++?a++:b++

    • step1: a>b? → 3>2 → true

             b++   →  b=2+1=3
      
    • step2: x=a++ → x=a=3

             a++ →  a=3+1=4
      
    • step3: x=3 a=4 b=3

题目:

int a=5;
int b,c;
b=++a;
c=a++;

a, b, c的值最终是多少?
++a  a=6
b=a=6
c=a=6
a++ a=7

a=7 b=6 c=6

题目:

int a=0,b=3,c=2;
if(a++&&b++)
	c--;
else if(a+1==6 || b--)
	c++;

a、b、c的值最终是多少?
&& 且 有0则0 左边为0 直接不看右边
if(a++&&b++)    ->   if(0&&b++)       ->   if(0)
	c--;                              不执行                    不执行
a用完后++     a=0+1=1

|| 且 有1则1 左为1 不看右边
else if(a+1==6 || b--)        1+1==6 ×      (0 || 3) ->  true (1)
b用完后--  b=3-1=2
执行里面 c++   c=2+1=3

a=1 b=2 c=3
image-f390c144

8、考察 if 语句的作用范围与逗号表达式

题目:定义 int x=4,y=3,z=5;,下列四个程序段中有 3 个程序段的执行结果相同,另一个执行结果不同的是________

A、if(x>y) z=x,x=y,y=z;

B、if(x>y) { z=x,x=y,y=z;}

C、if(x>y) z=x;x=y;y=z;

D、if(x>y) {z=x;x=y;y=z;}

  • 解析
    • x > y4 > 3,条件为真。
    • A: if(x>y) z=x,x=y,y=z; —— 逗号表达式 z=x,x=y,y=z 被视为一条语句。因为条件为真,这三个赋值都会执行。
    • B: if(x>y) { z=x,x=y,y=z;} —— 花括号内包含一条逗号表达式语句,效果同 A,全部执行。
    • D: if(x>y) {z=x;x=y;y=z;} —— 花括号内包含三条独立语句,因为有花括号包裹,全部受 if 控制,全部执行。
    • C: if(x>y) z=x; x=y; y=z; —— 陷阱! if只能控制紧跟其后的第一条语句(即 z=x;)。后面的 x=y;y=z;if 无关,无论条件真假都会执行。
    • 虽然在本题具体数值下,条件为真,看起来大家都执行了。但从程序逻辑结构来看,C 的控制范围与其他三项不同(如果 x>y为假,A/B/D 什么都不做,而 C 依然会执行后两句赋值)。

9、考察do-while 循环

题目:程序段 int n=3; do{ printf("%d",n--);}while(!n); 的执行结果是________

A、3 2 1 B、死循环 C、2 D、3

  • 解析
    1. 第一次执行循环体:打印 n (3),然后 n自减变为 2。
    2. 判断条件while(!n)while(!2)。在 C 语言中,2 是真,!2 就是假 (0)。
    3. 结果:条件为假,循环终止。
    4. 所以屏幕上只输出了一个 3

10、考察无符号数

题目:设 int i,a=1; unsigned j;,则下列语句执行会出现无限循环的语句是________

A、for(j=15;j>0;j-=2) a++;

B、for(j=0;j>15;j+=2) a++;

C、for(i=0;i>15;i+=2) a++;

D、for(i=15;i>0;i-=2) a++;

  • 解析
    • A: for(j=15; j>0; j-=2)。注意 junsigned (无符号整数)
      • 序列变化:15, 13, ..., 3, 1。
      • 下一步:1 - 2。在数学上是 -1,但在计算机无符号数运算中,它会变成一个巨大的正整数(通常是 2^32 - 1)。
      • 结果:这个巨大的数依然 > 0,循环条件永远成立,导致死循环。
    • B/C: 初始条件 0 > 15 不成立,一次都不执行。
    • D: iint (有符号)。 1 - 2变成 -1-1 > 0 为假,循环正常结束。

11、考察循环语句的特性

题目:下列关于循环语句 for、while、do-while 的叙述中正确的是________

A、三种循环语句都可能出现无穷循环

B、三种循环语句中都可以缺省循环终止条件表达式

C、三种循环语句的循环体都至少被无条件地执行一次

D、三种循环语句的循环体都必须放入一对花括号中

  • 解析
    • A: 正确。只要条件设置不当(如 while(1)),三种循环都可能死循环。
    • B: 错误。for(;;) 可以缺省条件,但 while()do-while() 的括号内必须有表达式(哪怕是常数 1)。
    • C: 错误。forwhile 是“先判断后执行”,可能一次都不执行。只有 do-while至少执行一次。
    • D: 错误。如果循环体只有一条语句,花括号 {} 可以省略。

**题目:**在c语言中,whiile和do while循环的主要区别是______

A、do while 的循环体至少无条件执行一次

B、do while 允许从外部转到循环体内

C、while的循环控制条件比do while的更严格

D、do while 的循环体不能是复合语句

12、考察数组声明

题目:下列数组声明语句正确的是________

A、int n, a[n];

B、int a[];

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

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

  • 解析
    • A: n是变量且还没有被初始化,不能用来定义数组长度。
    • B:只有在两种情况下可以省略数组长度:
      • 声明的同时进行了初始化:int a[] = {1, 2, 3};(编译器会自动数个数)。
      • 作为函数的形参:void func(int a[])
    • C:越界(初始值过多)
      • 分析: 声明 a[2][3] 意味着这个数组只有 2 行
      • 初始值: {{2}, {1}, {3}} 这里的三个大括号代表试图填充 3 行 数据。
    • D:“行可省,列不可省”。
      • 声明了列数是 3(不可省略,必须告诉编译器一行有多宽)。
      • 省略了行数 []
      • 编译器去数后面的初始值:{2} 是第一行,{1} 是第二行, {3}是第三行。
      • 编译器自动推导出:哦,原来有 3 行
      • 最终等价于:int a[3][3] = {{2,0,0}, {1,0,0}, {3,0,0}};

13、考察函数形参声明

题目:下列关于函数形参的声明中正确的是________

A、int a[][]

B、int a[]

C、int a[]={0}

D、int a[2][]

  • 解析
    • B: int a[] —— 正确。作为函数参数时,一维数组可以不写长度(本质是退化为指针 int *a)。
    • A/D: int a[][]int a[2][] —— 错误。多维数组作为参数,第一维可以省略,但后面的维度必须写明(例如 int a[][5] 是对的)。编译器需要知道列宽来计算地址。
    • C: int a[]={0} —— 错误。函数形参只是声明,不能在参数列表中进行初始化赋值

题目:下列函数定义的头部中正确的是______。

A. int fun(int a[], b) B. int fun(int a[], int a)

C. int fun(int a, int b) D. int fun( int char a[][], int b)

14、考察构造数据类型

题目:在c语言中,下列属于构造类型的是

A、整型 B、实型 C、指针类型 D、结构体类型

题目:若要使表达式 x++ 无语法错误,则变量 x 不能声明为______。

A. float x; B. long x; C. int *x; D. struct{int y;}x;

15、考察文件写入

题目:下列函数中,可以把整型数据以二进制形式写入文件中的函数是________

A、fputc() B、fwrite()

C、fread() D、fgetc()

  • 解析
    • B: fwrite() —— 专门用于二进制块读写的函数。
    • A: fputc() —— 写入单个字符(文本模式)。
    • C/D: fread() / fgetc() —— 都是读取函数,不是写入。
image-8c905c3f

16、考察文件关闭

题目:当文件被正常关闭时,fclose()函数的返回值是________

A、-1

B、true

C、0

D、1

  • 解析
    • fclose 关闭成功返回 0
    • 关闭失败返回 EOF(通常定义为 -1)。

17、考察宏定义

题目:在宏定义语句 #define PI 3.1415926 中,宏名 PI 的是________

A、一个字符串

B、一个单精度数

C、一个常量

D、一个双精度数

  • 解析
    • 在 C 语言术语中,用 #define 定义的标识符被称为符号常量(Symbolic Constant)。
    • 它不是字符串(没有双引号),也不是变量。虽然它代表一个双精度数,但“符号常量”是更准确的定义分类。
image-ac0a0a00 image-34cf7327

18、考察宏替换

题目:设有定义和声明如下:

#define d 2
int a = 3;
float b = 3.14;
char c = 'd';

以下表达式中有语法错误的是_______

A、x++

B、y++

C、c++

D、d++

  • 解析
    • 宏定义是纯文本替换。预处理阶段会将代码中所有的 d 替换为 2
    • A: x++ -> 5++(变量自增,正确)。
    • B: y++ -> 3.83++(变量自增,正确)。
    • C: c++ -> c 是变量,可以自增。
    • D: d++ -> 被替换为 2++
      • 错误原因:自增运算符 ++ 只能用于变量(左值),不能用于常量或表达式。你不能强迫数字 2 变成 3。
image-30abfe31

注:上面的内容是原题,接下来的内容为推测必考点:

19、考察数组常识

在C语言中,数组下标是从( )开始的。

A. 0

B. 1

C. 2

D. 任意整数

若有定义 int arr[5];,则数组arr的最后一个元素是( )。

A. arr[0]

B. arr[4]

C. arr[5]

D. arr[6]

以下关于二维数组的描述,正确的是( )

A. 二维数组在内存中是按列存储的

B. 二维数组可以看作是一维数组的数组

C. 二维数组的行数和列数必须在编译时确定

D. 二维数组的所有元素必须具有相同的值

image-30e20165

若定义 int a[10], b[3][3]; ,则以下对数组元素赋值的语句中,不会出现越界访问的是( )

A. a[-1]=0 B. a[10]=0 C. b[3][0]=0 D. b[0][3]=0

若定义 int k, a, b; unsigned long y = 5; double x = 1.23; ,则下列表达式中正确的是______。

A. x%(-3) B. x=y[5] b C. k=(a=1,b=2,&a+&b) D. a+=a-=(b=3)*(a=2)

若定义 int a[3][2]={1,2,3,4,5,6}; ,则数组元素______的值为6。

A. a[3][2] B. a[2][1] C. a[1][2] D. a[2][3]

image-2c94fd09

20、考察函数常识

什么是形参,什么是实参?

  • 形参 (Formal Parameter):
    • 定义时括号里的变量。
    • 它是“占位符”,函数未被调用时不占内存,只有调用时才分配内存。
    • 例子: void fun(int a) 里的 a
  • 实参 (Actual Parameter):
    • 调用时括号里传递的具体数值或变量。
    • 它是“实际的数据”,必须有确定的值。
    • 例子: fun(2) 里的 2。k
  • 关系: 发生函数调用时,实参的值会单向复制给形参(值传递)。

以下函数调用语句中有______个实参。

func((a1,a2,a3),(a4,a5));

A.2 B. 5 C. 1 D. 不合法

21、考察格式化输出

执行printf("%x",100);后的输出结果是______。

A. 100 B. 0x100 C. 0X64 D. 64

下列语句中有语法错误的是______。

A. printf("%d",0e); B. printf("%f",0e2); C. printf("%d",0x2); D. printf("%s","0x2");

22、考察表达式运算结果

在C程序中,用______表示逻辑值“真”。

A. 小于0的数 B. 非0的数 C. 非1的数 D. 大于0的数

问题:int k = a > b;
	   k =
a>b -> true -> 1
k=1a>b -> false -> 0
k=0

23、考察switch case

image-c37af5b2 image-bd62b477

24、考察字符串比较函数strcmp()

如何通过strcmp函数比较两个字符串?
strcmp 函数定义在 <string.h> 头文件中,用于 比较两个字符串。

函数原型:
int strcmp(const char *str1, const char *str2);

返回值说明:
返回 0:两个字符串 完全相同
返回 < 0:str1 小于 str2(按 ASCII 码顺序比较)
返回 > 0:str1 大于 str2(按 ASCII 码顺序比较)

注意:strcmp 是区分大小写的。例如 "abc""ABC" 不相等。
image-fae0ef71 image-2e6d0882 image-03e82a37

二、填空题(共10空,每空1分,共10分)

1、考察三目运算符

题目:若定义 int a=5; 则表达式 (a>0 ? a : -a)的值为 5

考点

  • 语法表达式1 ? 表达式2 : 表达式3
  • 解析
    1. 先判断 表达式1 (即 a>0) 是否为真。
    2. 因为 a=5,所以 5>0 成立(为真)。
    3. 表达式2 (即 a) 的值作为整个表达式的结果。
    4. 如果是假,则取 表达式3 (-a)。
    • 注:这其实就是求绝对值的逻辑。

2、考察循环结构

**题目:**若循环结构的循环体至少要执行一次,则最适合描述该循环结构的语句是 do while 语句。

3、考察数组

**题目:**若有数组a,且数组元素a[0]~a[9]的值为9,4,12,8,2,10,7,5,1,3,则:

该数组中,可用的最小下标为 0 ,最大下标为 9

4、考察函数

return 语句被执行时,程序的执行流程无条件地从一个函数跳转到另一个函数。

用于定义外部函数的关键字是 extern ,用于定义内部函数 (静态函数)的关键字是 static

5、考察输入输出

题目:scanf()函数的输入项必须是 地址 ,不能是变量名。

考点

  • scanf 原理scanf 需要修改变量的值,在 C 语言中,要修改一个变量,必须知道它在内存中的位置。
  • 解析
    • 错误写法:scanf("%d", a); (只传了值,scanf 找不到屋子在哪,没法把东西放进去)。
    • 正确写法:scanf("%d", &a);& 取地址符,告诉 scanf 变量 a 的门牌号)。
    • 特例:如果是字符数组(字符串) char str[10], 写 scanf("%s", str) 是对的,因为数组名本身就是地址。

6、考察字符输入输出

题目:getchar()和putchar()函数只能用于 单个 字符的输入和输出。

考点

  • 函数功能差异
    • printf/scanf:格式化,可以处理各种类型,可以处理多个。
    • puts/gets:专门处理字符串(一串字符)。
    • putchar/getchar:专门处理单个字符 (char)。
  • 解析getchar 一次只读一个字, putchar一次只打一个字。

7、考察逻辑运算与算术运算

题目:若定义“int m=23,n=5;”,则表达式 !m+m%n 的值是 3

考点

  • 运算符优先级! (逻辑非) > %(取余) > + (加法)。
  • 逻辑值的数值化:真为 1,假为 0。
  • 解析
    1. 先算 !mm是 23 (非 0 即为真)。对“真”取反就是“假”。在 C 语言中,假的值是 0。所以 !m变成了 0
    2. 再算 m%n23 除以 5,商 4 余 3。所以 m%n3
    3. 最后算加法0 + 3 = 3

**题目:**若定义“int x=33,y=4;”,则表达式 !x+x/y 的值是 8

解析:

!330
0 + x / y
33 / 48
0 + 8
0 + 8 = 8

8、考察结构体

题目:结构体的每个成员的数据类型可以是基本数据类型,也可以是 构造 类型。

考点

  • 数据类型分类
    • 基本类型:int, float, char 等。
    • 构造类型:数组、结构体 (struct)、共用体 (union)。
  • 解析:结构体里可以套娃。比如“学生”结构体里,可以有一个成员是“生日”,而“生日”本身又是一个包含年月日的小结构体。

9、考察sizeof

题目:使用sizeof()函数计算结构体List的长度的表达式是 sizeof(struct List)

考点

  • sizeof 用法sizeof 是运算符,不是函数(虽然长得像)。
  • 语法sizeof(类型名)sizeof(变量名)
  • 解析
    • 题目说“结构体 List”,通常指的是 struct List { ... };
    • 在 C 语言中,引用这个类型时必须带上 struct 关键字,即 struct List
    • 所以计算大小时写 sizeof(struct List)
image-38c166a9 > 注:上面的内容是原题,接下来的内容为推测必考点:

10、考察基础知识

程序设计语言的基本功能包括数据表达与 数据处理

算法应具备如下几个特征:确定性、有穷性、可行性和 一个或多个输出

C语言中共有 32 个关键字。

一个完整的C程序可以有多个函数,其中有且只能有一个 main() 函数。C程序总是从 main ()函数 开始执行。

字符(char)是 1,短整(short)是 2。 整型(int)、单浮点(float) 都是 4。 双浮点(double) 是 8

任何程序的结构都可以通过3种基本结构进行组合来实现,分别是 顺序结构选择结构循环结构

11、考察输入输出

printf函数调用时,格式说明符数量与实际提供的参数数量不一致时会发生什么?

格式说明符少于参数
int a = 10, b = 20;
printf("%d", a, b);
%d 只有一个说明符,但提供了两个参数

结果:
只使用第一个参数 a
b 被忽略
不会报错,也不会打印 b

执行printf("%d;%d",a,b,c,d);后屏幕上将输出 2 个整数。

已知有声明和语句int a;scanf("a=%d",&a);,欲使从键盘上为变量a输入的值为2,则正确的输入应是 a=2

格式修饰符都有哪些?
%d, %3d, %u, %x, %#x, %ld, %f, %lf, %4.2f, %c, %s的含义分别是什么?
%d
	含义:输出十进制有符号整数(int)

%3d
	含义:输出宽度至少为 3 的十进制整数,右对齐
	若数字不足 3 位,左边补空格
	若超过 3 位,直接原样输出

%u
	含义:输出无符号十进制整数(unsigned int)
	同一块内存,按“无符号”方式解释

%x
	含义:以十六进制输出整数(小写字母 a–f)
	仅影响显示,不改变变量的值

%#x
	含义:以十六进制输出,并自动加前缀 0x
	字母仍为小写

%ld
	含义:输出 long int 类型的十进制整数
	用于 long 型整数

%f
	含义:输出浮点数(float / double)
	默认 保留 6 位小数

%lf
	含义:
		printf 中:等价于 %f
		scanf 中:表示读入 double

%4.2f
	含义:
		总宽度至少为 4
		小数点后保留 2 位
		默认右对齐

%c
	含义:输出一个字符
	对应 char 类型或字符常量

%s
	含义:输出字符串(以 \0 结尾的字符数组)
	不输出 \0

printf("%c,%d", 'A', 'A');
%c
'A' 作为字符输出
输出字符:A

%d
'A' 实际是字符常量
在 C 语言中,字符本质是整数(ASCII 码)
'A' 的 ASCII 值是 65

输入输出的格式控制字符中,int型数据采用 %d ,float型数据采用 %f ,char型数据采用 %c

printf(“%c,%d”, ‘A’,’A);
实际输出结果:
A,65

12、考察字符数组

以下代码段的输出结果是 China

char ch[20] = {"China\0Beijing"};

printf("%s", ch);

sizeof(“hello”)=?
6
末尾有\0

13、考察指针(应该不考 看不懂可跳过)

第 1 题:指针与变量别名

int k=1,j=2;
int *p=&k, *q=&j; // p 指向 k,q 指向 j
(*p)++;           // 1. 取 p 指向的值(k)并自增。k 从 1 变成 2。
k=j;              // 2. 把 j 的值(2)赋给 k。此时 k 还是 2。
(*p)++;           // 3. 取 p 指向的值(k)并自增。k 从 2 变成 3printf("%d, %d",k,j);

结果: 3, 2

C

int k=1,j=2;
int *p=&k, *q=&j; // p 指向 k,q 指向 j
(*p)++;           // 1. 取 p 指向的值(k)并自增。k 从 1 变成 2。
k=j;              // 2. 把 j 的值(2)赋给 k。此时 k 还是 2。
(*p)++;           // 3. 取 p 指向的值(k)并自增。k 从 2 变成 3printf("%d, %d",k,j);

核心考点:

  • *p 就是 k 的替身。动 *p就是动 k
  • qj 在这道题里其实是打酱油的, j的值全程没变,一直是 2。

第 2 题:循环陷阱与指针移动

结果: 0BC

代码拆解: 第一部分:分号陷阱

int k; char *s="ABC";
for(k=10; k!=0; k--);  // ⚠️ 注意这里有个分号!
printf("%d",k);

C

int k; char *s="ABC";
for(k=10; k!=0; k--);  // ⚠️ 注意这里有个分号!
printf("%d",k);
  • 这个 for 循环后面直接跟了分号,说明循环体是的。
  • 它会一直空转,直到 k 减到 0 为止。
  • 所以 printf 输出的是 0

第二部分:指针的“吃字”现象

while(*s++) putchar(*s);

C

while(*s++) putchar(*s);

这是一个非常经典的逻辑陷阱:

  1. 判断阶段:先取 *s 的值(也就是 'A')来判断循环条件(真)。
  2. 自增阶段:判断完瞬间,s++ 生效,指针向后移一位,指向了 'B'。
  3. 执行阶段:执行 putchar(*s)。注意!此时 s已经指向 'B' 了。
    • 所以第一次输出的是 B('A' 被跳过了)。
  4. 下一次循环
    • 判断:取 *s ('B') 为真。
    • 自增:s 移向 'C'。
    • 执行:输出 C
  5. 再下一次
    • 判断:取 *s ('C') 为真。
    • 自增:s 移向 '\0' (结束符)。
    • 执行:输出结束符(看不见)。
  6. 最后
    • 判断:取 *s ('\0') 为假,循环结束。

所以最终连起来是 0 B C

第 3 题:全局变量、传值与传址

结果: 4,3,4,2,7--1,5,4,1,6

这道题必须分步骤跟踪内存。 全局变量初始状态: x=5, y=2, z=0

第一步:p(&x, y) 调用

  • 传参
    • 形参 *x 指向全局 x (5)。(传址)
    • 形参 y 是全局 y 的副本,值为 2。(传值)
  • 函数内执行
    1. --*x; -> 全局 x 减 1,变为 4
    2. y++; -> 局部副本 y 加 1,变为 3 (不影响全局 y)。
    3. z=*x+y; -> 全局 z = 全局 x(4) + 局部 y(3) = 7
    4. printf: 输出 *x(4) 和 y(3)。
    • 函数内输出:4, 3,

第二步:main 函数中间的 printf

  • 此时回到主函数,看全局变量:
    • x 刚才被改成了 4
    • y 刚才没被改(传的是值),还是 2
    • z 刚才被改成了 7
  • 主函数输出:4, 2, 7--

第三步:p(&y, x) 调用

  • 传参
    • 形参 *x 指向全局 y (2)。(注意!这次指针指着 y)
    • 形参 y 是全局 x 的副本,值为 4。(传值)
  • 函数内执行
    1. --*x; -> 指针指向的是全局 y,所以全局 y减 1,变为 1
    2. y++; -> 局部副本 y (也就是那个4) 加 1,变为 5
    3. z=*x+y; -> 全局 z = 全局 y(1) + 局部 y(5) = 6
    4. printf: 输出 *x(1) 和 y(5)。
    • 函数内输出:1, 5,

第四步:main 函数最后的 printf

  • 看全局变量最终状态:
    • x 没变,还是 4
    • y 被改成 1 了。
    • z 被改成 6 了。
  • 主函数输出:4, 1, 6

三、判断题(共10小题,每小题1分,共10分)

注:这部分没明确暗示,把一些没归类好的但90%考的整理进来:

long long 占8字节 使用%lld格式化输出

unsigned long long 使用%llu格式化输出

switch语句中,各个case和default的出现次序影响执行结果吗? 不影响

函数调用之前是否需要先声明?

  • 原则上需要。 编译器必须先认识这个函数(知道它的名字、参数类型、返回值类型),才能检查调用是否合法。
  • 特例: 如果函数的定义(完整代码)写在调用它的位置之前,那么定义本身就充当了声明,不需要额外写声明语句。

是否可以调用 main 函数?

  • 在 C 语言中:可以。 main 函数本质上也是一个函数,可以在程序中递归调用自己(虽然在实际开发中极少这样做,容易死循环)。
  • (注:在 C++ 中是禁止调用 main 函数的,但 C 语言考试通常认为可以)

c语言本身提供输入输出语句吗? C 语言的“语言本身”不提供输入输出语句;输入输出是由标准库提供的。

  • C 语言核心语法(如变量、表达式、控制结构、函数定义等)中没有内建的 I/O 功能

    也就是说,像 printfscanf 这样的函数不是语言内置语句。

  • I/O 由 C 标准库提供

    标准库 <stdio.h> 中定义了常用的输入输出函数,例如:

    • printf():格式化输出到标准输出
    • scanf():从标准输入读取格式化数据
    • putchar()getchar():字符输入输出
    • fopen()fread()fwrite()等:文件操作
  • 所以:C 语言的 I/O 是“库函数”,而不是“语言关键字或语句”

    这与一些语言(如 Python 的 print 是关键字或内建函数)不同。

C语言为文件的操作提供了一系列标准函数,它们定义在stdio.h中,因此进行文件操作时需要使用文件包含命令 #include<stdio.h> 。文件操作涉及下列一些函数。

(1) 字符读写函数:fputc() 和 fgetc() 。

(2) 字符串读写函数: fputs() 和 fgets() 。

(3) 格式化读写函数: fscanf() 和 fprintf() 。

(4) 二进制读写函数: fread() 和 fwrite() 。

四、程序阅读题(共5小题,每小题4分,共20分)

1、考察字符型变量(ASCII码 大小写转换)

非真题:(但由书上两道可能是真题的题目合成)

#include <stdio.h>

int main()
{
    char c1, c2;

    // 步骤 1:初始化
    c1 = 65;     // 考点 A:直接赋整数值
    c2 = 'b';    // 考点 A:赋字符形式

    // 步骤 2:运算(大小写转换)
    c1 = c1 + 32;  // 考点 B:大写转小写
    c2 = c2 - 32;  // 考点 B:小写转大写

    // 步骤 3:输出
    printf("%c, %d\n", c1, c1);
    printf("%c, %d\n", c2, c2);

    return 0;
}

初始化的本质(考点 A)

  • c1 = 65;
    • 内存视角: 计算机在 c1 里存入二进制的 65
    • 理解: 虽然赋值的是整数,但在 ASCII 码表中,65 对应的就是大写字母 'A'
  • c2 = 'b';
    • 内存视角: 计算机查表找到 'b' 的 ASCII 码是 98,存入二进制的 98。

运算逻辑(考点 B)

  • c1 = c1 + 32;
    • 65 + 32 = 97
    • 在 ASCII 码性质中,大写字母 + 32 = 对应的小写字母
    • 此时 c1 变成了 97(即字符 'a')。
  • c2 = c2 - 32;
    • 98 - 32 = 66
    • 在 ASCII 码性质中,小写字母 - 32 = 对应的大写字母
    • 此时 c2 变成了 66(即字符 'B')。

输出格式

  • 第一行 printf("%c, %d", c1, c1)
    • %c 输出 c1 (97) 对应的字符 \(\rightarrow\) a
    • %d 输出 c1 (97) 对应的数值 \(\rightarrow\) 97
  • 第二行 printf("%c, %d", c2, c2)
    • %c 输出 c2 (66) 对应的字符 \(\rightarrow\) B
    • %d 输出 c2 (66) 对应的数值 \(\rightarrow\) 66

答案:

a, 97
B, 66

2、考察 Sizeof 与 运算符优先级

非真题:(但由书上两道可能是真题的题目合成)(可能不考这个 考13456五道)

 #include <stdio.h>

int main()
{
    int a, b;

    // 考点 A:混合运算的类型提升(括号内)
    // 3(int) + 5.0(double) -> 结果类型为 double
    a = sizeof(3 + 5.0);

    // 考点 B:优先级与隐式转换(无括号)
    // sizeof 3 先算,结果再加 5.0,最后赋值给 int b
    b = sizeof 3 + 5.0;

    // 考点 C:基本数据类型与字符串(\0)
    // sizeof(short) vs sizeof("China")
    printf("%d, %d, %d, %d\n", sizeof(short), a, b, sizeof("China"));

    return 0;
}
  1. 基本类型的长度 sizeof(short)
  • 分析:这是最基础的死记硬背考点。
  • 规则short(短整型)在标准环境下通常占用 2 个字节。
  • 结果:输出 2
  1. 类型提升陷阱 a = sizeof(3 + 5.0)
  • 分析sizeof 计算的是括号内表达式运算结果的数据类型长度。
  • 过程
    • 3 是整数 (int)。
    • 5.0 默认是双精度浮点数 (double)。
    • 规则:C 语言运算时,"小类型"往"大类型"靠。int + double \(\rightarrow\) double
    • double 类型占用 8 个字节。
  • 结果:输出 8
  1. 优先级陷阱 b = sizeof 3 + 5.0
  • 分析:这里没有括号,拼的是优先级。
  • 过程
    1. 优先级sizeof 运算符的优先级 高于 +号。
    2. 第一步:先算 sizeof 3。因为 3 是 int,所以 sizeof 3 等于 4
    3. 第二步:表达式变成了 4 + 5.0。结果是 9.0(double)。
    4. 第三步:赋值给 int b。浮点数 9.0被强制截断为整数 9
  • 结果:输出 9
  1. 字符串陷阱 sizeof("China")
  • 分析:肉眼看到的字符数 vs 实际内存占用。
  • 过程
    • 可见字符:C-h-i-n-a (5个)。
    • 规则:字符串常量在内存结束处必须有一个隐形的结束标记 \0
    • 计算:5 + 1 = 6。
  • 结果:输出 6

答案:

2, 8, 9, 6

3、考察嵌套循环与 Continue

大概率是原题

#include<stdio.h>

int main()
{
    int i, j, n = 0;
    for(i = 0; i < 2; i++)
    {
        n++;
        for(j = 0; j <= 3; j++)
        {
            if(j % 2) continue;
            n++;
        }
        n++;
    }
    printf("n = %d\n", n);
    return 0;
}
  • 考点:逻辑流程控制,continue 跳过谁, n++执行了几次。

  • 解析

    • 外层 i=0: n变成1。进入内层。
    • 内层 j=0: 偶数不continue,n变成2。
    • 内层 j=1: 奇数continue (跳过n++)。
    • 内层 j=2: 偶数不continue,n变成3。
    • 内层 j=3: 奇数continue。
    • 内层结束。n变成4 (外层最后还有个n++)。
    • 外层 i=1: n变成5。重复一遍内层逻辑, n加3,变成8。
  • 答案

    n= 8
    
image-18662853 image-53131d15 image-201cf0fd

可以尝试掌握:(但大概率不考 就是考上面那题)

#include<stdio.h>
int main()
{
    int n=5;
    do
    {
        switch(n%2)
        {
			case 0:
				n--;
				break;
			case 1:
				n--;
				continue;
		}
        n--;
        printf("%2d",n);
    }while(n>0);
    return 0;
}

初始状态: n = 5

第一轮循环 (n=5)

  1. 进入 do
  2. switch(5 % 2) \(\rightarrow\) 结果是 1 \(\rightarrow\) 命中 case 1
  3. Case 1 执行:
    • n--:n 变为 4
    • continue【关键点】 直接跳到 while 判断处!
    • (后面的 n-- printf 全部被跳过,没执行)
  4. While 判断: 4 > 0 成立。继续循环。

第二轮循环 (n=4)

  1. 进入 do
  2. switch(4 % 2) \(\rightarrow\) 结果是 0 \(\rightarrow\) 命中 case 0
  3. Case 0 执行:
    • n--:n 变为 3
    • break:跳出 switch,继续往下走。
  4. Switch 之后:
    • n--:n 变为 2
    • printf("%2d", 2)【输出】 2
  5. While 判断: 2 > 0 成立。继续循环。

第三轮循环 (n=2)

  1. 进入 do
  2. switch(2 % 2) \(\rightarrow\) 结果是 0 \(\rightarrow\) 命中 case 0
  3. Case 0 执行:
    • n--:n 变为 1
    • break:跳出 switch,继续往下走。
  4. Switch 之后:
    • n--:n 变为 0
    • printf("%2d", 0)【输出】 0
  5. While 判断: 0 > 0 不成立。循环结束。

答案

2 0

难点:

  • break switch 中:
    • 作用:仅仅跳出 switch 语句块。
    • 后果:程序继续向下执行 switch 后面的代码(即 n--printf)。
  • continue switch 中(且在循环里):
    • 作用:直接作用于外部的循环(do-while)。
    • 后果:它不仅跳出 switch,还跳过了 switch 后面所有的代码(跳过了外面的 n--printf),直接飞奔去进行 while(n>0) 的条件判断!

4、考察函数递归

大概率是原题

#include<stdio.h>

void fun(int a,int b)
{
    if(a >= b)
        printf("%d",a);  // 【位置 A】
    else
        fun(a+1, b);     // 【位置 B】递归调用

    printf("%d",a);      // 【位置 C】这是公用的代码!无论 if 还是 else,最后都会执行这一句!
}

int main()
{
    fun(1,2);
    return 0;
}

第一层:main 调用 fun(1, 2)

  • 参数a=1, b=2

  • 判断if(1 >= 2) 不成立。

  • 执行 else:进入 【位置 B】,调用下一层递归 fun(2, 2)

    • (注意:此时第一层函数暂停在这里,等待第二层运行结束,它手里还捏着最后的 【位置 C】 没执行)

    第二层:递归调用 fun(2, 2)

    • 参数a=2, b=2
    • 判断if(2 >= 2) 成立!
    • 执行 if:进入 【位置 A】,打印 2
      • 当前屏幕:2
    • 继续执行if 语句结束,程序继续向下走。
    • 执行末尾:到达 【位置 C】,打印 2
      • 当前屏幕:22
    • 结束:第二层函数运行完毕,返回上一层。

回到第一层:fun(1, 2)

  • 继续:刚才在 【位置 B】 等待的递归调用结束了。
  • 继续向下:程序走到最后的 【位置 C】。
  • 此时的 a:在这一层,a 仍然是 1
  • 执行末尾打印 1
    • 当前屏幕:221
diagram-1767590037591-b91b5d69

答案:

221
image-216a36db

练习:

#include<stdio.h>
void fun(int a)
{
    if(!a)
        printf("%d",a);
    else
        fun(a-1);
    printf("%d",a);
}
int main()
{
    fun(2);
    return 0;
}
0012
image-03241012 image-d6571bf8

5、考察共用体Union

大概率是原题

#include<stdio.h>

int main()
{
    union { char c; int i; } s;
    s.c = 'A'; // 内存里写入 65 (0x41)
    s.i = 2; // 内存里写入 2。因为共用地址,'A' 被覆盖成了 2。
    printf("%d%d", s.c, s.i);
    return 0;
}
  • 共用起始地址
    • char cint i 并不是分开存放的,而是从同一个内存地址开始存放
    • 可以把共用体想象成一个屋子,同一时间只能住一个人。
  • 覆盖过程(Timeline)
    • s.c = 'A':内存的第 1 个字节被写入了 65('A'的ASCII码)。
    • s.i = 2:这里是关键! int 占用 4 个字节(包含第 1 个字节)。当你写入整数 2 时,系统会将这 4 个字节全部重写(通常是 00000010 00000000... 二进制)。
    • 结果:原本那个位置的 'A' (65) 瞬间被整数 2 的低位数据覆盖掉了。
  • 输出读取
    • s.c:去读第 1 个字节 \(\rightarrow\) 发现是 2
    • s.i:去读整个 4 字节 \(\rightarrow\) 发现是 2
    • 所以输出 22,即 22

答案:

22
image-a86f6ce8

6、考察宏定义

大概率是原题

#include<stdio.h>
#define LAB1 0
#define LAB2 2
#include<stdio.h>

void main()
{
    int temp;
    #ifdef LAB1
        temp=LAB1;
    #else
        temp=LAB2;
    #endif
    printf("%d",temp);
}
  • 区分 #ifdef #if
    • #if LAB1:这是在判断 真假。如果代码写的是这个,因为 LAB1是 0(假),确实会跳到 else
    • #ifdef LAB1:这是在判断 存在性(Is Defined?)。它完全不关心 LAB1 的值是 0、1 还是 100,它只关心:“你在前面通过 #define 提到过 LAB1 这个名字吗?”
  • 执行逻辑
    • 第一行 #define LAB1 0 已经“注册”了 LAB1 这个名字。
    • 所以在 #ifdef LAB1 时,判定结果为 “存在”(真)。
    • 编译器会保留 temp = LAB1; 这一行代码。
    • temp = 0;
  • 总结
    • 只要 #define 出现过,#ifdef 就成立。哪怕你定义的是 #define LAB1(后面什么都不写),它也是成立的。

答案:

0
image-fde6de95

五、算法设计题(共2小题,每小题8分,共16分)

1、考察二维数组与循环

大概率是真题:

编程计算n×n的矩阵的两条对角线上所有元素(各元素数值任意)之和

#include <stdio.h>
#define N 5  // 【改这里】定义矩阵大小,如果是5就是5x5,如果是3就是3*3

int main()
{

	int a[N][N] = {
        {1, 1, 1, 1, 1},
        {1, 1, 1, 1, 1},
        {1, 1, 5, 1, 1},
        {1, 1, 1, 1, 1},
        {1, 1, 1, 1, 1}
    };

//    int a[N][N] = {
//    	{9, 4, 7},
//    	{5, 6, 1},
//    	{8, 3, 2}
//    };

    int sum = 0, i, j;

    printf("%d x %d 数组:\n", N, N);

    // 输出矩阵部分
    for(i = 0; i < N; i++)
    {
        for(j = 0; j < N; j++)
            printf("%3d", a[i][j]);
        printf("\n");
    }

    // 计算求和部分
    for(i = 0; i < N; i++)
    {
        for(j = 0; j < N; j++)
        {
            // 核心公式
            // 1. i == j 是主对角线
            // 2. i + j == N - 1 是副对角线 (3x3时是2, 5x5时是4)
            if(i == j || i + j == N - 1)
            {
                sum = sum + a[i][j];
            }
        }
    }

    printf("对角线元素之和为:%d\n", sum);
    return 0;
}

2、考察字符串与指针

大概率是真题

编程使用字符指针将26个英文字母以每行10个的形式输出

#include<stdio.h>

int main()
{
    char *str1, *str2;
    str1 = "abcdefghijklmnopqrstuvwxyz";

    // 遍历字符
    for(str2 = str1; str2 < str1 + 26; str2++)
    {
        printf("%c ", *str2);

        // 判断是否换行
        // str2-str1 得到的是当前字符的下标(0-25)
        // +1 是为了变成第几个字符(1-26)
        if((str2 - str1 + 1) % 10 == 0)
            printf("\n");
    }
    return 0;
}

六、程序设计题(共2小题,每小题12分,共24分)

1、考察流程图(循环)

画出完成如下要求的程序对应的流程图:求两个自然数的最大公约数和最小公倍数

/* 【例 5.17】求两个自然数的最大公约数和最小公倍数 */
#include <stdio.h>

int main()
{
    int a, b, r, n, m;

    printf("请输入两个整数:");
    scanf("%d%d", &a, &b);

    m = a; n = b;   /* 保存原始数值,因为后面 a 和 b 会变 */

    /* 核心算法:辗转相除法 */
    do
    {
        r = a % b;  /* 求余数 */
        a = b;      /* 把除数 b 变成下一轮的被除数 a */
        b = r;      /* 把余数 r 变成下一轮的除数 b */
    } while (r != 0); /* 当余数不为0时,继续循环;余数为0,结束 */

    printf("%d和%d的最大公约数是:%d\n", m, n, a);
    printf("最小公倍数是:%d\n", m * n / a);

    return 0;
}
image-31f8f99d
  • 开始框(圆角矩形/椭圆):写上 开始
  • 输入框(平行四边形):写 输入 a, b
  • 处理框(矩形):写 m = a, n = b(保存原值)。
  • 处理框(矩形):写 r = a % b
  • 处理框(矩形):写 a = b
  • 处理框(矩形):写 b = r
  • 判断框(菱形):写 r != 0 ? (或者 r == 0?)。
    • 如果 是 (True):箭头指回到第 4 步(求余数的那一步)。
    • 如果 否 (False):箭头向下继续。
  • 处理框(矩形):计算最小公倍数 lcm = m * n / a (此时 a 已经是最大公约数了)。
  • 输出框(平行四边形):写 输出 a, lcm
  • 结束框(圆角矩形/椭圆):写 结束

2、可能还是考察画图(分支)

没有明确的暗示

自己掌握好相关知识点

怎么画图

题目一:寻找最大的数

image-e83f60a2

题目二:有3个数 a, b, c,要求按大小顺序把它们输出

image-0e8d25ad

题目三:判断一个数 n 能否同时被 3 和 5 整除

image-e2352b0f

3、也可能考察程序 不过还是画图可能性大些

出容易点就是

问题:编程打印数字金字塔。

      0

    123

   23456

 3456789
#include <stdio.h>

int main() {
    int height = 4; // 金字塔高度
    int i, j;

    for (i = 0; i < height; i++) {
        // 打印每行前的空格,使数字居中
        for (j = 0; j < height - i - 1; j++) {
            printf(" ");
        }

        // 打印数字,每行从 i 开始,长度为 2*i + 1
        for (j = 0; j < 2 * i + 1; j++) {
            printf("%d", i + j);
        }

        // 换行
        printf("\n");
    }

    return 0;
}
问题:求阶乘。
#include <stdio.h>

int main() {
    int n;
    unsigned long long fact = 1;

    scanf("%d", &n);

    if (n < 0) {
        printf("阶乘只适用于非负整数。\n");
        return 1;
    }

    for (int i = 1; i <= n; i++) {
        fact *= i;
    }

    printf("%d 的阶乘是 %llu\n", n, fact);

    return 0;
}