C语言笔试

考试时间

题型分布

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

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

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

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

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

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

按知识点一个一个过关

选填判(小题)

按知识点学习:

基础知识

程序是什么(目的):

程序是为实现特定目标或解决特定问题而用计算机语言(程序设计语言)编写的一系列语句或指令,计算机会严格执行这些指令。

C语言中,语句是程序最基本的执行单位,以分号结尾。

函数 语句

main函数是什么:

所有c程序有且只有一个main函数,c程序总是从main()函数的第一条语句开始执行,当main()函数结束执行时,程序结束执行。

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

标识符

c语言的标识符由字母、数字和下划线组成,其中第一个字符必须是字母或下划线。

注意还有一点 不能是c语言已有的关键字

但这题主要考察的是能不能以数字开头 记住:不能

有时候看见下划线开头会感觉奇怪 但其实是可以的

这题应该会以选择题的形式出现 会做这几道就没问题 掌握了

image-617c0155
  • 练习:

    • image-bf2ef698
    • image-f203927f
    • image-37bd00da
    • image-92d83c0b
    • image-535a3d27
    • image-370bf651

数据类型

在 C 语言中,数据类型通常分为两大类

  1. 基本数据类型(分为整型、字符型、实型(浮点型)) 用于表示最基本的数据,包括:
    • int(整型)
    • float(单精度浮点型)
    • double(双精度浮点型)
    • char(字符型)
  2. 构造数据类型 由基本数据类型组合或派生而来,包括:
    • 数组(array)
    • 结构体(struct)
    • 共用体(union)
    • 枚举(enum)
    • 指针(pointer)

考察形式应该为填空题

C 语言的数据类型分为基本数据类型构造数据类型两类。 C 语言的基本数据类型分整型字符型、**实型(浮点型)**三类。

unsigned:无符号(没有负数)

特性: 只能存储非负整数(0 和正数)。因为不需要存符号位,正数范围比同级有符号类型大一倍。

  • 考察点: 负数变大数、正数翻倍。
  • 含义: 只能存 0 和正数。
  • 好处: 正数范围扩大一倍(比如 char 最大 127,unsigned char 最大 255)。
  • 输入输出: %u
  • 下溢/回绕 (Underflow):
    • 无符号数永远不会是负数。
    • 如果你写 unsigned short a = 0; a = a - 1; 试图计算 0 - 1 时,它不会变成 -1,而是会“倒转”回该类型能表示的最大值
    • unsigned short (范围 0~65535)。0 - 1 = 65535
      • 类比: 就像机械里程表,从 0000 往回拨一格,会变成 9999
  • 练习:
    • image-0d4a519a
    • image-28168d29
    • image-31f226b7

long long (超长整型):

  • 用途: 当标准 int (通常 ±21亿左右) 不够存时使用(如阶乘、斐波那契数列)。
  • 内存: 标准规定至少 64 位,通常占用 8 个字节
  • 格式控制符: 输入输出必须使用 %lldscanf("%lld", &a); printf("%lld", a); 如果误用 %d,会导致数据截断或打印出错误的负数/乱码。
  • 练习
    • image-cde7e06f
    • image-72443a94
    • image-d63adcf0
    • image-20cedf2c

不过很大可能只是简单的出一道填空题 问:

unsigned使用**%u格式化输出,long long使用%lld**格式化输出。

sizeof

表达式sizeof()

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

常量

字符常量

知识点1:

'A' 的 ASCII 值是 65(以此类推 'B'=66)。

'a' 的 ASCII 值是 97(以此类推 'b'=98)。

'0' 的 ASCII 值是 48

转换规律: 小写字母 = 大写字母 + 32。

**怎么考:**题目通常会问 char c = 'A' + 32; 输出什么(答案是 'a'),或者问 printf("%d", 'b'); 输出什么(答案是 98)。

知识点2:

区分 字符 '4'数字 4

数字 4:就是整数 4,在内存里存的就是二进制的 4。

字符 '4':是一个符号,在内存里存的是它的 ASCII 码。已知 '0'=48,那么 '4' = 48 + 4 = 52

**怎么考:**让你计算 '4' - '0' 的结果(答案是整数 4),或者问 printf("%d", '4'); 输出什么(答案是 52,绝对不是 4)。

知识点3:

printf 的格式控制符 %e

知识点: %e 用来以**指数形式(科学计数法)**输出浮点数。

怎么考: 给你一个数 123.456,问用 %e 输出长什么样(类似 1.234560e+002)。

  • 练习:
    • image-ca0e7731
    • image-c34e8066
    • image-6f718743
字符串常量

字符串常量是由一对双引号“”括起来的零个或多个字符序列,如“HelloWorld"等,默认以‘\0’作为结束标志,因此由n个字符组成的字符串在内存中需要占用n+1个字节的空间。

考点核心在于区分 “字符串长度”“占用内存大小”

对于字符串 "Hello\0World",编译器在内存里是这样存的:

H e l l o \0 W o r l d \0
1 2 3 4 5 6 7 8 9 10 11 12

注意最后那个隐形的 \0,这是 C 语言自动帮你加的字符串结束符。

考法 A:问长度 (strlen)

规则: strlen 函数是个“老实人”,它从头开始数,一旦遇到第一个 \0 就立马停工,后面的东西它全都不看。

结果: 它只数到 'o' 就停了。

答案: 长度为 5。

考法 B:问占用空间 (sizeof)

规则: sizeof 关注的是“房子有多大”。它不管你中间有没有结束符,它只管这一坨东西在内存里实打实占了几个格子。

计算: "Hello" (5个) + \0 (1个) + "World" (5个) + 最后的隐形 \0 (1个)。

答案: 占用字节数为 12。

  • 练习:
    • image-e010c245
    • image-3b96d5cd
    • image-4e9b87ca
    • image-e3d29aa0
    • image-6ea71c3c
符号常量

在C语言中,可以用一个标识符来表示一个常量,称为符号常量,一般形式为

#define 标识符 常量

注意!!! 它是****直接替换

考点

1、千万别加分号!

❌ 错:#define PI 3.14;

✅ 对:#define PI 3.14

为什么? 它是“傻瓜式替换”。如果你加了分号,代码里写 x = PI * 2; 就会变成 x = 3.14; * 2;,直接报错!

2、没有“=”号

❌ 错:#define N = 100

✅ 对:#define N 100

它不是赋值变量,中间用空格隔开。

3、原样替换(运算优先级陷阱)

高分题的分水岭。

考题: #define M 2+3,问 M * 2 是多少?

直觉(错): (2+3) * 2 = 10。

真相(对): 它是原样拼凑!变成了 2+3 * 2。根据优先级先算乘法,结果是 2 + 6 = 8

  • 练习:
    • image-ac0a0a00
    • image-30abfe31
    • image-59566f7a

