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语言已有的关键字
但这题主要考察的是能不能以数字开头 记住:不能
有时候看见下划线开头会感觉奇怪 但其实是可以的
这题应该会以选择题的形式出现 会做这几道就没问题 掌握了
-
练习:
-
数据类型
在 C 语言中,数据类型通常分为两大类:
- 基本数据类型(分为整型、字符型、实型(浮点型))
用于表示最基本的数据,包括:
int(整型)float(单精度浮点型)double(双精度浮点型)char(字符型)
- 构造数据类型
由基本数据类型组合或派生而来,包括:
- 数组(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。
- 类比: 就像机械里程表,从
- 练习:
-
long long (超长整型):
- 用途: 当标准
int(通常 ±21亿左右) 不够存时使用(如阶乘、斐波那契数列)。 - 内存: 标准规定至少 64 位,通常占用 8 个字节。
- 格式控制符: 输入输出必须使用
%lld。scanf("%lld", &a);printf("%lld", a);如果误用%d,会导致数据截断或打印出错误的负数/乱码。 - 练习
-
不过很大可能只是简单的出一道填空题 问:
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)。
- 练习:
-
字符串常量
字符串常量是由一对双引号“”括起来的零个或多个字符序列,如“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。
- 练习:
-
符号常量
在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。
- 练习:
-
运算符
优先级
- 核心概念
在 C 语言中,一个表达式不是从左到右随便算的,而是由运算符优先级和结合性共同决定计算顺序。
- 优先级 (Precedence):决定“谁先算”。优先级高的运算符先结合(例如乘除先于加减)。
- 结合性 (Associativity):当优先级相同的时候,决定“怎么算”。
- 左结合:从左向右算(大多数运算符,如
a - b + c)。 - 右结合:从右向左算(主要是单目运算符、赋值运算符和三目运算符)。
- 左结合:从左向右算(大多数运算符,如
- 常见运算符优先级速查表(高 → 低)
| 优先级 | 运算符 | 说明 | 结合性 |
|---|---|---|---|
| 最高 | !, ++, --, (type), sizeof |
单目运算符 (逻辑非, 自增, 强转等) | 右到左 |
| ↓ | *, /, % |
算术运算 (乘, 除, 取余) | 左到右 |
| ↓ | +, - |
算术运算 (加, 减) | 左到右 |
| ↓ | <, <=, >, >= |
关系运算 (大小比较) | 左到右 |
| ↓ | ==, != |
关系运算 (相等性判断) | 左到右 |
| ↓ | && |
逻辑与 | 左到右 |
| ↓ | ` | ` | |
| 最低 | = |
赋值运算 | 右到左 |
- 短路求值
在逻辑表达式中,如果在计算过程中,表达式的最终真假已经可以确定,那么剩余的子表达式将不会再被求值。
在 C 语言中,只有两个运算符具有短路行为:
| 运算符 | 名称 |
|---|---|
&& |
逻辑与 |
| ` |
①:逻辑与 &&
A && B
若 A == 0(假)
整个表达式结果已确定为假
B 不再计算
➡️ 左假即停
**②:**逻辑或 ||
A || B
- 若
A != 0(真) - 整个表达式结果已确定为真
- B 不再计算
➡️ 左真即停
短路的执行顺序(非常关键)
(1) 固定从左到右
&&和||一定从左向右求值- 不允许交换顺序
- 不允许并行计算
(2)与“优先级”的关系
优先级决定表达式如何分组 短路决定分组后的子表达式是否继续计算
两者是不同层面的规则,不能混为一谈。
- 练习
-
问题:
a=5; b=4;
int k = a > b; k = ?
a>b -> 5>4 -> true -> 1
k=1
逻辑运算(真假与数值的关系)
- C 语言中的“真假”规则
C 语言并没有专门的 bool(C99 之前),逻辑判断遵循:
- 0 → 假(false)
- 非 0 → 真(true)
这是理解所有逻辑表达式的基础。
- 常见逻辑运算符含义
| 运算符 | 含义 | 结果 |
|---|---|---|
!a |
逻辑非 | 真→0,假→1 |
a && b |
逻辑与 | 同真才真 |
| `a | b` |
注意:
-
逻辑运算的结果只能是 0 或 1
-
即使参与运算的是任意整数,结果仍被“压缩”为真假值
-
练习
-
逻辑运算符和逻辑表达式(短路是拔尖题 必考)
! && ||
- 优先级阶梯:
!(非) > 算术运算符 (+ - * /) > 关系运算符 (> < ==) >&&(与) >||(或) > 赋值 (=)- 口诀: “非”最高,“与”比“或”大。
- 例子:
a || b && c等价于a || (b && c)。
- 真假的定义:
- 输入时: 非 0 即为真,0 为假。(例如:
-5是真,3.14是真,只有0是假)。 - 输出时: 逻辑运算的结果只有两个值:1 (真) 或 0 (假)。
5 && 4的结果是1,绝不是4或20!
- 输入时: 非 0 即为真,0 为假。(例如:
- 【核心难点】短路法则(懒人法则):
&&(与) 的短路: 如果左边是 假 (0),右边完全不执行。(因为一假则假,没必要看了)。||(或) 的短路: 如果左边是 真 (非0),右边完全不执行。(因为一真则真,没必要看了)。
典型例题分析(短路陷阱)
int a = 0, b = 1;
if (a++ && b++) {
// ...
}
printf("a=%d, b=%d", a, b);
-
分析:
- 执行
a++ && ...。由于是后缀++,先取a的旧值 0 参与逻辑运算。 0是假。对于&&来说,左边为假,整体必为假。触发短路!- 后果: 右边的
b++直接被跳过,根本不执行。 - 之后
a完成自增变为 1。
- 执行
-
结果:
a=1,b=1(b 没变)。 -
练习题:
-
一句话总结: 逻辑题核心看 “短路”:&& 遇假就停,|| 遇真就停,后面有 i++ 也别管它!
填空题:
int x=33,y=4;
!x+x/y=?
!33 → 0
0 + x / y
33 / 4 → 8
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。
- 练习:
-
填空题:
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)。
- 先看最外层:
- 练习
-
看见 ? 就问自己“是真的吗?”,真就拿走冒号前面的,假就拿走后面的。遇到一长串,先把最右边的括号括起来看。
输入输出
基本输入输出函数
- 输出函数:
printf
printf("格式字符串", 参数列表);
- 作用:把数据输出到屏幕
- 格式字符串决定“怎么显示”
- 参数列表决定“显示什么值”
⚠️ 参数个数必须 ≥ 格式符个数
printf("%d", a, b); // 只输出 a,b 被忽略
- 输入函数:
scanf
scanf("格式字符串", 地址列表);
- 作用:从键盘读入数据,存入变量
- 本质:
scanf要“往变量里写数据”
scanf 的核心规则
- 为什么要加
&(取地址符)
规则:
scanf往变量里存数据,必须知道变量住在哪里(内存地址)。
int a; scanf("%d", &a); // 正确
- 必须加
&的情况
- 普通变量:
int / float / char / double
scanf("%d", &a); // ✅
- 不用加
&的特殊情况(了解)
- 数组名
- 指针变量本身
- 错题典范
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 表示补零 |
宽度的本质
- 宽度是“最小输出宽度”
- 如果数据本身更宽:
- 直接原样输出
- 不会截断
精度的本质(浮点数特有)
- 精度控制的是“小数位数”
vdouble x = 3.14159;
printf("%.2f", x); // 3.14 发生 四舍五入
- 不只是简单截断
- 精度优先于宽度
printf("%4.1f", 12.34); // 12.3
- 先按精度算结果
- 再检查宽度是否足够
格式字符串与参数的对应关系
规则
一个格式符,对应一个参数
printf("%d %d", a, b); // 输出两个整数
printf("%d", a, b); // 只输出 a
⚠️ 多给参数不会报错,但没用
scanf 中格式符的注意点
%d与输入形式
scanf("%d", &a);
- 自动跳过空白符(空格、回车、Tab)
- 读取一个十进制整数
%d≠-%d
-
-不是格式符 -
%-5d中的-是 printf 的对齐控制 -
在
scanf中没有“左对齐/右对齐”的概念 -
练习:
-
问题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'(换行符) —— 容易以为还是读下一个字母,错!
- 第1次读到:
-
练习题:
-
数学库函数
在头文件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)。这是一大坑点!
- 考点: C 语言里的
要调用数学函数时,在#include命令行应该包含 math.h
- 练习题:
-
分支结构
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(小数不能精确比较),或者字符串。 -
练习:
-
问题1:
if (条件) {
a = 1;
b = 2;
}
if (条件)
a = 1;
b = 2;
if (条件)
a = 1, b =2;
if (条件);
a = 1;
b = 2;
注意区分 看准括号和分号的位置
问题2:
switch语句中,各个case和default的出现次序影响执行结果吗?
循环结构
for 循环
- 语法
for (初始化; 条件; 更新) {
// 循环体
}
- 初始化:循环变量初始值,只执行一次
- 条件:循环执行的判断条件(真/假)
- 更新:每次循环结束后执行(通常自增/自减)
-
执行流程
-
执行 初始化
-
判断 条件
- 真 → 执行循环体
- 假 → 结束循环
-
执行 循环体
-
执行 更新
-
回到 条件判断
-
重复步骤 2-5,直到条件为假
-
特点
- 循环次数通常确定或可控
- 循环变量在循环外可用
- 适合 计数循环(for i=0;i<n;i++)
while 循环
- 语法
while (条件) {
// 循环体
}
-
执行流程
-
判断 条件
- 真 → 执行循环体
- 假 → 跳出循环
-
执行循环体
-
回到 条件判断
-
重复步骤 1-3
-
特点
- 循环次数不确定,取决于条件
- 条件先判断,循环体可能一次都不执行
- 适合 先判断再执行的循环
do…while 循环
- 语法
do {
// 循环体
} while (条件);
-
执行流程
-
执行 循环体
-
判断 条件
- 真 → 回到步骤 1
- 假 → 结束循环
-
特点
- 先执行后判断 → 循环体至少执行一次
- 循环次数不确定,取决于条件
- 适合 至少执行一次的循环
| 循环类型 | 条件位置 | 是否至少执行一次 | 典型应用 |
|---|---|---|---|
for |
前(循环开始前) | 不一定 | 已知循环次数 |
while |
前 | 不一定 | 条件判断先行 |
do…while |
后 | 一定 | 至少执行一次 |
-
练习:
-
数组
一维数组
- 定义与下标
- 定义格式:
类型 数组名[常量表达式];- 例如:
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]是越界,考试常考!
- 初始化(赋初值)
- 完全初始化:
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。
二维数组
- 内存存储(线性)
- 二维数组在内存中是**“按行存放”**的,也是连续的一条线,不是真的方块。
- 先存第 0 行,再存第 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
- 【核心考点】省略维度的规则
声明二维数组并进行初始化时:
- 第一维(行数):可以省略。
- 第二维(列数):不可省略!
| 写法 | 合法性 | 解析 |
|---|---|---|
int a[][3] = {1,2,3,4}; |
✅ 合法 | 知道一行 3 个,总共 4 个数据 \(\rightarrow\) 算出需要 2 行。 |
int a[2][] = {1,2,3,4}; |
❌ 非法 | 不知道一行几个,无法切割内存。 |
int a[][] = {1,2,3,4}; |
❌ 非法 | 两个都不知道,彻底懵圈。 |
问题:
声明二维数组时,可以省略哪个维度?
只能省略第一个维度(行),绝对不能省略第二个维度(列)。
字符串
- 字符串的特殊性
char s1[] = {'h', 'e', 'l', 'l', 'o'};- 这是普通字符数组,长度 5,占用 5 字节。
char s2[] = "hello";- 这是字符串,长度 5,占用 6 字节(因为自动加了
\0)。
- 这是字符串,长度 5,占用 6 字节(因为自动加了
- 常用操作陷阱
- 赋值: 只能在定义时初始化赋值。
- ✅
char s[10] = "abc"; - ❌
char s[10]; s = "abc";(数组名是常量地址,不能被赋值!)
- ✅
- 输入:
scanf("%s", s);(注意:数组名s本身就是地址,不需要加 &)。
问题:
sizeof(“hello”)=?
6
末尾有\0
字符串比较函数strcmp()
- 基础档案
- 头文件:
#include <string.h>(考试填空常考) - 功能: 按照 ASCII 码值,从左到右一个个字符比较两个字符串。
- 原型:
int strcmp(const char *s1, const char *s2);
- 三个返回值
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),所以前者小,结果是负数。
if判断陷阱
❌ 错误写法:
// 很容易以为 strcmp 返回 1 代表相等,大错特错!
if (strcmp(s1, s2)) {
printf("相等");
}
C
// 很多初学者以为 strcmp 返回 1 代表相等,这是大错特错!
if (strcmp(s1, s2)) {
printf("相等");
}
解析: 如果 s1 和 s2 真的相等,strcmp 返回 0。在 C 语言中,0 代表假。所以上面的代码根本不会执行 printf。
✅ 正确写法:
if (strcmp(s1, s2) == 0) {
printf("相等");
}
- 练习:
-
想要判断相等,必须写 <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:如何使用结构体变量?
有两种方式,取决于你手里拿的是“变量本身”还是“指针”。
- 通过变量名访问 (点号
.)- 格式:
结构变量名.成员名 - 例子:
s.age = 18;
- 格式:
- 通过指针访问 (箭头
->)-
格式:
结构指针->成员名 -
例子:
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)
- 核心概念
- 定义: 使几个不同类型的变量共用同一段内存空间。
- 大小: 共用体变量所占的内存长度,等于其最长成员的长度。
- 特点(喜新厌旧):
- 同一时刻只能存储其中一个成员的值。
- 对一个成员赋值,会覆盖掉其他成员的值(因为大家住同一个房间)。
#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.c和s.i的起始地址是一样的。当你执行s.i = 99时,原本s.c占用的那个字节也被 99 的数据覆盖了(或者成为了 99 的一部分)。最后读s.i,当然就是刚刚存进去的 99。
链表(Brief Intro) (P246)
- 节点的定义
链表节点 = 数据域 + 指针域。
struct Node {
int data; // 数据域:存数据
struct Node *next; // 指针域:存下一个节点的地址
};
- 常见考点
- 头指针 (head): 指向链表第一个节点的指针。
- 结束标志: 最后一个节点的
next指针指向NULL。 - 遍历:
p = p->next;(让指针 p 移动到下一个节点)。
文件
文件类型 (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 函数执行成功?
-
判断 fopen 成功: 检查返回值是否为
NULL。FILE *fp = fopen("data.txt", "r"); if (fp == NULL) { printf("文件打开失败!\n"); return -1; // 或 exit(0); } -
判断 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。
- 易错点:
2. 条件编译
Q: #ifdef 和 #if 的区别?
#ifdef 标识符(If Defined)- 作用: 判断某个宏是否已经被定义过(不管它的值是多少,哪怕是空的)。
- 逻辑: 只要前面写过
#define 标识符,条件就成立。 - 场景: 常用于防止头文件重复包含(Header Guard)。
#if 表达式- 作用: 判断后面的常量表达式的值是否为真(非 0)。
- 逻辑: 类似于 C 语言里的
if语句,但只在编译前判断。如果表达式算出是 0,中间的代码直接被剪掉,不会被编译。 - 场景: 用于版本控制,例如
#if VERSION > 2。
按题型(推测)
一、选择题(共20小题,每小题1分,共20分)
(涵盖:基础、标识符、类型、运算符、流控、数组、指针、结构体、文件)
- 【基础】 C语言程序执行的入口是:
A. 头文件 B.
main函数 C. 程序中的第一个函数 D. 预处理指令 - 【标识符】 下列合法的用户标识符是:
A.
2ab(数字开头) B.int(关键字) C._totalD.a-b(减号非法) - 【数据类型】 若有 definition
unsigned short a = 0;,执行a = a - 1;后,a的值是: A. -1 B. 0 C. 65535 (类型最大值) D. 编译错误 - 【常量与ASCII】 已知字符
'A'的 ASCII 码是 65,则执行printf("%d", 'C');的输出结果是: A. C B. 66 C. 67 D. c - 【字符串常量】 字符串
"Hello"在内存中占用的字节数是: A. 5 B. 6 (包含\0) C. 4 D. 8 - 【宏定义】 若有
#define M 2+3,则M * 2的值是: A. 10 B. 7 C. 8 (即 2+3*2) D. 编译出错 - 【运算符】 表达式
5 && 0 || 3的值是(注意优先级): A. 0 B. 1 C. 3 D. 假 - 【自增陷阱】 若
int k=0;,执行while(k++ < 3);后,k 的值是: A. 3 B. 4 (判断失败时已自增) C. 2 D. 5 - 【逻辑短路】 若
int a=0, b=1;,执行if(a++ && b++)后,a 和 b 的值分别为: A. 1, 1 (左假右不执行) B. 1, 2 C. 0, 1 D. 0, 2 - 【输入输出】 使用
scanf输入double类型变量时,必须使用的格式符是: A.%fB.%lfC.%dD.%ld - 【数学函数】 要计算浮点数的绝对值,应调用的函数是:
A.
abs()B.fabs()C.sqrt()D.pow() - 【if语句陷阱】 代码
if(a=5)表示的含义是: A. 判断 a 是否等于 5 B. 语法错误 C. 将 5 赋值给 a,条件永远为真 D. 将 5 赋值给 a,条件永远为假 - 【Switch】
switch(exp)语句中,表达式exp的类型不能是: A.intB.charC.shortD.float - 【二维数组】 下列二维数组定义合法的是(省略维度规则):
A.
int a[2][]={1,2,3};B.int a[][3]={1,2,3};(行可省,列不可省) C.int a[][]={1,2,3};D.int a[][3]; - 【字符串比较】
strcmp("abc", "abd")的返回值是: A. 0 B. 正数 C. 负数 ('c' < 'd') D. 1 - 【指针】 若有
int a=10, *p=&a;,则*p的值是: A. a 的地址 B. 10 C. p 的地址 D. 随机值 - 【函数】 关于形参和实参,下列说法正确的是: A. 形参不占内存 B. 实参和形参必须同名 C. 数据传递是单向的(实参传给形参) D. 函数内部修改形参,实参一定会变
- 【共用体】 若
union {char c; int i;} u;,已知int占4字节,则sizeof(u)为: A. 1 B. 4 (取最大成员长度) C. 5 D. 8 - 【文件】 若文件打开失败,
fopen函数的返回值是: A. -1 B. 0 C.NULLD.EOF - 【文件模式】 使用模式
"w"打开文件时,若文件已存在,则: A. 在文件尾追加 B. 报错 C. 保留原内容 D. 清空原内容
二、填空题(共10空,每空1分,共10分)
(涵盖:格式符、关键字、库函数、计算细节)
- C语言中,无符号整型 (
unsigned int) 的格式化输出控制符是 %u。 long long类型数据的输入输出格式控制符是 %lld。- 字符串 "China" 实际占用的内存字节数是 6。
- 逻辑表达式
!5的值是 0 (填0或1)。 - 数学函数
sqrt(x)要求参数 x 必须是 非负 数。 - 数组下标从 0 开始。
- 结构体类型的关键字是 struct ,共用体类型的关键字是 union 。
- 在
switch语句中,用于跳出switch结构的关键字是 break 。 - 函数
strlen("Hello\0World")的返回值是 5 (遇\0停止)。
三、判断题(共10小题,每小题1分,共10分)
(涵盖:基本概念易错点)
- ( √ ) 标识符
_123是合法的 C 语言标识符。 - ( × ) 在 C 语言中,
main函数必须写在所有自定义函数的最前面。 - ( × ) 宏定义
#define N 10;(带分号) 是正确的,引用N时不会出错。 - ( √ )
do...while循环的循环体至少会执行一次。 - ( × ) 数组名是一个地址常量,可以进行自增运算(如
a++)。 - ( √ ) 表达式
(a=3)的值是 3,且该表达式的结果为“真”。 - ( × )
unsigned short类型的变量可以正确存储负数(如 -5)。 - ( √ ) 假如
a < b,则条件运算符a > b ? a : b的结果是b。 - ( × ) 在文件操作中,文本文件和二进制文件的存储形式是完全一样的。
- ( √ ) 逻辑运算符
&&具有短路特性,若左操作数为假,则右操作数不执行。
程序阅读题(五题 差一道)
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的优先级比+加法高。 - 计算步骤:
- 先算
sizeof 3:3 是 int,大小为 4。 - 再算
4 + 5.0:变成4.0 + 5.0 = 9.0。 - 最后赋值给
int b:9.0被强制截断成整数 9。
- 先算
- 所以
b = 9。
- 分析
sizeof("China")- 字符串 "China" 有 5 个字母。
- 切记:字符串末尾自动加
\0。 - 占地大小 = 5 + 1 = 6 字节。
3、函数递归
void fun(int a)
{
if(!a) // 如果 a 是 0
printf("%d",a); // 打印 0
else
fun(a-1); // 递归调用,参数减 1
printf("%d",a); // 【注意】这行代码在 else 外面,无论走哪个分支,最后都会执行!
}
int main() { fun(2); ... }
执行流程拆解 (Stack Trace)
- 第一层
fun(2):a=2。!2为假,走else。- 调用
fun(1)... (暂停,等待子函数返回)
- 第二层
fun(1):a=1。!1为假,走else。- 调用
fun(0)... (暂停,等待子函数返回)
- 第三层
fun(0):a=0。!0为真,走if。- 执行
printf("%d", 0)-> 输出 0 - if 结束,继续向下。
- 执行
printf("%d", 0)-> 输出 0 - 函数结束,返回到上一层。
- 回到第二层
fun(1):fun(0)调用结束。- 继续执行下面的
printf("%d", 1)-> 输出 1 - 函数结束,返回到上一层。
- 回到第一层
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、可能是带有 continue 或 break 的循环,或者是逻辑短路测试。
算法设计题(两题)
1、循环(二层)
第一题:编程计算3×3的矩阵的两条对角线上所有元素之和。
#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的矩阵的转置矩阵。
#include <stdio.h>
int main()
{
// a 是 3行4列,b 是 4行3列
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个的形式输出:
#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;
}
2、估计还有一道应该是分支语句
题目一:寻找最大的数
题目二:有3个数 a, b, c,要求按大小顺序把它们输出
题目三:判断一个数 n 能否同时被 3 和 5 整除
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;
}

💬 评论