简介:C语言笔试大全是一份面向求职者与在校学生的C语言基础知识点梳理文档,聚焦笔试常见考点,帮助读者在面试与考试前快速回顾核心概念。内容覆盖static关键字用途、引用与指针区别、实时系统特性、全局与局部变量的内存分布、平衡二叉树定义、堆栈溢出成因、虚函数限制、冒泡排序时间复杂度、float零值比较写法、TCP/IP协议层次、ARP地址转换、IP地址编码、switch参数类型、extern引用全局变量及循环链表计数输出等经典题目,并附有代码输出分析与找错练习。资源包共1个doc文件,约225KB,以问答条目形式组织,便于按知识点逐条查阅与自测。目前已有258人学习,适合需要系统刷题、查漏补缺的C语言笔试备考者使用。
1. C语言笔试的知识地图:从关键字到内存模型的一条主线
C语言笔试看着像一堆散题,其实背后有一条很硬的主线:关键字语义 → 内存布局 → 指针与数组 → 函数与链接 → 数据结构与算法 → 操作系统与网络常识。你把这六块串起来,绝大多数笔试题都能归位;串不起来,就只能靠背答案,换个问法立刻翻车。
这份《C语言笔试大全》的价值不在于题目多,而在于它把高频考点按“概念—陷阱—代码输出”三层摆在一起。适合两类人:一是正在系统学 C、需要一张复习地图的人;二是刷过题但知识点是散的、想查漏补缺的人。下面按模块拆开,重点标出最容易踩坑的地方。
2. 关键字与存储类:static、const、volatile 的语义边界
2.1 static 的三层含义要分开记
static 是笔试出现频率最高的关键字之一,很多人只记得“静态”,但它在不同位置含义不同,必须按场景拆开:
| 使用位置 | 作用 | 生命周期 | 作用域 |
|---|---|---|---|
| 全局变量前 | 限制作用域到本文件 | 程序运行期 | 本编译单元 |
| 局部变量前 | 改变存储方式到静态区 | 程序运行期 | 仍限于函数内 |
| 函数前 | 限制函数链接属性 | 程序运行期 | 本编译单元 |
梳理下来会发现一个容易混的点:static 修饰局部变量改变的是生命周期,修饰全局变量和函数改变的是作用域。这两件事经常被一句话带过,导致选择题一换场景就错。
#include <stdio.h> void counter(void) { static int n = 0; // 只初始化一次,存放在静态区 n++; printf("%d\n", n); } int main(void) { counter(); // 输出 1 counter(); // 输出 2 counter(); // 输出 3 return 0; }这段代码的关键在static int n = 0;:初始化只执行一次,后续调用沿用上一次的值。如果去掉 static,每次调用都会重新初始化为 0,输出全是 1。笔试里常把它和“局部变量存放在栈上”混着考,看到“只初始化一次”就要条件反射想到静态存储区。
2.2 const 与 volatile 的读法
const 的难点不在“常量”,而在它修饰谁。判断口诀是从变量名往外读:
const int *p; // p 指向的内容不可改,p 本身可改 int * const p; // p 本身不可改,指向的内容可改 const int * const p; // 两者都不可改volatile 告诉编译器“这个值可能被外部改变”,每次都要从内存重新读,不能优化进寄存器。典型场景是硬件寄存器、中断里修改的变量、多线程共享标志。笔试常问“volatile 能不能保证原子性”,答案是不能,它只解决可见性和优化问题。
2.3 引用与指针的区别(C++ 交叉考点)
这份资料里把引用和指针放在一起对比,虽然 C 本身没有引用,但笔试常混考:
- 引用必须初始化,指针可以不初始化;
- 引用初始化后不能改绑,指针可以改变指向;
- 不存在空引用,但存在空指针。
注意最后一条:空引用在语法上不允许,但通过解引用空指针构造引用属于未定义行为,不是“合法空引用”。
3. 内存布局与变量生存期:栈、堆、静态区的分工
3.1 四块内存要能对上号
| 区域 | 存放内容 | 分配方式 | 典型问题 |
|---|---|---|---|
| 栈 | 局部变量、函数参数、返回地址 | 系统自动 | 栈溢出、返回局部变量地址 |
| 堆 | malloc/calloc/realloc 申请的内存 | 手动申请释放 | 内存泄漏、野指针 |
| 静态区 | 全局变量、static 变量、字符串常量 | 编译期确定 | 误改字符串常量 |
| 代码区 | 函数体机器指令 | 只读 | 一般笔试不深究 |
最容易错的是“全局变量存在静态数据库”这种说法,规范表述是静态存储区。局部变量在栈上,动态申请的内存在堆上,这三句话要能脱口而出。
3.2 字符串常量与字符数组的地址比较
资料里有一道经典输出题:
char str1[] = "abc"; char str2[] = "abc"; const char *str5 = "abc"; const char *str6 = "abc"; printf("%d %d\n", str1 == str2, str5 == str6);输出是0 1。原因是 str1、str2 是两个独立数组,各有各的栈空间;str5、str6 是指针,指向同一块字符串常量区。这里最容易翻车的是把数组名当指针直接比较,忘了数组名在表达式里退化为首元素地址,但两个数组的地址本来就不同。
3.3 返回局部变量地址为什么危险
char *get_str(void) { char buf[16] = "hello"; return buf; // 返回栈上地址,函数结束即失效 }函数返回后栈帧回收,buf 所在空间可能被后续调用覆盖,拿到的是悬空指针。正确做法是让调用方传缓冲区,或用 malloc 在堆上分配并由调用方释放。笔试里这类题常伪装成“输出为什么是乱码”,本质就是生命周期判断。
4. 指针与数组:最容易出输出题的深水区
4.1 数组名、&数组名、指针加一的区别
资料里有一道很典型的题:
int a[5] = {1, 2, 3, 4, 5}; int *ptr = (int *)(&a + 1); printf("%d, %d\n", *(a + 1), *(ptr - 1));输出是2, 5。a + 1是首元素地址加一个 int,指向 a[1];&a + 1是数组指针加一,跳过整个数组,指向 a[5] 之后的位置,再转成 int* 减一,回到 a[4]。这里的关键是指针加一加多少,取决于指针类型,不是固定加一个字节。
4.2 sizeof 在函数参数里会退化
void upper(char str[]) { // 这里的 str 已退化为 char*,sizeof(str) 是指针大小 size_t n = sizeof(str) / sizeof(str[0]); }函数形参写成数组,实际传递的是指针,sizeof 测不到数组长度。资料里专门点了这个坑:函数外sizeof(str)/sizeof(str[0])能得到 6,函数内只能得到指针字节数。复习时记住一句话:sizeof 看类型,数组一旦退化为指针就不再是数组。
4.3 函数指针数组的读法
int (*s[10])(int)是笔试常客。从里往外读:s 是数组,有 10 个元素,每个元素是指针,指向参数为 int、返回 int 的函数。对比int *s[10]是指针数组,int (*s)[10]是数组指针。这三种写法建议手写三遍,考场上才不会临时推。
5. 函数、链接与预处理:编译期和链接期的分界线
5.1 extern 与头文件引用的报错时机
资料里对比了两种引用全局变量的方式:包含头文件声明,或直接用 extern。差别在报错阶段——头文件里写错名字,编译期就报;extern 写错名字,编译期不报,链接期才报“未定义符号”。这个区别在选择题里经常出现,记法是:声明不分配空间,定义才分配空间。
5.2 宏的副作用与 inline 的差别
#define SQUARE(a) ((a) * (a)) int b = SQUARE(a++); // 展开后 a 被自增两次宏是文本替换,参数带副作用就会翻车。C 里短小频繁调用的函数常用宏,C++ 里用 inline。笔试问“宏和函数的区别”,要答出:宏在预处理阶段展开、无类型检查、可能多次求值;函数有类型检查、有调用开销。
5.3 预编译头解决什么问题
资料提到预编译的适用场景:大型代码体不常改动、多个模块共用一组标准包含文件和编译选项。把公共头预编译成一个预编译头,能减少重复解析时间。这不是语法考点,但笔试和面试里常作为“编译优化”背景题出现,知道适用条件即可。
6. 数据结构与算法小题:栈、队列、排序与链表
6.1 两个栈实现队列的思路
入队直接 push 到栈 A;出队时若栈 B 为空,把 A 全部 pop 并 push 到 B,再从 B 弹出栈顶。平摊复杂度 O(1)。这里容易错的是“每次出队都倒腾一遍”,那样复杂度会退化。判断条件要写清楚:只有 B 为空才倒。
6.2 冒泡排序与平衡二叉树的定义
冒泡排序时间复杂度 O(n²),这是基础送分点。平衡二叉树的定义要答完整:左右子树都是平衡二叉树,且左右子树深度差的绝对值不大于 1。只答“左右高度差不多”会被扣分。
6.3 单链表找环入口
资料里提到快慢指针:一个每次走一步,一个每次走两步,相遇后一个回到头,两个同速前进,再次相遇点就是环入口。笔试常只问“怎么判断有环”,能补出找入口的步骤会加分。注意快指针每次走两步,要判空,否则空指针解引用直接翻车。
7. 操作系统与网络常识:进程线程、死锁与 TCP/IP
7.1 进程与线程的四点差别
| 维度 | 进程 | 线程 |
|---|---|---|
| 调度单位 | 资源拥有单位 | 调度和分配的基本单位 |
| 并发性 | 进程间可并发 | 同进程内多线程也可并发 |
| 资源 | 独立地址空间 | 共享进程资源,独立栈 |
| 开销 | 创建撤销开销大 | 开销小 |
这四点要能按顺序说出来,笔试简答题基本按点给分。
7.2 死锁四个必要条件
互斥、请求保持、不可剥夺、环路等待。四个条件同时成立才可能死锁,破坏任意一个就能预防。资料里还提到处理策略:鸵鸟策略、预防、避免、检测与解除。答题时先写四个条件,再写策略,层次清楚。
7.3 TCP 与 UDP、ARP 与 IP 编码
TCP 提供可靠传输、流量控制、全双工,包头大;UDP 不保证可靠,包头小、开销小。ARP 负责 IP 地址到物理地址的转换。IP 地址分网络号和主机号,要靠子网掩码按位与才能区分。这几条属于网络常识,背准关键词即可,别展开成教材。
8. 易错点清单:五类最容易翻车的地方
易漏点一:static 修饰局部变量和全局变量混为一谈。为什么漏:都叫“静态”,容易只记一个“静态存储”。怎么补:按“改变生命周期”还是“改变作用域”分两栏默写,各配一个代码例子。
易漏点二:sizeof 数组在函数参数里退化。为什么漏:函数外测得对,函数内想当然。怎么补:凡是形参写成type arr[],立刻在草稿上标“指针”,sizeof 结果按指针算。
易漏点三:字符串常量被修改。为什么漏:char *s = "abc"; s[0] = 'B';编译能过,运行才崩。怎么补:看到字符串字面量赋给普通 char*,先标“只读”,要改就用字符数组。
易漏点四:malloc 后不检查、不释放、长度不加一。为什么漏:倒序字符串题里malloc(len)少分配\0空间。怎么补:申请长度统一写len + 1,使用后 free,指针置 NULL。
易漏点五:宏参数带副作用。为什么漏:SQUARE(a++)展开后自增两次。怎么补:宏参数一律加括号,带副作用的自增不要传进宏,能写函数就写函数。
9. 复习清单:按这张表查漏补缺
复习到后期,不建议再从头刷题,而是按模块做“能否复述”的自检。下面这张表可以直接当检查单用:
| 模块 | 自检问题 | 达标标准 |
|---|---|---|
| 关键字 | static 三种用法分别改变什么 | 能各举一例并说清生命周期/作用域 |
| 内存 | 栈、堆、静态区各放什么 | 能画出四区图并标出变量归属 |
| 指针 | &a+1与a+1差多少 | 能口算输出题并解释指针类型 |
| 函数 | extern 写错名字何时报错 | 能区分编译期与链接期 |
| 预处理 | 宏与 inline 的差别 | 能说出副作用和类型检查 |
| 数据结构 | 两栈实现队列的平摊复杂度 | 能写出入队出队步骤 |
| 操作系统 | 死锁四个必要条件 | 能按顺序默写并说破坏方法 |
| 网络 | TCP/UDP 各自特点 | 能各写三条对比 |
具体操作上,我自己的习惯是:每复习完一个模块,合上资料,用白纸把该模块的“概念—陷阱—一道输出题”写出来,写不出来的地方就是漏洞。尤其是指针和内存那两章,光看会了不算,必须手推一遍地址偏移。还有一个小技巧:把资料里所有带printf的输出题单独抄成一页,遮住答案限时做,错题旁边标上错因分类(生命周期、指针类型、宏展开、越界),一周后只重做错因相同的那几道。
从那以后我每次复习 C 语言笔试,都强制先过一遍这张模块表,再动笔做题,避免在会的题上反复花时间、在漏的点上一直绕。希望这份梳理能帮到你,需要完整题目和代码细节的,可以按文末方式获取这份《C语言笔试大全》慢慢对照。
本文还有配套的精品资源,点击获取