运算符

优先级
  1. 核心概念

在 C 语言中,一个表达式不是从左到右随便算的,而是由运算符优先级结合性共同决定计算顺序。

  • 优先级 (Precedence):决定“谁先算”。优先级高的运算符先结合(例如乘除先于加减)。
  • 结合性 (Associativity):当优先级相同的时候,决定“怎么算”。
    • 左结合:从左向右算(大多数运算符,如 a - b + c)。
    • 右结合:从右向左算(主要是单目运算符赋值运算符三目运算符)。
  1. 常见运算符优先级速查表(高 → 低)
优先级 运算符 说明 结合性
最高 !, ++, --, (type), sizeof 单目运算符 (逻辑非, 自增, 强转等) 右到左
*, /, % 算术运算 (乘, 除, 取余) 左到右
+, - 算术运算 (加, 减) 左到右
<, <=, >, >= 关系运算 (大小比较) 左到右
==, != 关系运算 (相等性判断) 左到右
&& 逻辑与 左到右
` `
最低 = 赋值运算 右到左
  1. 短路求值

在逻辑表达式中,如果在计算过程中,表达式的最终真假已经可以确定,那么剩余的子表达式将不会再被求值。

在 C 语言中,只有两个运算符具有短路行为

运算符 名称
&& 逻辑与
`

①:逻辑与 &&

A && B

若 A == 0(假)

整个表达式结果已确定为假

B 不再计算

➡️ 左假即停

**②:**逻辑或 ||

A || B
  • A != 0(真)
  • 整个表达式结果已确定为真
  • B 不再计算

➡️ 左真即停

短路的执行顺序(非常关键)

(1) 固定从左到右

  • &&|| 一定从左向右求值
  • 不允许交换顺序
  • 不允许并行计算

(2)与“优先级”的关系

优先级决定表达式如何分组 短路决定分组后的子表达式是否继续计算

两者是不同层面的规则,不能混为一谈。

  • 练习
    • image-7ac35e2b
    • image-e3f7c79c
    • image-0fb95bc6
    • image-2354a140
    • image-5a937872

问题:

a=5; b=4;
int k = a > b;        k = ?
a>b -> 5>4 -> true -> 1
k=1
逻辑运算(真假与数值的关系)
  1. C 语言中的“真假”规则

C 语言并没有专门的 bool(C99 之前),逻辑判断遵循:

  • 0 → 假(false)
  • 非 0 → 真(true)

这是理解所有逻辑表达式的基础。

  1. 常见逻辑运算符含义
运算符 含义 结果
!a 逻辑非 真→0,假→1
a && b 逻辑与 同真才真
`a b`

注意:

  • 逻辑运算的结果只能是 0 或 1

  • 即使参与运算的是任意整数,结果仍被“压缩”为真假值

  • 练习

    • image-574053ff
    • image-ad15a8e9
    • image-19648bac
    • image-07c22480
    • image-1d8e6bc7
逻辑运算符和逻辑表达式(短路是拔尖题 必考)

! && ||

  • 优先级阶梯:
    • ! (非) > 算术运算符 (+ - * /) > 关系运算符 (> < ==) > && (与) > || (或) > 赋值 (=)
    • 口诀: “非”最高,“与”比“或”大。
    • 例子: a || b && c 等价于 a || (b && c)
  • 真假的定义:
    • 输入时: 非 0 即为真,0 为假。(例如:-5 是真,3.14 是真,只有 0 是假)。
    • 输出时: 逻辑运算的结果只有两个值:1 (真)0 (假)
      • 5 && 4 的结果是 1,绝不是 420
  • 【核心难点】短路法则(懒人法则):
    • && (与) 的短路: 如果左边是 假 (0),右边完全不执行。(因为一假则假,没必要看了)。
    • || (或) 的短路: 如果左边是 真 (非0),右边完全不执行。(因为一真则真,没必要看了)。

典型例题分析(短路陷阱)

int a = 0, b = 1;
if (a++ && b++) {
    // ...
}
printf("a=%d, b=%d", a, b);
  • 分析:

    1. 执行 a++ && ...。由于是后缀 ++,先取 a 的旧值 0 参与逻辑运算。
    2. 0 是假。对于 && 来说,左边为假,整体必为假。触发短路!
    3. 后果: 右边的 b++ 直接被跳过,根本不执行
    4. 之后 a 完成自增变为 1。
  • 结果: a=1, b=1 (b 没变)。

  • 练习题:

    • image-1f63881b
    • image-e3b23cf6
    • image-34b26f04

一句话总结: 逻辑题核心看 “短路”&& 遇假就停,|| 遇真就停,后面有 i++ 也别管它!

填空题:

int x=33,y=4;
!x+x/y=?
!330
0 + x / y
33 / 48
0 + 8
0 + 8 = 8

结果为 8
自增自减运算符

(可能考程序阅读也可能考小题 挺重要的 一定要掌握)

不管题目多花哨,记住这两个原则:

  • ++i(前缀):先变,后用。
    • (先把自己的值 +1,然后再拿去参加运算/赋值)。
  • i++(后缀):先用,后变。
    • (先拿现在的旧值去参加运算/赋值,等这一行代码执行完了,自己再偷偷 +1)。

骗局 A:赋值时的差异

int a = 3;
int b = a++;
// 过程:b 先拿走 a 的旧值 3,然后 a 自己变成 4。
// 结果:b=3, a=4

VS

int a = 3;
int b = ++a;
// 过程:a 先变成 4,然后 b 拿走这个新值 4。
// 结果:b=4, a=4

骗局 B:作为循环条件(高频考点)

int k = 0;
while (k++ < 3);
printf("%d", k);

分析: 很容易会填 3。错!答案是 4。

  • 最后一次判断时,3 < 3 不成立,循环结束。但在判断的那一瞬间,k++ 已经执行了(先判断,判断完立刻自增)。所以 k 变成了 4。

遇到 while(i++ < n),记得判断完的时候,i 已经加了 1,进门(循环体)的是大一号的 i

  • 练习:
    • image-87864fdb
    • image-fba31251
    • image-72394776
    • image-471c7c65
    • image-410ef570

填空题:

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
条件运算符

三目表达式 ? :

核心公式(死记硬背)

C

表达式1 ? 表达式2 : 表达式3
  • “真 1 挑 2,假 1 挑 3”。
  • 翻译:
    • 先算 表达式1(是不是真?)。
    • 真 (非0):结果就是 表达式2 的值。
    • 假 (0):结果就是 表达式3 的值。

经典考法:求最大值

这是最基础的考法,必须秒杀。

C

max = (a > b) ? a : b;
  • 意思: 如果 a 大于 b,就把 a 给 max;否则把 b 给 max。

  • 等价于:

    if (a > b) max = a;
    else       max = b;
    

【高阶陷阱】右结合性(连环嵌套)

这是选择题里最爱用来绕晕你的考法。 例题: a > b ? a : c > d ? c : d

  • 解题秘籍: 遇到连续的 ? :从右往左 或者是 剥洋葱 式的看。
  • 正确断句: a > b ? a : (c > d ? c : d)
    • 先看最外层:a > b 成立吗?
    • 成立 -> 直接取 a,游戏结束。
    • 不成立 -> 跑去执行括号里的 (c > d ? c : d)
  • 练习
    • image-91d80328
    • image-c0418a4b
    • image-885abcfa

看见 ? 就问自己“是真的吗?”,真就拿走冒号前面的,假就拿走后面的。遇到一长串,先把最右边的括号括起来看。

输入输出

基本输入输出函数
  1. 输出函数:printf

printf("格式字符串", 参数列表);

  • 作用:把数据输出到屏幕
  • 格式字符串决定“怎么显示”
  • 参数列表决定“显示什么值”

⚠️ 参数个数必须 ≥ 格式符个数

printf("%d", a, b);   // 只输出 ab 被忽略
  1. 输入函数:scanf

scanf("格式字符串", 地址列表);

  • 作用:从键盘读入数据,存入变量
  • 本质scanf 要“往变量里写数据”
scanf 的核心规则
  1. 为什么要加 &(取地址符)

规则:

scanf 往变量里存数据,必须知道变量住在哪里(内存地址)。

int a; scanf("%d", &a); // 正确

  1. 必须加 & 的情况
  • 普通变量int / float / char / double

scanf("%d", &a); // ✅

  1. 不用加 & 的特殊情况(了解)
  • 数组名
  • 指针变量本身
  1. 错题典范
scanf("%d", a);    // ❌ 错误:a 不是地址
scanf("%d", &a);   // ✅ 正确
常用格式控制符(输入 + 输出)
格式符 含义 说明
%d 十进制整数 最常用
%x 十六进制整数(小写) 输出 a–f
%X 十六进制整数(大写) 输出 A–F
%o 八进制整数
%f 浮点数 默认 6 位小数
%c 字符
%s 字符串

⚠️ 重点理解

格式符决定“如何解释和显示数据”,不改变变量本身的值

示例:%x / %X

int a = 10;
printf("%x", a);  // a
printf("%X", a);  // A
  • 变量里存的还是 十进制 10
  • %x / %X 只是输出时按十六进制显示
printf 的输出宽度与对齐

正数宽度:右对齐,左补空格 负数宽度:左对齐,右补空格


假设:

int a = 123;

常见格式化示例

格式 含义 输出效果(□ 表示空格) 记忆要点
%5d 宽度 5,右对齐 □□123 正数 → 右对齐
%-5d 宽度 5,左对齐 123□□ 负号表示左对齐
%2d 宽度 2(不够) 123 不截断
%05d 宽度 5,补 0 00123 0 表示补零
%8.2f 宽度 8,小数 2 位 □□□□3.14 默认右对齐
%-8.2f 宽度 8,小数 2 位 3.14□□□□ 负号左对齐
%4.2f 宽度 4,小数 2 位 3.14 刚好
%3.2f 宽度 3,不够 3.14 不截断
%08.2f 宽度 8,补 0 00003.14 0 表示补零

宽度的本质

  • 宽度是“最小输出宽度”
  • 如果数据本身更宽:
    • 直接原样输出
    • 不会截断

精度的本质(浮点数特有)

  1. 精度控制的是“小数位数”
vdouble x = 3.14159;
printf("%.2f", x);   // 3.14 发生 四舍五入
  • 不只是简单截断
  1. 精度优先于宽度

printf("%4.1f", 12.34); // 12.3

  • 先按精度算结果
  • 再检查宽度是否足够
格式字符串与参数的对应关系

规则

一个格式符,对应一个参数

printf("%d %d", a, b);   // 输出两个整数
printf("%d", a, b);      // 只输出 a

⚠️ 多给参数不会报错,但没用

scanf 中格式符的注意点
  1. %d 与输入形式

scanf("%d", &a);

  • 自动跳过空白符(空格、回车、Tab)
  • 读取一个十进制整数
  1. %d-%d
  • - 不是格式符

  • %-5d 中的 -printf 的对齐控制

  • scanf 中没有“左对齐/右对齐”的概念

  • 练习:
    • image-ca7e4616
    • image-e184c07c
    • image-d6454a01
    • image-c833911c
    • image-98863cb0
    • image-cb535e7a
    • image-b560d1bc
    • image-09e2f833
    • image-d47d93d7
    • image-1b9fc7fd
    • image-c43de94f
    • image-c4a1912c
    • image-48fe4ab5

问题1:

格式修饰符都有哪些?
%d, %3d, %u, %x, %#x, %ld, %f, %lf, %4.2f, %c, %s的含义分别是什么?
printf(“%c,%d”, ‘A’,’A’);
%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

实际输出结果:
A,65

问题2:

printf函数调用时,格式说明符数量与实际提供的参数数量不一致时会发生什么?
1. 格式说明符多于参数
int a = 10;
printf("%d %d", a);

%d 有两个,但只提供了一个参数
第二个 %d 没有对应参数

结果:
输出第一个数字正确(10)
第二个数字随机、可能乱码、甚至导致段错误
本质原因:printf 依赖栈访问参数,如果没提供就读取随机栈值

2. 格式说明符少于参数
int a = 10, b = 20;
printf("%d", a, b);

%d 只有一个说明符,但提供了两个参数

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

3. 参数类型与格式符不匹配
double x = 3.14;
printf("%d", x);

%d 期望 int,实际传入 double

结果:
输出不可预测
可能出现乱码或错误值
这是 典型未定义行为(Undefined Behavior)
情况 行为 备注
格式符 > 参数 随机值 / 崩溃 没有对应参数会访问“垃圾栈”
格式符 < 参数 多余参数忽略 不会报错
类型不匹配 未定义行为 输出不可预测

字符型数据的输入和输出

putchar:专一的输出者
  • 全名: put character(输出字符)。
  • 功能: 向屏幕输出一个字符。
  • 写法: putchar(参数)
    • 参数可以是字符常量:putchar('A')
    • 参数可以是变量:putchar(c)
    • 参数可以是整型(ASCII):putchar(65) (输出 'A')
    • 参数可以是转义字符:putchar('\n') (换行)
  • 考点:只能输出一个字符,不能输出字符串(不能写 putchar("ABC"))。等价于 printf("%c", c)
getchar:来者不拒的接收者
  • 全名: get character(获取字符)。

  • 功能: 从键盘读入一个字符。

  • 写法: 变量 = getchar(); (注意:括号里没参数!)

  • 【超级死穴】回车符陷阱:

    • getchar() 是个“老实人”,它连你按下的 回车键 (Enter) 都会当成一个字符 '\n' 读进去!
    • 场景: 考试程序让你输入 "A",然后按回车。
    • 如果你写了两次 getchar()
      • 第1次读到:'A'
      • 第2次读到:'\n' (换行符) —— 容易以为还是读下一个字母,错!
  • 练习题:

    • image-d839cce9
    • image-3cee7357
    • image-bf3a783f

数学库函数

在头文件math.h中

指数 exp()

绝对值 fabs()

对数 log()

幂函数 pow()

平方根 sqrt()

  • 绝对值
    • abs(int x):求整数的绝对值(其实它在 <stdlib.h> 里,但考试常混考)。
    • fabs(double x):求浮点数(小数)的绝对值(这才是 <math.h> 的亲儿子)。
    • 考点: 看到 fabs(-3) 别选 3,要选 3.0(因为返回值是 double)。
  • pow(x, y) 幂函数
    • 表示 \(x^y\)(x 的 y 次方)。
    • 考点: 两个参数都是 double,返回值也是 double
    • 坑: pow(2, 3) 的结果不是整数 8,而是浮点数 8.0
  • sqrt(x) 平方根
    • 考点: 参数必须是非负数sqrt(-4) 会导致运算错误(Domain error)。返回值是 double
  • log(x) 对数
    • 考点: C 语言里的 log(x) 特指自然对数\(\ln(x)\)(以 \(e\) 为底)。
    • 如果考试让你算 \(\lg(x)\)(以 10 为底),必须用 log10(x)。这是一大坑点!

要调用数学函数时,在#include命令行应该包含 math.h

  • 练习题:
    • image-b2b26806
    • image-731d4aac
    • image-59ab5fff

分支结构

if else

switch case

注意if()后面加个; 里面不执行

if 语句的“三大坑”

坑一:夺命分号

C

if (a > 5);   // <-- 注意这里!有个分号
    printf("大");
  • 后果: 这里的 ; 代表“空语句”。意思是:如果 a>5,什么都不做。
  • 现象: 下面的 printf("大") 不再受 if 控制,无论 a 是多少,它都会打印
  • 口诀: if 后面别加分号,除非你想让它空转。

坑二:赋值与等于混淆

  • ❌ 错:if (a = 5) —— 这是把 5 赋值给 a,表达式结果为 5(真),永远执行
  • ✅ 对:if (a == 5) —— 这才是判断相等。

坑三:悬空 else(配对原则)

C

if (a > 0)
    if (b > 0) printf("A");
else           printf("B");  // 这个 else 属于谁?
  • 规则: else 总是跟最近的、还没配对的那个 if 结合。
  • 真相: 这个 else 是属于 if(b>0) 的,尽管缩进故意骗你它属于第一个。
switch 语句的“连环坑”

坑一:穿透现象(漏写 break)

C

switch(a) {
    case 1: printf("A"); // 没写 break,继续往下跑!
    case 2: printf("B"); break;
}
  • 结果: 如果 a 是 1,它会先打印 A,再打印 B,直到遇到 break 才停。这叫“穿透”。

坑二:类型限制

  • switch(x) 括号里的 x 只能是整型(int, long等)或字符型(char)

  • 绝对不能是: float, double(小数不能精确比较),或者字符串。

  • 练习:
    • image-5dee81f9
    • image-6c862a81
    • image-c37af5b2
    • image-bd62b477

问题1:

if (条件) {

	a = 1;

	b = 2;

}

if (条件)

	a = 1;

b = 2;

if (条件)

	a = 1, b =2;

if (条件);
a = 1;

b = 2;

注意区分 看准括号和分号的位置

问题2:

switch语句中,各个casedefault的出现次序影响执行结果吗?
image-27180752

循环结构

for 循环
  1. 语法
for (初始化; 条件; 更新) {
    // 循环体
}
  • 初始化:循环变量初始值,只执行一次
  • 条件:循环执行的判断条件(真/假)
  • 更新:每次循环结束后执行(通常自增/自减)
  1. 执行流程

  2. 执行 初始化

  3. 判断 条件

    • 真 → 执行循环体
    • 假 → 结束循环
  4. 执行 循环体

  5. 执行 更新

  6. 回到 条件判断

  7. 重复步骤 2-5,直到条件为假

  8. 特点

  • 循环次数通常确定或可控
  • 循环变量在循环外可用
  • 适合 计数循环(for i=0;i<n;i++)
while 循环
  1. 语法
while (条件) {
    // 循环体
}
  1. 执行流程

  2. 判断 条件

    • 真 → 执行循环体
    • 假 → 跳出循环
  3. 执行循环体

  4. 回到 条件判断

  5. 重复步骤 1-3

  6. 特点

  • 循环次数不确定,取决于条件
  • 条件先判断,循环体可能一次都不执行
  • 适合 先判断再执行的循环
do…while 循环
  1. 语法
do {
    // 循环体
} while (条件);
  1. 执行流程

  2. 执行 循环体

  3. 判断 条件

    • 真 → 回到步骤 1
    • 假 → 结束循环
  4. 特点

  • 先执行后判断 → 循环体至少执行一次
  • 循环次数不确定,取决于条件
  • 适合 至少执行一次的循环
循环类型 条件位置 是否至少执行一次 典型应用
for 前(循环开始前) 不一定 已知循环次数
while 不一定 条件判断先行
do…while 一定 至少执行一次
  • 练习:
    • image-030a3d9c
    • image-45fd087e
    • image-79a7153b

数组

一维数组
  1. 定义与下标
  • 定义格式: 类型 数组名[常量表达式];
    • 例如:int a[10];
    • ⚠️ 注意: 数组长度在 C89 标准中必须是常量(如 10#define N 10),不能是变量(如 int n=10; int a[n]; 是非法的,虽然 C99 支持变长数组,但考试通常按 C89 标准判错)。
  • 下标范围:0 开始,到 长度-1 结束。
    • int a[10]; 的合法下标是 a[0]a[9]
    • 越界陷阱: 访问 a[10] 是越界,考试常考!
  1. 初始化(赋初值)
  • 完全初始化: int a[5] = {1, 2, 3, 4, 5};
  • 部分初始化(重点): int a[5] = {1, 2};
    • 规则: 前两个是 1, 2,后面剩下的全部自动补 0
    • 常用技巧: int a[10] = {0}; 可以把整个数组清零。
  • 省略长度初始化: int a[] = {1, 2, 3};
    • 编译器会根据大括号里的个数(3个)自动推算出数组长度为 3。
二维数组
  1. 内存存储(线性)
  • 二维数组在内存中是**“按行存放”**的,也是连续的一条线,不是真的方块。
    • 先存第 0 行,再存第 1 行...
  1. 初始化的花样
  • 按行赋值(最清晰): int a[2][3] = {{1, 2, 3}, {4, 5, 6}};
  • 连续赋值(易错): int a[2][3] = {1, 2, 3, 4, 5, 6};
    • 自动按顺序填满第一行,再填第二行。
  • 部分赋值: int a[2][3] = {{1}, {4}};
    • 第一行是 1, 0, 0
    • 第二行是 4, 0, 0
  1. 【核心考点】省略维度的规则

声明二维数组并进行初始化时:

  • 第一维(行数):可以省略。
  • 第二维(列数):不可省略!
写法 合法性 解析
int a[][3] = {1,2,3,4}; 合法 知道一行 3 个,总共 4 个数据 \(\rightarrow\) 算出需要 2 行。
int a[2][] = {1,2,3,4}; 非法 不知道一行几个,无法切割内存。
int a[][] = {1,2,3,4}; 非法 两个都不知道,彻底懵圈。

问题:

声明二维数组时,可以省略哪个维度?
只能省略第一个维度(行),绝对不能省略第二个维度(列)。

字符串

  1. 字符串的特殊性
  • char s1[] = {'h', 'e', 'l', 'l', 'o'};
    • 这是普通字符数组,长度 5,占用 5 字节。
  • char s2[] = "hello";
    • 这是字符串,长度 5,占用 6 字节(因为自动加了 \0)。
  1. 常用操作陷阱
  • 赋值: 只能在定义时初始化赋值。
    • char s[10] = "abc";
    • char s[10]; s = "abc"; (数组名是常量地址,不能被赋值!)
  • 输入: scanf("%s", s); (注意:数组名 s 本身就是地址,不需要加 &)。

问题:

sizeof(“hello”)=?
6
末尾有\0
字符串比较函数strcmp()
  1. 基础档案
  • 头文件: #include <string.h> (考试填空常考)
  • 功能: 按照 ASCII 码值,从左到右一个个字符比较两个字符串。
  • 原型: int strcmp(const char *s1, const char *s2);
  1. 三个返回值

n = strcmp(s1, s2);

返回值 含义 记忆方法
0 两个字符串完全相同 0 代表“无差别”
> 0 (正数) s1 大于 s2 s1 赢了(ASCII码更大)
< 0 (负数) s1 小于 s2 s1 输了(ASCII码更小)

怎么比大小?

  • 像查字典一样。
  • "abc""abd":前两个一样,比第三个。'c'(99) 小于 'd'(100),所以结果是负数
  • "abc""abcd":前三个一样,第四个位置 '\0'(0) 小于 'd'(100),所以前者小,结果是负数
  1. if 判断陷阱

❌ 错误写法:

// 很容易以为 strcmp 返回 1 代表相等,大错特错!
if (strcmp(s1, s2)) {
    printf("相等");
}

C

// 很多初学者以为 strcmp 返回 1 代表相等,这是大错特错!
if (strcmp(s1, s2)) {
    printf("相等");
}

解析: 如果 s1s2 真的相等,strcmp 返回 0。在 C 语言中,0 代表假。所以上面的代码根本不会执行 printf

✅ 正确写法:

if (strcmp(s1, s2) == 0) {
    printf("相等");
}
  • 练习:
    • image-fae0ef71
    • image-2e6d0882
    • image-03e82a37

想要判断相等,必须写 <mark> 0;如果不写 </mark> 0,那它实际上是在判断“这两个字符串不一样”。

C

if (strcmp(s1, s2) == 0) {
    printf("相等");
}

问题:

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

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

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

注意:strcmp 是区分大小写的。例如 "abc""ABC" 不相等。

函数

有参函数的定义 (P154)

定义函数就是“造工具”。你告诉编译器这个工具叫什么、能干什么、需要什么材料。

  • 基本语法格式:

    返回值类型  函数名( 类型 形参1, 类型 形参2, ... )
    {
        // 函数体(要做的事情)
        return 结果;
    }
    
  • 例子:

    int add(int x, int y) { // x, y 是接收数据的容器
        return x + y;
    }
    
函数的返回值 (P160)
  • 关键字: return
  • 规则:
    • return 后面的表达式类型应与函数定义时的“返回值类型”一致。如果不一致,会自动发生类型转换(以函数定义时的类型为准)。
    • 如果函数类型为 void,则不需要返回值,可以写 return; 也可以不写。
    • 一旦执行到 return,函数立刻结束,跳回调用处。
函数声明 (P161)
  • 作用: 相当于“菜单”。在正式上菜(定义函数)之前,先告诉编译器“我有这个菜”。
  • 何时需要?
    • 如果函数定义在 main 函数之后,必须在 main 之前(或调用之前)进行声明。
    • 如果函数定义在 main 函数之前,声明可以省略。
  • 格式: 返回值类型 函数名(类型, 类型); (分号不能少!)
问题

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

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

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

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

Q3:是否可以调用 main 函数?

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

Q4:什么是外部函数和内部函数,分别使用哪个关键字?

  • 内部函数 (静态函数):
    • 只能被当前文件中的其他函数调用,不能被其他文件使用。
    • 关键字:static
    • 格式:static int fun(...)
  • 外部函数:
    • 可以被其他文件调用。C 语言中函数默认都是外部的。
    • 关键字:extern (定义时通常省略,声明时可加上)

Q5:当哪条语句被执行时,程序的执行流程无条件地从一个函数跳转到另一个函数?

  • 答案:函数调用语句
    • 当程序执行到 fun(); 这种调用语句时,控制权会立即从当前函数(如 main)转移到被调用的函数(如 fun)。
    • return 语句则是将控制权跳回

指针与字符串

字符串与字符指针P209

  • 定义方式:
    • 数组方式:char str[] = "Hello"; (存放在栈区,变量,内容可修改)
    • 指针方式:char *p = "Hello"; (p 指向常量区的字符串,常量,内容不可修改)
  • 核心区别:
    • str 是数组名,是地址常量不能进行自增操作(如 str++ 是非法的)。
    • p 是指针变量,可以改变指向,可以进行自增操作(如 p++ 合法)。
问题:

Q1:利用字符指针输出字符串,每行一个字符

这是一个经典的指针遍历字符串的应用。

char *str = "Hello"; // 定义一个字符串
char *p = str;       // 让指针 p 指向字符串的第一个字符 'H'

// 循环条件:*p != '\0',即只要指针指向的不是字符串结束符
while (*p != '\0') {
    printf("%c\n", *p); // 输出当前指针指向的字符,并换行
    p++;                // 让指针向后移动一个位置,指向下一个字符
}

Q2:用指针遍历数组元素

指针可以通过算术运算来访问数组中的元素。

int a[5] = {10, 20, 30, 40, 50};
int *p = a; // 指针 p 指向数组首地址

for (int i = 0; i < 5; i++) {
    // 写法 1:指针不移动,通过偏移量访问
    printf("%d ", *(p + i));

    // 写法 2:直接移动指针 (如果用这种写法,p 真的会变,要注意后续使用 p 的位置)
    // printf("%d ", *p++);
}

结构体与链表

结构体 (struct)

结构体是把不同类型的数据组合在一起的“大包裹”。

结构体数组的初始化 (P233)

  • 语法格式:

    struct 结构名 数组名[元素个数] = { {初值列表1}, {初值列表2}, ... };
    
    struct Student {
        char name[20];
        int age;
    };
    
    // 定义并初始化一个包含 3 个学生的数组
    struct Student stus[3] = {
        {"ZhangSan", 18},
        {"LiSi", 19},
        {"WangWu", 20}
    };
    
问题:

Q1:如何使用结构体变量?

有两种方式,取决于你手里拿的是“变量本身”还是“指针”。

  1. 通过变量名访问 (点号 . )
    • 格式:结构变量名.成员名
    • 例子:s.age = 18;
  2. 通过指针访问 (箭头 -> )
    • 格式:结构指针->成员名

    • 例子:

      struct Student s;
      struct Student *p = &s; // p 是指向 s 的指针
      p->age = 18;            // 等价于 (*p).age
      

Q2:如何查看结构体类型所占用的内存空间大小?

  • 方法: 使用 sizeof 运算符。
  • 格式: sizeof(struct 结构名)sizeof(结构变量名)
  • 注意(内存对齐):
    • 结构体的大小通常不等于所有成员大小的简单相加。
    • 编译器为了提高读写效率,会进行“内存对齐”(比如 char 后面可能会补空字节,以便让后面的 int 从 4 的倍数地址开始存放)。
    • 考试技巧: 如果问具体大小,通常只需算出总和即可(除非考题明确提及内存对齐/Padding)。
共用体 (union) (P251)
  1. 核心概念
  • 定义: 使几个不同类型的变量共用同一段内存空间。
  • 大小: 共用体变量所占的内存长度,等于其最长成员的长度。
  • 特点(喜新厌旧):
    • 同一时刻只能存储其中一个成员的值。
    • 对一个成员赋值,会覆盖掉其他成员的值(因为大家住同一个房间)。
#include<stdio.h>
int main()
{
    // 定义一个匿名共用体变量 s
    union {
        char c;   // 占 1 字节
        int i;    // 占 4 字节(假设)
    } s;          // s 的总大小是 4 字节(取最大的)

    s.c = 'A';    // 内存里写入了 'A' (65)
    s.i = 99;     // 【关键】内存里写入了 99。由于 c 和 i 共用地址,这会覆盖掉刚才的 'A'。

    printf("%d", s.i); // 此时内存里存的是 99,所以输出 99。
    return 0;
}
  • 输出结果: 99
  • 解析: 共用体的核心就是“共享”。s.cs.i 的起始地址是一样的。当你执行 s.i = 99 时,原本 s.c 占用的那个字节也被 99 的数据覆盖了(或者成为了 99 的一部分)。最后读 s.i,当然就是刚刚存进去的 99。
链表(Brief Intro) (P246)
  1. 节点的定义

链表节点 = 数据域 + 指针域

struct Node {
    int data;           // 数据域:存数据
    struct Node *next;  // 指针域:存下一个节点的地址
};
  1. 常见考点
  • 头指针 (head): 指向链表第一个节点的指针。
  • 结束标志: 最后一个节点的 next 指针指向 NULL
  • 遍历: p = p->next; (让指针 p 移动到下一个节点)。
image-5f6c16ff

文件

文件类型 (P263)

数据文件主要分为两种:

  • ASCII 文件 (文本文件): 每个字符占一个字节,存储的是字符的 ASCII 码,可以直接阅读(如 .txt, .c)。
  • 二进制文件 (Binary): 将内存中的数据原样存储,不进行转换,不仅省空间而且读写快,但无法直接阅读(如 .exe, .jpg)。
文件的打开与关闭 (P266)
打开文件 fopen
  • 格式: FILE *fp = fopen("文件名", "打开方式");
  • 常见打开方式(扩展):
    • "r" (read):只读。文件必须存在,否则报错。
    • "w" (write):只写。文件不存在则新建,存在则清空内容(慎用!)。
    • "a" (append):追加。文件不存在则新建,存在则在末尾追加。
    • "rb", "wb", "ab":对应的二进制文件操作。
    • "r+", "w+":读写模式(较少考,了解即可)。
关闭文件 fclose
  • 格式: fclose(文件指针);
  • 作用: 释放缓冲区,断开连接,防止数据丢失。
Q: 如何判断 fopen、fclose 函数执行成功?
  1. 判断 fopen 成功: 检查返回值是否为 NULL

    FILE *fp = fopen("data.txt", "r");
    if (fp == NULL) {
        printf("文件打开失败!\n");
        return -1; //exit(0);
    }
    
  2. 判断 fclose 成功: 检查返回值是否为 0

    • 返回 0:成功关闭。
    • 返回 EOF (-1):关闭失败。
文件读写操作详解 (P268 - P273)

不同函数用于读写不同类型的数据,考点在于区分它们的适用场景

Q: 各种读写函数的区别?
函数对 读函数 (Input) 写函数 (Output) 处理单位 适用场景
字符读写 fgetc(fp) fputc(ch, fp) 单个字符 逐个字符处理文本文件
字符串读写 fgets(str, n, fp) fputs(str, fp) 字符串/行 按行读取文本文件 (注意 fgets 会读 n-1 个字符)
格式化读写 fscanf(fp, ...) fprintf(fp, ...) 格式化数据 像 printf/scanf 一样读写含数字、文本的混合数据
数据块读写 fread(...) fwrite(...) 二进制块 读写结构体数组、图片等二进制数据 (效率最高)
  • P273 格式化读写补充:
    • fprintf(fp, "%d %s", age, name); :把数据格式化后写入文件(变成文本)。
    • fscanf(fp, "%d %s", &age, name); :从文件中按格式解析数据。

编译预处理 (P281)

1. 宏定义
  • 格式: #define 标识符 字符串
  • 例子: #define PI 3.14159#define S(a,b) a*b
Q: 预处理时对宏的处理过程?
  • 核心机制: 单纯的文本替换
  • 时机: 发生在编译(Compile)之前。预处理器扫描源代码,把所有的宏名(如 PI)直接换成对应的字符串(如 3.14159)。
  • 重要特性: 预处理阶段不做语法检查,也不做数值计算。它只是傻傻地把字符搬过去。
    • 易错点: #define N 2+3,代码中 N*N 会被替换成 2+3*2+3,而不是 25
image-4311fef1
2. 条件编译
Q: #ifdef 和 #if 的区别?
  1. #ifdef 标识符 (If Defined)
    • 作用: 判断某个宏是否已经被定义过(不管它的值是多少,哪怕是空的)。
    • 逻辑: 只要前面写过 #define 标识符,条件就成立。
    • 场景: 常用于防止头文件重复包含(Header Guard)。
  2. #if 表达式
    • 作用: 判断后面的常量表达式的值是否为真(非 0)。
    • 逻辑: 类似于 C 语言里的 if 语句,但只在编译前判断。如果表达式算出是 0,中间的代码直接被剪掉,不会被编译。
    • 场景: 用于版本控制,例如 #if VERSION > 2
image-a49d06ac image-17fd952a

按题型(推测)

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

(涵盖:基础、标识符、类型、运算符、流控、数组、指针、结构体、文件)

  1. 【基础】 C语言程序执行的入口是: A. 头文件 B. main 函数 C. 程序中的第一个函数 D. 预处理指令
  2. 【标识符】 下列合法的用户标识符是: A. 2ab (数字开头) B. int (关键字) C. _total D. a-b (减号非法)
  3. 【数据类型】 若有 definition unsigned short a = 0;,执行 a = a - 1; 后,a 的值是: A. -1 B. 0 C. 65535 (类型最大值) D. 编译错误
  4. 【常量与ASCII】 已知字符 'A' 的 ASCII 码是 65,则执行 printf("%d", 'C'); 的输出结果是: A. C B. 66 C. 67 D. c
  5. 【字符串常量】 字符串 "Hello" 在内存中占用的字节数是: A. 5 B. 6 (包含\0) C. 4 D. 8
  6. 【宏定义】 若有 #define M 2+3,则 M * 2 的值是: A. 10 B. 7 C. 8 (即 2+3*2) D. 编译出错
  7. 【运算符】 表达式 5 && 0 || 3 的值是(注意优先级): A. 0 B. 1 C. 3 D. 假
  8. 【自增陷阱】int k=0;,执行 while(k++ < 3); 后,k 的值是: A. 3 B. 4 (判断失败时已自增) C. 2 D. 5
  9. 【逻辑短路】int a=0, b=1;,执行 if(a++ && b++) 后,a 和 b 的值分别为: A. 1, 1 (左假右不执行) B. 1, 2 C. 0, 1 D. 0, 2
  10. 【输入输出】 使用 scanf 输入 double 类型变量时,必须使用的格式符是: A. %f B. %lf C. %d D. %ld
  11. 【数学函数】 要计算浮点数的绝对值,应调用的函数是: A. abs() B. fabs() C. sqrt() D. pow()
  12. 【if语句陷阱】 代码 if(a=5) 表示的含义是: A. 判断 a 是否等于 5 B. 语法错误 C. 将 5 赋值给 a,条件永远为真 D. 将 5 赋值给 a,条件永远为假
  13. 【Switch】 switch(exp) 语句中,表达式 exp 的类型不能是: A. int B. char C. short D. float
  14. 【二维数组】 下列二维数组定义合法的是(省略维度规则): A. int a[2][]={1,2,3}; B. int a[][3]={1,2,3}; (行可省,列不可省) C. int a[][]={1,2,3}; D. int a[][3];
  15. 【字符串比较】 strcmp("abc", "abd") 的返回值是: A. 0 B. 正数 C. 负数 ('c' < 'd') D. 1
  16. 【指针】 若有 int a=10, *p=&a;,则 *p 的值是: A. a 的地址 B. 10 C. p 的地址 D. 随机值
  17. 【函数】 关于形参和实参,下列说法正确的是: A. 形参不占内存 B. 实参和形参必须同名 C. 数据传递是单向的(实参传给形参) D. 函数内部修改形参,实参一定会变
  18. 【共用体】union {char c; int i;} u;,已知 int 占4字节,则 sizeof(u) 为: A. 1 B. 4 (取最大成员长度) C. 5 D. 8
  19. 【文件】 若文件打开失败,fopen 函数的返回值是: A. -1 B. 0 C. NULL D. EOF
  20. 【文件模式】 使用模式 "w" 打开文件时,若文件已存在,则: A. 在文件尾追加 B. 报错 C. 保留原内容 D. 清空原内容

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

(涵盖:格式符、关键字、库函数、计算细节)

  1. C语言中,无符号整型 (unsigned int) 的格式化输出控制符是 %u。
  2. long long 类型数据的输入输出格式控制符是 %lld。
  3. 字符串 "China" 实际占用的内存字节数是 6。
  4. 逻辑表达式 !5 的值是 0 (填0或1)。
  5. 数学函数 sqrt(x) 要求参数 x 必须是 非负 数。
  6. 数组下标从 0 开始。
  7. 结构体类型的关键字是 struct ,共用体类型的关键字是 union 。
  8. switch 语句中,用于跳出 switch 结构的关键字是 break 。
  9. 函数 strlen("Hello\0World") 的返回值是 5 (遇\0停止)。

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

(涵盖:基本概念易错点)

  1. ( √ ) 标识符 _123 是合法的 C 语言标识符。
  2. ( × ) 在 C 语言中,main 函数必须写在所有自定义函数的最前面。
  3. ( × ) 宏定义 #define N 10; (带分号) 是正确的,引用 N 时不会出错。
  4. ( √ ) do...while 循环的循环体至少会执行一次。
  5. ( × ) 数组名是一个地址常量,可以进行自增运算(如 a++)。
  6. ( √ ) 表达式 (a=3) 的值是 3,且该表达式的结果为“真”。
  7. ( × ) unsigned short 类型的变量可以正确存储负数(如 -5)。
  8. ( √ ) 假如 a < b,则条件运算符 a > b ? a : b 的结果是 b
  9. ( × ) 在文件操作中,文本文件和二进制文件的存储形式是完全一样的。
  10. ( √ ) 逻辑运算符 && 具有短路特性,若左操作数为假,则右操作数不执行。

程序阅读题(五题 差一道)

1、字符型变量

P33

第一题:字符变量的本质(对应教材 P33 例 2.10)

这道题的核心考点是:字符 'A' 和整数 65 在内存里是一回事。

#include <stdio.h>

int main()
{
    char c1, c2;
    c1 = 'A';    // 以字符形式赋值
    c2 = 65;     // 以ASCII码数值形式赋值(65对应'A'printf("%c, %c\n", c1, c2);  // 考点1:都以字符形式输出
    printf("%d, %d\n", c1, c2);  // 考点2:都以整数形式输出

    return 0;
}

问:上述程序的输出结果是什么?

答:

A, A
65, 65
  • 赋值区别c1 赋的是字符 'A'c2 赋的是整数 65。但在计算机看来,它俩存的都是二进制的 65。
  • 输出区别
    • %c:把 65 当作字符看 -> 输出 A
    • %d:把 65 当作数字看 -> 输出 65

第二题:大小写转换运算(对应教材 P33 例 2.11)

这道题的核心考点是:小写字母 = 大写字母 + 32

#include <stdio.h>

int main()
{
    char c1, c2;
    c1 = 'A';    // 初始化为大写 A
    c2 = 'B';    // 初始化为大写 B

    c1 = c1 + 32;  // 考点:大写转小写运算
    c2 = c2 + 32;  // 66 + 32 = 98

    printf("%c, %c\n", c1, c2);  // 输出转换后的字符
    printf("%d, %d\n", c1, c2);  // 输出转换后的ASCII码数值

    return 0;
}

问:上述程序的输出结果是什么?

答:

a, b
97, 98
  • 运算逻辑
    • c1 原本是 'A'(65),执行 65 + 32 = 97。97 对应的字符是 'a'
    • c2 原本是 'B'(66),执行 66 + 32 = 98。98 对应的字符是 'b'
  • 大写转小写,加 32;小写转大写,减 32。

2、sizeof()

第一题:基本数据类型的长度(对应教材 P34 例 2.15)

#include <stdio.h>
int main()
{
    printf("char 类型占 %d 字节\n", sizeof(char));
    printf("short int 类型占 %d 字节\n", sizeof(short int));
    printf("int 类型占 %d 字节\n", sizeof(int));
    printf("float 类型占 %d 字节\n", sizeof(float));
    printf("double 类型占 %d 字节\n", sizeof(double));
    return 0;
}
char 类型占 1 字节
short int 类型占 2 字节
int 类型占 4 字节
float 类型占 4 字节
double 类型占 8 字节

第二题:sizeof 陷阱与混合运算(对应教材 P34 例 2.16)

混合了类型转换运算符优先级字符串长度三个考点。

#include <stdio.h>
int main()
{
    int a, b;

    // 坑点1:表达式类型自动提升
    a = sizeof(3 + 5.0);

    // 坑点2:优先级问题
    b = sizeof 3 + 5.0;

    // 坑点3:字符串包含 \0
    printf("%d, %d, %d\n", a, b, sizeof("China"));
    return 0;
}
8, 9, 6
  • 分析 a = sizeof(3 + 5.0);
    • 括号里是 3(int) + 5.0(double)。
    • C 语言规则:小类型遇到大类型,自动变大。所以 int 变成 double 参与运算。
    • 结果类型是 double
    • sizeof(double) = 8。所以 a = 8
  • 分析 b = sizeof 3 + 5.0; (最容易错!)
    • 这里没有括号!
    • 优先级规则sizeof 的优先级比 + 加法
    • 计算步骤:
      1. 先算 sizeof 3:3 是 int,大小为 4
      2. 再算 4 + 5.0:变成 4.0 + 5.0 = 9.0
      3. 最后赋值给 int b9.0 被强制截断成整数 9
    • 所以 b = 9
  • 分析 sizeof("China")
    • 字符串 "China" 有 5 个字母。
    • 切记:字符串末尾自动加 \0
    • 占地大小 = 5 + 1 = 6 字节。

3、函数递归

void fun(int a)
{
    if(!a)               // 如果 a0
        printf("%d",a);  // 打印 0
    else
        fun(a-1);        // 递归调用,参数减 1

    printf("%d",a);      // 【注意】这行代码在 else 外面,无论走哪个分支,最后都会执行!
}

int main() { fun(2); ... }

执行流程拆解 (Stack Trace)

  1. 第一层 fun(2)
    • a=2!2 为假,走 else
    • 调用 fun(1) ... (暂停,等待子函数返回)
  2. 第二层 fun(1)
    • a=1!1 为假,走 else
    • 调用 fun(0) ... (暂停,等待子函数返回)
  3. 第三层 fun(0)
    • a=0!0 为真,走 if
    • 执行 printf("%d", 0) -> 输出 0
    • if 结束,继续向下。
    • 执行 printf("%d", 0) -> 输出 0
    • 函数结束,返回到上一层。
  4. 回到第二层 fun(1)
    • fun(0) 调用结束。
    • 继续执行下面的 printf("%d", 1) -> 输出 1
    • 函数结束,返回到上一层。
  5. 回到第一层 fun(2)
    • fun(1) 调用结束。
    • 继续执行下面的 printf("%d", 2) -> 输出 2
    • 函数结束。

最终输出结果

0012

4、宏定义

#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);
}
0

5、可能是带有 continuebreak 的循环,或者是逻辑短路测试。

算法设计题(两题)

1、循环(二层)

第一题:编程计算3×3的矩阵的两条对角线上所有元素之和。

image-6b1e8bb4

#include<stdio.h>
int main()
{
    int a[3][3] = {9, 4, 7, 5, 6, 1, 8, 3, 2}, sum = 0, i, j;
    printf("数组:\n");

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

    // 计算求和部分
    for(i = 0; i < 3; i++)                /* 求主副对角线元素之和 */
        for(j = 0; j < 3; j++)
            // i==j 是主对角线,i+j==2 是副对角线(因为维度是3,最大下标是2)
            if(i == j || i + j == 2)      /* 判断是否为主副对角线元素 */
                sum = sum + a[i][j];

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

第二题:编写程序,求3×4的矩阵的转置矩阵。

image-f248d54b
#include <stdio.h>

int main()
{
    // a34列,b 是 43列
    int a[3][4] = {2, 5, 7, 9, 6, 14, 20, 8, 15, 0, 12, 3}, b[4][3], i, j;

    printf("数组 a:\n");
    for(i = 0; i < 3; i++)          /* 输出矩阵 a */
    {
        for(j = 0; j < 4; j++)
            printf("%-4d", a[i][j]);
        printf("\n");
    }

    // 转置核心逻辑:将 a 的行 i 给 b 的列 i,a 的列 j 给 b 的行 j
    for(i = 0; i < 4; i++)          /* 矩阵 a 转置成矩阵 b */
        for(j = 0; j < 3; j++)
            b[i][j] = a[j][i];

    printf("数组 b:\n");
    for(i = 0; i < 4; i++)          /* 输出矩阵 b */
    {
        for(j = 0; j < 3; j++)
            printf("%-4d", b[i][j]);
        printf("\n");
    }

    return 0;
}

2、字符数组指针

将26个英文字母以每行10个的形式输出:

image-74458c0d
#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;
}

程序设计题(两题)

1、画流程图(循环语句)

求两个数a和b的最大公约数、最小公倍数 流程图

P96,例题5.17最大公约数,最小公倍数流程图画出来

/* 【例 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

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