
简介面向准备C语言程序设计考试与PTA理论测评的初学者及任课教师这份文档围绕理论考场景整理了判断题、选择题及对应答案可用于考前速查、课堂练习与错题复盘帮助读者快速定位基础语法中的易混点。压缩包内共1个docx文件约345KB内容以试题、答案和知识点详解为主结构轻便适合在电脑上查阅或打印使用。文档覆盖编译预处理命令、main函数与程序执行起点、变量存储、运算符与表达式合法性、scanf和printf格式控制、条件判断、for循环及复合语句等核心考点并对整数除法、赋值与比较混淆、浮点输入输出精度等常见失分点作出解释。目前已有1897人学习下载可作为C语言理论复习阶段的自测清单和查漏补缺材料。1. 知识地图PTA理论考到底在考哪几类题PTA理论考看起来是判断题和选择题的堆叠实际上它只考三件事语法是否合法、执行结果是什么、概念表述对不对。把题目按这三类拆开你会发现大部分失分不是“没背过”而是“没模拟执行”。这份笔记把C语言理论考拆成五块地图语法骨架、表达式与类型、输入输出、流程控制、易错点清单。每一块都对应一类判断题的判定依据复习时先定位题目属于哪块再套判定规则比从头翻书快得多。适合已经写过一点C代码、但一到理论题就凭感觉选的人。2. 语法骨架预处理、main 与语句边界2.1 预处理命令与程序入口#include stdio.h是编译预处理命令作用是让编译器在编译前把标准输入输出头文件的内容包含进来之后才能用printf、scanf这些库函数。理论考常拿它做判断题考点不是“它能不能用”而是“它是不是C语句”。预处理命令独占一行不以分号结尾如果在行末加了分号这个分号会留在源文件里变成多余记号属于自己给自己挖坑。#include stdio.h /* 预处理命令不以分号结尾 */ int main(void) { printf(hello\n); return 0; }上面这段代码里#include负责引入头文件main是程序入口return 0表示正常结束。理论考还有一道高频判断题任何一个程序都必须有而且只能有一个main()函数。在教材语境下按“有且只有一个”记但要注意程序执行不是从源文件第一条语句开始而是从main函数的第一条可执行语句开始。注释、空行、预处理命令都不算执行起点。另一道常错题是“C语言程序是从源文件的第一条语句开始执行的”判断依据就是入口在main不在文件开头。还有一类题会问“以下程序段是否符合C语言语法”比如先写k 1;再写int k;。这类题的判定规则只有一条变量必须先声明后使用。声明之前的赋值语句找不到k这个标识符语法上直接不成立。C99之后允许变量在块内混排但理论考按教材的“先声明后使用”走不要拿新标准去抬杠。2.2 变量声明顺序与存储单元变量定义之后它在内存中占一个存储单元这个说法在理论考里通常判对。真正容易错的是声明顺序和初始化。看下面两段int main(void) { int k; /* 先声明 */ k 1; /* 再使用合法 */ return 0; }k 1; /* 错误k 还没有声明 */ int k;第二段就是典型的“使用在声明之前”。理论题不会直接告诉你“这题考声明顺序”它会把两行代码摆出来让你判断语法。复习时养成一个动作看到变量名先往上找它的声明找不到这题就结束。还有一种题会问“变量代表内存中的一个存储单元”对不对。这句话本身是对的但要注意区分“变量名”和“存储单元”变量名是标识符存储单元是内存位置定义变量时编译器给这个标识符分配存储单元。判断题里如果表述成“变量代表内存中的一个存储单元”按教材结论走。2.3 语句、复合语句与空语句C语言里大部分语句以分号结束但有三类要单独记预处理命令不以分号结尾复合语句{}在语法上被视为一条语句右大括号后面不加分号空语句就是一个单独的分号什么也不做。int i, sum 0; for (i 1; i 10; i) { sum sum i; /* 复合语句内的多条语句 */ }这段里{}把循环体包成一条复合语句语法上算一条语句。如果循环体只有一条语句大括号可以省如果有多条必须用{}包起来否则只有第一条属于循环体。理论考常拿“循环体包括一个以上语句时必须用大括号组成复合语句”做判断这个表述按教材判对。空语句的写法是单独一个分号for (i 0; i 10; i) ; /* 空语句循环体什么也不做 */空语句在语法上是合法的它不执行任何操作。判断题里说“仅由一个分号构成的语句称为空语句它什么也不做”按教材判对。但实际写代码时空语句后面如果多打一个分号很容易把原本的循环体变成空循环这是另一类坑后面易错点章节会展开。3. 表达式与输入输出类型、优先级和格式符3.1 运算符分类与优先级单目运算符只需要一个操作数比如、--、!、负号-、sizeof。理论考有一道判断题“在C语言中单目运算符需要2个操作数。”判定依据很直接单目就是一眼一操作数这句话是错的。容易混的是和-作为二元运算符时需要两个操作数作为一元负号时只需要一个。赋值运算符的优先级低于算术运算符所以n 10 2;先算加法再把12赋给n。有一道题故意假设“赋值运算符的优先级比算术运算符高”问执行后n的值是不是10。按这个假设表达式会变成(n 10) 2n被赋成10整个表达式的值是12。所以“n的值为10”在这个假设下成立。这类题考的是“优先级改变结合方式”不是考真实优先级读题时先把假设圈出来。还有两道经典语法判断题5(fahr-32)/9和x y 22。前者在5和括号之间缺少运算符C语言不会自动补乘号语法不成立后者赋值号左边是x y不是左值也不能成立。判定规则一句话乘号不能省赋值左边必须是可修改的左值。3.2 整数除法与取模禁区整数除法会舍去小数部分。若n是int且|n| 11/n的结果恒为0。因为1除以任何绝对值大于1的整数商的整数部分都是0。判断题里这个表述按教材判对。取模运算符%只能用于整数。若x定义为doublex % 2不合法因为%的操作数必须是整数类型。这是理论考的高频语法题看到%先看两边是不是整数。int n 5; double x 4.0; printf(%d\n, 1 / n); /* 0整数除法 */ printf(%.2f\n, 1.0 / n); /* 0.20浮点除法 */ /* printf(%d\n, x % 2); 错误% 不能用于 double */这段代码里1 / n是整数除法结果是01.0 / n因为有一个操作数是浮点数按浮点除法算结果是0.20。理论题经常把1.0/i和1/i摆在一起让你选区别就在分子是不是浮点常量。3.3 赋值与比较if(x20) 的坑if (x 20)里用的是赋值号不是比较号。执行时先把20赋给x整个表达式的值也是20非零为真所以条件恒成立x的值也被改成了20。判断题问“执行后x的值不变”判定依据就是赋值号会改x这句话是错的。int x 0, y 0; if (x 20) { /* 赋值x 变成 20条件为真 */ y 1; } else { y 0; } printf(x%d y%d\n, x, y); /* x20 y1 */对比if (x 20)才是比较不会改x。理论考里还有fahr与fahr 1的对比fahr会改变fahr的值fahr 1只计算表达式的值不改变fahr。判断题说“两者等价”按“是否改变变量”判错。3.4 scanf/printf 格式匹配scanf和printf的格式符必须和变量类型对齐。double输入用%lf输出用%f或%lf都可以int用%dfloat输入用%f。理论考常拿“double x; scanf(%d, x);”和“double x; scanf(%f, x);”做判断题两者都是类型不匹配输入结果不可靠。double x; scanf(%lf, x); /* 正确double 输入用 %lf */ printf(%.2f\n, x); /* 输出保留两位小数 */如果格式串里带字面字符比如scanf(x%lf, x);输入时必须原样写x30只输入30匹配不上。还有一类题把多个变量放在一条scanf里格式串顺序和变量类型错位比如int money, year; double rate; scanf(%d %lf %d, money, year, rate);第二个%lf对应的是int的year第三个%d对应的是double的rate类型全错输出自然不对。判定方法把格式符和变量声明逐个对齐错一个就整条不可靠。3.5 字符常量与 ASCII 码字符常量用一对单引号括单个字符A、z、0、$都是合法的字符常量。数字字符1的值是ASCII码49不是数字1这是判断题的经典陷阱。字符ASCII值说明048数字字符起点957数字字符终点A65大写字母起点Z90大写字母终点a97小写字母起点z122小写字母终点从表里能读出几条判定规则0到9按升序连续排列A到Z按升序连续排列a到z也按升序连续排列但Z到a之间隔了6个符号所以“大小写字母整体按升序连续排列”是错的。M的ASCII是77m是109大写比小写小32判断题说“M比m大”判错。a是97Z是90差7不是1判断题说“a是Z加1”也判错。D - A等于3d - a也等于3所以这个等式成立。3.6 getchar/putchar 与字符输入输出putchar(S)在屏幕上输出的是S不带引号。判断题如果说输出结果是S带引号判错。getchar()一次读一个字符输入三个字符时它只读第一个。char ch; ch getchar(); /* 读入一个字符 */ putchar(ch); /* 输出该字符不加引号 */如果是scanf(%c%c%c, ch1, ch2, ch3); printf(%c, ch2);输入三个字符后输出的是第二个字符。理论考常把“输入三个字符输出第一个”和“输出第二个”混在一起判定时数一下%c的位置就行。4. 流程控制分支、循环与 switch 的执行轨迹4.1 if-else 配对与测试用例设计else的配对规则是与之前最近的、且不带else的if配对。缩进不决定配对编译器不看缩进。看下面这段if (a b) if (b 0) c 0; else c; /* 这个 else 与内层 if 配对不是外层 */这段里else跟的是内层if (b 0)不是外层if (a b)。理论考选择题经常把缩进排成外层配对的样子判定时从else往上找最近的未配对if。测试用例设计也是常考点。省略else的if语句只有两个分支条件成立和不成立。所以if (grade 60) { printf(Fail\n); }至少需要2组测试用例小于60一组大于等于60一组。判断题说“至少需要3组小于、大于、等于60”是错的因为大于和等于走同一条路径等价类只有两个。嵌套if的三个分支需要3组大于、小于、等于。else-if如果条件是x 0、x 15、else有效区间是小于0、0到15、大于15三组就够不需要五组。字符分类题if字母、else if数字、else其他按等价类分是三组字母、数字、其他不是按52个字母加10个数字逐个算。计算器题有、-、*、/且除法里再分除数非0和除数为0再加上“其他运算符”一共6组。这类题不要数字符个数要数“互斥分支”。4.2 for 循环三表达式的执行次数for (表达式1; 表达式2; 表达式3)的执行顺序是表达式1只执行一次然后判断表达式2为真执行循环体为假结束循环每次循环体执行完后执行表达式3。判断题说“表达式1只执行一次”判对“表达式2为假则结束循环”也判对。int fahr; double celsius; for (fahr 91; fahr 100; fahr) celsius 5.0 * (fahr - 32) / 9.0; printf(%4d%6.1f\n, fahr, celsius);这段里fahr从91到100循环体执行10次循环结束后fahr再加一次变成101所以printf输出的是101和最后一次算出的celsius。理论题会拆成四问语句①执行几次、语句②执行几次、循环体执行几次、结束时fahr是多少。数的时候把“循环体次数”和“表达式3次数”分开循环体10次表达式3也执行10次但fahr最终值是101。4.3 switch 语句穿透、break 与 defaultswitch的执行流程是先求表达式的值和各个case后的常量表达式比较相等就从那个case开始往下执行遇到break跳出都不相等就看有没有default有就从default开始执行没有就什么都不做。几个高频判定点case后面必须是常量表达式不能写case choice 1:因为choice 1是关系表达式不是常量同一个switch里case的值不能重复default不是必须的break也不是必须的没有break就会顺序往下穿透。switch (choice) { case 1: price 3.0; break; case 2: price 2.5; break; case 3: price 4.0; break; case 4: price 3.5; break; default: price 0.0; break; }这段里每个case都有break执行完对应分支就跳出。如果把default挪到最前面只要每个分支都有break整体逻辑不变但如果去掉defaultchoice不等于1到4时price就不会被赋值和原来不等价。等价性判断题的判定方法先看有没有default再看每个分支有没有break最后看case的覆盖范围。嵌套switch里break只跳出当前层不跳出外层。理论题如果给两层switch要标清楚每个break属于哪一层。4.4 循环里的数值陷阱求和、求阶乘、累加分数是理论考里最容易“看着对、算出来错”的一类。先看求和int i, sum; for (i 1; i 10; i) { sum sum i; }这段代码里sum没有初始化它的初值是不确定的所以最后结果不一定是55。判断题说“sum的值是55”判定依据是“累加器有没有赋初值”没赋初值就不能给确定结果。求阶乘的常见错误是初值设成0int i; double product 1; /* 求积初值必须是 1 */ for (i 1; i 20; i) { product product * i; }如果写成product 0;第一次循环0 * 1还是0后面永远是0算不出阶乘。另一道题说“20的阶乘超过double能表示的最大值”这个表述不准确double的表示范围远大于20的阶乘真正的问题是有效数字有限结果会有精度损失。理论考如果问“能不能算”按“能算但精度有限”理解。分数累加要注意整数除法和截断int i; double sum 0; for (i 1; i 2; i) { sum sum 1.0 / i; /* 1.0 保证浮点除法 */ }如果写成1 / ii为2时1/2是0累加结果不对。如果sum定义成int即使写1.0 / i每次赋值给sum也会截断小数部分最终结果也不是1.5。理论题把sum的类型、分子是1还是1.0、循环步长三个点摆在一起判定时逐个检查。5. 易错点清单五类最容易翻车的判断题5.1 易漏点预处理命令后面加分号易漏点看到#include stdio.h;觉得“分号结束语句”很自然判断题直接判对。为什么漏平时写代码时#include单独一行分号加不加编译器可能只是警告考试时容易忽略“预处理命令不是C语句”这个前提。怎么补记一条硬规则预处理命令独占一行不以分号结尾。判断题里只要出现“预处理命令以分号结束”或“#include是语句”先标错。复习时把#include、#define单独列一组不和普通语句混在一起背。5.2 易漏点scanf 格式串和变量类型对不上易漏点只检查格式符写没写对不检查它对应的变量是什么类型。为什么漏%d、%f、%lf长得像读题时眼睛扫过去觉得“有格式符就行”忘了double输入必须用%lfint必须用%d。怎么补养成“先看变量声明再配格式符”的顺序。做题时在草稿上把变量类型和格式符连成线double对%lf输入、%f输出int对%dfloat对%fchar对%c。格式串里有x这类字面字符时再补一条“输入必须带前缀”。5.3 易漏点把赋值当比较易漏点if (x 20)扫一眼当成if (x 20)直接判“x不变”。为什么漏和视觉太像判断题又不会报错只能靠主动识别。怎么补看到if括号里出现单个先停一秒标记“这是赋值条件值等于赋进去的值变量会被改”。再检查有没有else分支因为赋值表达式恒为真时else永远走不到。把if (x 20)和if (x 20)各写一遍对比x输出手感就出来了。5.4 易漏点循环累加器初值易漏点只盯循环条件和循环体不看sum、product有没有初始化。为什么漏代码逻辑读起来通顺sum sum i看起来就是在求和容易默认sum是0。怎么补读循环题先找三个东西循环变量初值、累加/累乘变量初值、循环体里的类型转换。求和sum 0求积product 1计数器按题目要求。看到int sum;后面直接进循环先标“未初始化结果不确定”看到product 0做乘法先标“恒0”。5.5 易漏点switch 的 break 穿透和 case 常量易漏点只背“switch多分支选择”不模拟没有break时往下穿透的过程。为什么漏平时写代码习惯每个case都加break考试题故意去掉一个读题时脑补成“执行完就跳出”。怎么补做switch题先画执行路径从匹配的case开始逐行往下看遇到break才停没有break就把后面的语句段也算进去。再检查case后面是不是常量表达式case choice 1这种直接判不合法。default的位置不影响有break时的结果但去掉default后无匹配分支的行为会变等价性判断题要单独看这一点。6. 复习清单把错题变成知识地图的查漏补缺法复习到最后一轮不建议再从头翻教材而是拿一张表把错题归因。下面这张表可以照着填每道错题只填一行填完就知道自己缺的是哪块地图。错题类型判定依据常见错法补法语法合法性声明顺序、运算符、左值、%操作数凭感觉选“看起来对”把变量声明和表达式分开检查执行结果初值、整数除法、break穿透、循环次数不模拟执行直接猜草稿上逐行写变量值概念判断预处理、main、复合语句、ASCII 顺序把教材结论和实际实现混在一起按教材语境记结论不抬杠输入输出格式符与变量类型对齐、字面字符只看格式符不看变量先写类型再配格式符测试用例互斥分支数、等价类按字符个数数数“走不同路径的分支”填表的时候有一个小技巧每道错题只写“错因关键词”不抄整道题。比如“if(x20)赋值”“sum未初始化”“case后非常量”关键词写多了自然会归类。归完类你会发现真正反复错的就那么五六种把这几类各找五道同类题集中做一遍比刷一百道散题有用。我自己的血泪经验是第一遍做理论题时总想“这题我写过代码肯定对”结果栽在scanf(%d, x)配double和product 0求阶乘这种地方。后来每次做题前强制走一遍固定动作先找变量声明再找初值再模拟循环或分支走三步最后才看选项。这个习惯看起来慢实际上把“凭感觉”换成了“可复现的判定”正确率反而稳了。从那以后我每次复习理论考都先把错题按上面五类归因再回到对应章节补规则不再从头背书。希望帮到你。本文还有配套的精品资源点击获取