简介:这份详解版C语言期末题库面向高校理工科学生与备考C语言期末考试的读者,聚焦选择题、填空题、判断题、读程序写结果和压轴编码等常见题型,帮助在有限时间内梳理函数、标识符、三种控制结构、数据类型、运算符、条件表达式及标准库函数等核心考点。全套资料为1个PDF文件,压缩包约1.56MB,轻量便携,适合打印或在手机、平板上随时翻阅。已有428人学习下载,说明其在期末冲刺场景中具有一定参考价值。题库共305道题且逐题附有详细解析,不仅给出答案,还解释考点与易错原因,如main函数执行起点、标识符命名规则、求余运算和强制类型转换等,便于读者由题带点建立知识框架。读程序写结果与编码大题可训练程序阅读和上机实现能力,适合用于集中刷题、错题复盘和考前查漏补缺。
1. 从 305 道题看 C 语言期末考:先立一张能查漏的复习地图
C 语言期末备考最容易走偏的一步,是把题库当成“刷完就完”的练习册。这份 305 题的详解题库,题型分布本身就藏着一张地图:选择题 50 道压语法细节,填空题 50 道考术语精确度,判断题 100 道专攻“看起来对”的表述,读程序写结果若干道考执行顺序,最后几道压轴编码考综合运用。换句话说,选择填空决定会不会,判断题决定稳不稳,读程序与编码决定拿不拿高分。整张地图可以拆成五块:语法地基、数组与字符串、函数与存储类别、结构体与指针、易错点复盘。适合正在系统学 C、准备期末,或者想用一份详解题库快速定位薄弱点的人。
2. 语法地基:标识符、数据类型与运算符表达式的题眼
2.1 标识符与关键字:五道题其实在考同一条规则
题库选择题前 10 道里有 5 道在考标识符,题干换来换去,答案都落在同一条规则上:标识符只能由字母、数字、下划线三类字符组成,且第一个字符只能是字母或下划线;不能与关键字重名;大小写敏感。把这三点背成顺口溜没用,得会拿它逐字符扫一遍选项。
| 选项 | 判断 | 理由 |
|---|---|---|
a2_i | 合法 | 字母开头,字母数字下划线混合 |
-a1 | 非法 | 减号不属于三类字符 |
a[i] | 非法 | 方括号不属于三类字符 |
int | 非法 | 关键字 |
int_i | 合法 | 只是包含 int,不等于 int |
3ax | 非法 | 数字开头 |
_3d | 合法 | 下划线开头 |
case/union/while | 非法 | 分别是多分支、共用体、循环关键字 |
I\am/start$it/My->book | 非法 | 反斜杠、美元符、箭头都不在允许字符里 |
提示:判断“包含关键字”和“等于关键字”是两回事,
int_i是合法标识符,这是选择题里最常见的偷换。
大小写这一条也常考。a在 ASCII 表里是 97,A是 65,所以Main、MAIN都不是main。判断题里“C 语言不区分大小写”直接判错,解析里给的理由通常就是这两个码值。复习时不用背整张 ASCII 表,记住大小写相差 32 就够推大多数题。
2.2 数据类型只有三类,别把逻辑型当第四类
选择题问“简单数据类型包括”,四个选项里都塞了“逻辑型”,正确答案是不含逻辑型的那一项:整型、实型、字符型。C 语言在期末考范围内没有专门的布尔类型,关系表达式和逻辑表达式的值用 0 或 1 表示,0 为假,非 0 为真。
| 类别 | 关键字 | 说明 |
|---|---|---|
| 整型 | int、short、long | 存整数,%只能对它用 |
| 实型 | float、double | 存小数,float单精度,double双精度 |
| 字符型 | char | 存单个字符,本质是 ASCII 码值 |
这条结论直接决定了两道判断题的答案:while(!E)里的!E等价于E==0,while(E)里的E等价于E!=0。原因不玄学,就是“非 0 为真、0 为假”这一条在逻辑取反里的展开。字符型参与算术运算时取的是 ASCII 码值,'A'+32用%d输出是 97,用%c输出是 a,这类题记住大小写差 32 就能现场推。
2.3 运算符与表达式:整除、取余和三目
取余%只能用于整数。5%2得 1,5%5得 0,2%5得 2——小数对大数取余,商 0 余本身。很多人凭“余数一定比除数小”的印象选错,实际上2%5的结果就是 2,因为 2 除以 5 商 0,余下 2。
除法/的结果由操作数类型决定:两个整数相除做整除,11/3得 3;只要有一边是浮点,就做浮点除法,11.0/3得 3.666668。三目运算符? :的判断顺序是:先算?前的条件,为真取?后的值,为假取:后的值。int a=3,b=4; a<b?a:b得 3。
优先级与结合性也是填空题的常客。“如果一个运算对象两侧的运算符优先级相同,按自左向右顺序执行”考的就是结合性这个术语。优先级决定谁先算,结合性决定同级谁先算,C 语言多数运算符是自左向右,赋值和条件是少数从右往左的。
#include <stdio.h> int main(void) { int a = 5, b = 2; printf("%d %d %d\n", a % b, 2 % 5, a / b); /* 1 2 2,整除不四舍五入 */ printf("%d\n", 3 < 4 ? 3 : 4); /* 条件为真,取 3 */ printf("%d\n", (a > b) + (b > a)); /* 关系表达式结果只有 0/1 */ return 0; }a % b得 1,2 % 5得 2,a / b得 2 而不是 2.5;最后一行两个关系表达式相加得 1,用来说明关系表达式的值只有 0 和 1 两种,不会出现别的数。
2.4 类型转换:括号套在哪里,结果差一个量级
强制类型转换的形式是(类型名)(表达式),类型名和表达式各自带括号最稳妥。题库里四道题给出了四种结果,把它们并排放在一起看,规律就出来了:
| 表达式 | 结果 | 原因 |
|---|---|---|
11/3 | 3 | 两个整数,整除 |
11.0/3 | 3.666668 | 有浮点,整型提升为浮点 |
(float)11/3 | 3.666668 | 先把 11 转成浮点再除 |
(int)(11.0/3+0.5) | 4 | 先算 4.166,再截断小数 |
最容易翻车的是a=2时b=(float)(1/a):括号先算1/2,两个整数整除得 0,再转float得 0.000000。把括号挪成(float)1/a,先转 1.0 再除,才是 0.5。同一道题四个选项,考的不是“会不会转”,而是“括号管住了谁”。
还有n=(int)f%3、f=13.8这种:先算(int)f得 13,再13%3得 1。判断顺序靠两点——强制转换是单目运算符,优先级高;%两侧必须都是整数。
#include <stdio.h> int main(void) { int a = 2; float f = 13.8f; int n; printf("%.6f\n", (float)(1 / a)); /* 先整除得 0,再转浮点:0.000000 */ printf("%.6f\n", (float)1 / a); /* 先转 1.0,再除:0.500000 */ n = (int)f % 3; /* 13 % 3 = 1 */ printf("%d\n", n); return 0; }强制转换只影响当次运算,不改变原变量的类型和值。判断题“强制类型转换不会改变原变量的原有数据类型”判对,原因就在这里:转换产生的是一个临时值,变量本身没被改写。
3. 数组与字符串:下标、长度与输入函数的边界
3.1 数组定义的三条硬规矩
数组题的错误选项高度重复,翻来覆去就三条规矩:[]里必须是常量表达式,不能是变量;定义用方括号,不能用圆括号;引用下标从 0 开始,int a[10]的合法下标是 0 到 9。
题库里出现过的错误写法可以归成一张对照表:
| 写法 | 判断 | 原因 |
|---|---|---|
char a(10); | 错 | 定义数组不能用圆括号 |
int a[]; | 错 | 没给长度,编译器无法分配 |
int k=5,a[k]; | 错 | 方括号里是变量不是常量 |
int a[5]=(0,1,2,3,4); | 错 | 初始化用花括号不用圆括号 |
int a[3]={0,1,2}; | 对 | 标准初始化 |
#define N 10+int a[N]; | 对 | 宏展开后是常量 |
#include <stdio.h> #define N 10 int main(void) { int a[N]; /* 宏在预处理阶段替换成 10,合法 */ int b[5] = {0, 1, 2}; /* 只初始化前三个,其余自动补 0 */ printf("%d %d\n", a[0], b[4]); /* b[4] 是 0,不是随机值 */ return 0; }宏替换发生在预处理阶段,所以N到编译时已经是常量;用变量n写int a[n]属于变长数组,期末题库通常按错误处理。初始化列表没写满时,剩余元素自动补 0,这一点在读程序题里经常决定输出结果。
3.2 二维数组:第一维可省,第二维必写
int a[][3] = {{1,2,3},{4,5},{6,7}};的第一维由初始化列表里花括号的个数决定,是 3;第二维固定 3 列。合法定义只有一种省略方式:第一维不写,第二维必须写。
| 定义 | 判断 | 原因 |
|---|---|---|
int a[][3] | 合法 | 第一维可省,编译器按初始化推算 |
int a[2][] | 非法 | 第二维不能省 |
int a[][] | 非法 | 两维都省,形状无法确定 |
int a[2,3] | 非法 | 二维要用两个方括号 |
int a[3][4] | 合法 | 标准写法 |
下标同样从 0 开始,int a[3][4]的最后一个元素是a[2][3],写a[2][4]就越界,写a[1,3]是语法错。二维数组的题眼往往不在定义,而在“第几行第几列”和“下标几”之间的换算,做这类题先在草稿纸上画出 0 到 N-1 的编号,比心算稳。
3.3 字符数组与字符串:'\0'决定长度题
字符串以'\0'结束,这是所有字符串题的总开关。掌握这一个字符,长度类的题基本不会错:
#include <stdio.h> #include <string.h> int main(void) { char x[] = "hello"; /* 6 个字节,末尾自动补 '\0' */ char y[5] = {'h','e','l','l','o'}; /* 5 个字节,没有 '\0' */ char s[] = "student"; /* 8 个字节 */ printf("%zu %zu\n", sizeof(x), sizeof(y)); /* 6 5 */ printf("%zu\n", strlen(x)); /* 5,不数 '\0' */ printf("%zu\n", strlen(s)); /* 7 */ return 0; }sizeof算的是整个数组占的字节数,含'\0';strlen数到'\0'就停,不把它算进去。用初始化列表逐字符赋值时不自动补'\0',所以y的长度是 5,把它当字符串打印会越界读,这也是判断题“字符数组赋值后会自带\0”只对双引号写法成立的原因。
二维字符数组考的是定位:char x[][10]={"Linux","windows","Mac"};里x[1][2]是第二行第三个字符。x[1]是"windows",下标 0 是 w,1 是 i,2 是 n,答案就是 n。做这类题先定行再定列,别一上来就数。
3.4 输入输出函数各管一段
字符串输入函数的区别几乎是判断题的固定考点,用一张表理清分工:
| 函数 | 用途 | 关键点 |
|---|---|---|
scanf("%s", a) | 读字符串 | 遇空格或回车停止,数组名即首地址,不加& |
gets(a) | 读一行 | 可以带空格,一次只能一个数组 |
fgets(s, n, stdin) | 读一行 | 指定最大长度,会保留换行符 |
fgetc() | 读一个字符 | 字符级输入 |
fputc() | 写一个字符 | 字符级输出 |
fwrite() | 二进制写 | 面向数据块,不是字符串专用 |
scanf的格式符是%s时,后面跟数组名就够,因为数组名代表首元素地址;如果变量是int型,才需要写&。这条区别在填空题里反复出现:“scanf 函数中的格式控制后面应该是变量地址,而不是变量”,说的正是普通变量要加取地址符。
4. 函数、存储类别、结构体与指针:三类名字别搞混
4.1 函数返回值与参数传递
函数返回值的类型由定义时指定的类型决定,不由return后面表达式的类型决定。写int maxNumber(int a, int b),返回值就必须按int处理,return一个浮点数会被隐式截断。判断题和选择题都爱在这里做文章:把“返回值类型由 return 表达式决定”作为一个看似合理的错误选项。
参数传递是单向的,只能从实参传到形参,函数内部改形参不影响外面的实参。实参和形参可以同名,因为各自是局部定义,作用域不重叠。调用无参函数时也可以没有返回值,所以“函数调用后必须带回返回值”这句判错。
函数声明和函数定义的第一行基本一样,区别只在声明末尾多一个分号。函数调用语句由“一个函数调用”加“一个分号”组成,这个填空考的是语句的构成,不是函数的构成。按使用角度分,函数分库函数和自定义函数;按形式分,分无参函数和有参函数,两组分类不要交叉着答。
4.2 四种存储类别:默认是 auto
存储类别是填空题的高频区,四个关键字各有分工:
| 关键字 | 名称 | 特点 |
|---|---|---|
auto | 自动变量 | 函数内局部变量默认类别,可省略 |
static | 静态变量 | 生命周期贯穿整个程序,值保留 |
extern | 外部变量 | 声明变量在别的文件里定义 |
register | 寄存器变量 | 建议放入寄存器,取地址受限 |
未指定存储类别的变量,隐含类别是auto。写int number;时,编译器默认在int前面补上auto,这条结论在填空题里几乎原样出现。static的关键在“静态存储区”和“值不随函数退出而消失”,考题常拿它和局部变量比生命周期,而不是比作用域。
4.3 结构体、共用体、枚举与 typedef
结构体题的核心只有一个:分清类型名、变量名和 typedef 新名。看下面这段声明:
struct student { int num; char name[20]; float score; } stu, *p; typedef struct stu { int a; float b; } stutype;struct student是用户定义的结构体类型名,stu是结构体变量名,p是指向该类型的指针。student单独拿出来只是类型名的一部分,不能用来访问成员,所以student.num非法。成员访问只有三种正确写法:stu.num、p->num、(*p).num。
typedef的作用是给已有类型起一个新名字,stutype是新类型名,不是变量名。看到typedef就先圈出最后那个标识符,再判断它是类型还是变量,能避开大部分结构体选择题的坑。定义结构体用struct,共用体用union,枚举用enum,这三个关键字在填空题里成组出现。
结构体成员可以是指向自身类型的指针,成员名也可以和变量名相同,只是不建议这么写。指针指向结构体成员时,p = &data.a是标准写法:data.a是整型变量,前面加取地址符才得到地址。
4.4 指针:定义、解引用与动态内存
指针的定义由三部分组成:类型名、指针运算符*、指针变量名。int *p;读作“p 是指向 int 型数据的指针变量”。数组名代表首元素地址,所以int *p = a;等价于p = &a[0];,而int *p = &a;是错的——&a是数组的地址,类型对不上。
#include <stdio.h> #include <stdlib.h> struct student { int age; int num; }; int main(void) { struct student stu1 = {18, 1001}; struct student *p = &stu1; int arr[5] = {1, 2, 3, 4, 5}; int *q = arr; /* 等价于 q = &arr[0] */ int *m = (int *)malloc(5 * sizeof(int)); /* 动态申请 5 个 int */ if (m == NULL) { return 1; /* 申请失败直接退出,别继续用 */ } printf("%d %d\n", p->age, (*p).age); /* 两种访问成员写法等价 */ printf("%d %d\n", *(q + 2), q[2]); /* 都是 3 */ free(m); /* 用完释放,避免内存泄漏 */ return 0; }*(q + 2)和q[2]等价,都是元素;q + 2单独写只是地址,不是元素值。指针指向函数时,int (*p)()里的 p 指向函数入口地址,函数返回值类型要和 p 的基类型一致。动态内存四个函数——malloc、calloc、free、realloc——在填空题里成组考,malloc申请不初始化,calloc申请并清零,free释放,realloc调整大小。
注意:
malloc之后必须判空,free之后不要再解引用。读程序题里这两步经常被省略,输出看起来正常,实际是未定义行为。
5. 易漏点复盘:五类反复翻车的题目
5.1 易漏点一:把执行入口和编译起点混为一谈
易漏点:认为“C 程序从第一条语句开始执行”,或者“编译从 main 函数第一行开始”。判断题里这两句都判错,但很多人凭语感判对。
为什么漏:编译和执行是两个阶段,复习时只记了“从 main 开始”,没把“编译先处理预处理命令”这句补上。#include、#define这些以#开头的行在编译之前就被处理掉了,所以编译的起点是源文件第一行。
怎么补:背三句话——编译从源文件第一行开始;执行从main函数开始;main写在第几段都行,不要求放最前面。再补一条:死循环能编译通过,只是运行停不下来,所以“程序有死循环时上机编译不能通过”判错。
5.2 易漏点二:'\0'的长度账算错
易漏点:把strlen的结果当成数组长度,或者把char a[]="student"的长度记成 7。
为什么漏:“字符个数”“数组长度”“strlen 返回值”三个量在脑子里是一个数,做题时随手填。实际是三笔账:sizeof算字节含'\0',strlen数到'\0'停不含它,逐字符初始化列表不自动补'\0'。
怎么补:每道长度题先在草稿上写三个格子,分别填sizeof、strlen、初始化字符数,再对选项。遇到char y[5]={'h','e','l','l','o'}这种,先标“无\0”,再判断它能不能当字符串用。
5.3 易漏点三:强制类型转换的括号管住了谁
易漏点:(float)(1/a)和(float)1/a看成一回事,n=(int)f%3不确定先算哪一步。
为什么漏:只记住“要加括号”,没记住“括号改变计算顺序”。强制转换是单目运算符,优先级高,但它只管紧跟着的那个表达式;括号套得越靠外,转换发生得越晚。
怎么补:做题时先圈括号,再按“括号内先算、单目其次、乘除加减最后”的顺序标序号。遇到(类型名)后面直接跟变量、再跟运算符的写法,先判断转换发生在运算符之前还是之后。把(float)(1/a)与(float)1/a两个结果写在一起对照,比背定义管用。
5.4 易漏点四:数组下标和长度混着用
易漏点:int a[10]认为a[10]是最后一个元素;二维写a[2,3]当合法;a[3.5]以为会自动取整。
为什么漏:日常说“第 10 个”和下标 10 是两回事,做题时顺着语感走。二维数组的逗号写法在数学里常见,在 C 里是错的;下标必须是整型,浮点直接判错而不是取整。
怎么补:引用前先算上界,长度 N 的数组下标范围是 0 到 N-1,写在题号旁边。二维数组只认两个方括号,看到逗号直接排除。读程序题里出现a[a[i]]这种嵌套,先算内层再算外层,一步一步替换,不要跳步。
5.5 易漏点五:结构体的三个名字分不清
易漏点:拿struct student里的student当变量名去访问成员;把typedef最后那个stutype当成变量名。
为什么漏:中文习惯把struct后面那串都叫“结构体名”,教材讲得笼统,考试偏偏拿它做选项。typedef的语法又把新名字放在最后,视觉上很像变量。
怎么补:每道结构体题先画三行——struct后面是类型名,花括号后面是变量名,typedef最后是新类型名。访问成员只用变量名或指针,类型名永远不参与成员访问。判断题里“stutype 是用户定义的结构体变量名”这类表述,按这三行一对就能判错。
6. 收官复习清单:按题型过一遍,按知识点再拉一遍
复习到后期,最怕的是“题都见过,但说不清哪个模块弱”。用一张表把题库的题号区间和自检动作对上,比从头再刷一遍省时间:
| 模块 | 对应题号区间 | 自检动作 |
|---|---|---|
| 语法地基 | 选择 1-19、填空 1-12 | 默写标识符三条规则、三类数据类型、四个类型转换结果 |
| 数组与字符串 | 选择 20-32、填空 37-39 | 手算sizeof、strlen、初始化列表三个长度 |
| 函数与存储 | 选择 33-37、填空 13-20、40-43 | 写出返回值类型由谁决定、四种存储类别各自特点 |
| 结构体与指针 | 选择 38-50、填空 21-22、44-49 | 画三个名字、写三种成员访问、指针定义三部分 |
| 判断题 | 100 道 | 每道不只判对错,写一句理由 |
三轮的节奏可以这样排:第一轮按题型顺着做,找手感;第二轮按上表横向拉,同一知识点跨题型对照,比如把选择里的类型转换题和填空里的转换形式题放一起看;第三轮只做错题,每道错题后面写一句错因,比如“括号位置”“'\0'算进长度”“类型名当变量名”,考前只翻这些句子。
读程序写结果题有固定动作:草稿纸上画一张变量表,每执行一行更新一次值;遇到++、--标清前置后置;循环题把i的变化序列写出来,别在脑子里跑。压轴编码题先搭函数框架再填循环,数组题先定长度,指针题先定指向,写完拿 0、1、N-1 三个边界值走一遍。
这份 305 题的详解题库把选择、填空、判断、读程序、编码按题型分好,解析写在每题下面,正好适合第二轮横向拉通时当对照底稿;如果你手上缺一份能按题型定位薄弱点的题库,可以把它收进复习资料里,配合上面的清单用。
我以前复习时吃过一次亏:判断和选择刷得很顺,读程序题却总在'\0'和数组下标上丢分,后来才明白是长度账没单独算过。从那以后我每次碰数组和字符串题,都强制先在草稿上写三行——数组长度、strlen结果、有没有'\0',再动笔选答案。希望帮到你。
本文还有配套的精品资源,点击获取