简介面向准备C语言程序设计考试的学生与自学者这份PTA理论考练习文档聚焦基础语法判断与选择题的自测场景帮助读者在考前系统核对概念、定位易错点。内容围绕编译预处理命令、main函数入口、语句结束符、变量与内存存储单元、单目运算符、表达式合法性、赋值运算符优先级、scanf与printf格式控制、for循环结构、复合语句与空语句等高频考点逐题给出答案并配合逐条知识点详解说明每个判断的依据而不只停留在对错。压缩包内共1个docx文件约345KB为可直接打开查阅的Word试题集按HW02、HW03等题组顺序编排题干与答案相邻排布便于打印或边看边记。目前已有1897人学习下载。读者可借此获得一份覆盖C语言基础语法易错细节的题库通过对照答案与讲解自查运算符优先级、输入输出格式匹配、循环累加等常见误解适合课堂练习与考前突击使用。1. 一份 PTA 理论考错题地图长什么样PTA 理论考这部分题和能敲进编译器跑一遍的编程题完全是两种不同的考查方式判断题和选择题给的是一段段代码片段答案对错全押在脑子里那套语法判定规则准不准上。把整份题库摊开重排会发现它反复考的其实只有五束东西词法与语句结构、表达式与运算符、输入输出与字符处理、流程控制以及散落在这四块边界上的易漏点。适合两类人正在准备 C 语言理论考的和代码写得动但一遇到「以下程序段是否符合语法」就凭感觉蒙的。理论考不考你能不能跑通考你能不能在没编译器的时候判断这段代码会不会炸。2. 词法与语句地基预处理、分号、声明顺序与单目运算符这一章对应题库里最前面那批题看着最基础但丢分往往也丢在这里。原因不复杂这些规则平时写代码时被 IDE 帮你兜住了自动补全、波浪线提示、编译报错全都在替你判断到了纸面判断题上判断的人换成你自己边界一旦记模糊就只能靠语感。语感在这类题上基本靠不住。2.1 编译预处理命令与 main 函数的两个硬约束#include stdio.h是一个编译预处理命令判定特征有三个以#开头、独占一行、在编译之前由预处理器处理。预处理的工作方式是把stdio.h的内容原地展开到当前文件里展开完才轮到编译器干活。所以它不产生运行期代码也不参与运算行尾按惯例不写分号——写了多半也不报错但规范化写法是不写。stdio.h这个头文件里放的是printf、scanf、getchar、putchar这些函数的声明。少了这一行编译器在遇到printf时会认为这个函数没有被声明过行为就不确定了。main 函数有两条硬约束题库反复在这上面出题。第一条一个可以独立运行的程序有且只有一个main()函数它是程序入口。第二条更爱考——C 程序不是从源文件的第一条语句开始执行的而是从main()函数体的第一条可执行语句开始。源文件开头那些#include、宏定义、函数声明、甚至写在main前面的其他函数定义都不属于「执行起点」。说法判定理由程序从源文件第一行开始执行错起点是 main 函数体第一条可执行语句程序从 main 的第一条可执行语句开始执行对注释、空行、声明不算可执行语句一个程序可以有多个 main错独立程序有且只有一个入口一个程序可以没有 main错缺入口链接阶段就过不去提示判断题里出现「任何」「必须」「只能」这类绝对化措辞时先别急着判错回到规则本身核对一遍。C 语言的硬规则确实不少绝对化措辞只是提高警惕的信号不是判错依据。2.2 分号、复合语句与空语句C 语言里语句以分号结束这条规则本身没难度难度在分号的三种不同身份上。第一种是普通语句的结束符sum sum i;后面那个。第二种是复合语句的边界一对大括号{}把多条语句括起来语法上整体算作一条语句所以if、for、while后面的循环体或分支体如果是多条语句必须用大括号包起来包起来之后它对外就是「一条语句」不需要在右大括号后面再加分号。第三种最容易被忽略——单独一个分号构成空语句它什么也不做但它是合法的、完整的语句。空语句单独出现没危害混进控制结构里就是灾难。下面这段代码是理论考里最经典的陷阱之一int i, sum 0; /* 注意 for 后面这个分号它构成一条空语句成了整个循环的循环体 */ for (i 1; i 10; i); { sum sum i; /* 大括号块和 for 无关只执行一次此时 i 已经是 11 */ }这段代码跑完sum是 11不是 55。逻辑上拆开看for的循环体是那个空语句循环老老实实跑了 10 次每次什么也不干跑完之后i停在 11后面的大括号块是一条独立的复合语句顺序执行一次sum 0 11。要改成累加 1 到 10得把分号删掉让大括号块成为循环体。写循环时养成一个习惯for那一行结尾看到分号就停下来确认一下这是踩坑率最高的位置之一。复合语句还有一条判定要点因为它在语法上被视为一条语句所以它可以出现在任何「只允许一条语句」的位置。if (x 0) { a 1; b 2; }是合法的两个赋值被大括号打包成一条。反过来if (x 0) a 1; b 2;里只有a 1受条件控制b 2是无条件执行的这是另一类高频错题。2.3 变量必须先声明后使用k 1; int k;这样的程序段不符合 C 语言语法原因是变量k在被赋值时还没有声明。C 语言要求先声明后使用声明的作用是告诉编译器「这个名字存在类型是这个给它留一块空间」。声明之后这个变量就代表内存中的一个存储单元对变量的读写就是对这个存储单元的读写——这条判定在题库里以「C 程序中定义的变量代表内存中的一个存储单元」的形式出现是对的。正确顺序只有一种写法int k; /* 先声明告诉编译器 k 是 int 类型分配存储单元 */ k 1; /* 再使用把 1 写进 k 对应的存储单元 */ /* 或者声明的同时初始化效果一样 */ int m 1;参数说明int k;是定义性声明既声明名字又分配空间k 1;是赋值表达式语句要求k已经可见。如果在函数内部写k 1;而前面没有int k;编译器会报「未声明的标识符」这不是警告是错误。同一节里还有一条关于单目运算符的判定单目运算符只需要一个操作数。、--、!、-取负、sizeof都是单目、-、*、/、%在双目语境下需要两个操作数。判断这类题不用背表抓住「单目 一个操作数」这个定义就够了fahr只有一个操作数fahr是单目a b有两个操作数是双目。3. 表达式与输入输出优先级、整数除法、赋值陷阱与格式对齐这一章是整份题库里失分最集中的区域因为这里的规则彼此咬合优先级影响求值顺序类型影响运算结果格式串影响输入输出能不能对上。任何一环记错整道题的结论就反了。复习时不要孤立地背某一条要把它们串成一条链先看操作数类型再看运算符优先级最后看格式串匹配。3.1 运算符优先级与隐式乘法的缺失赋值运算符的优先级低于算术运算符这一点决定了n 10 2先算加法再赋值n得 12。题目里有一种反向考法假设赋值优先级比算术高问n是多少。按假设走一遍n 10先执行得到 10然后 2无处可去整段不成立。这类「假设规则反过来」的题考的是你能不能按给定前提推演而不是考真实规则读题时盯住「假设」两个字。比优先级更容易错的是运算符缺失。5(fahr-32)/9这种写法在 C 语言里不成立因为 C 没有隐式乘法数字和括号之间必须显式写出*。题库里这类题在标注上有出入复习时以规则为准任何两个操作数之间必须有运算符5 * (fahr - 32) / 9才合法。写温度转换这类公式时5.0 * (fahr - 32) / 9.0是标准写法用5.0和9.0而不是5和9是为了让整个表达式按浮点运算走。另外两条合法性判定经常一起出现。x y 22不合法因为赋值号左边必须是可修改的左值x y是一个表达式结果没有对应的存储单元不能作为赋值目标。x % 2在x声明为double时不合法因为取模运算符%只接受整型操作数。表达式前提是否合法原因5 * (fahr - 32) / 9int fahr;合法运算符完整整型运算5(fahr - 32) / 9int fahr;不合法缺乘号C 无隐式乘法x y 22int x, y;不合法赋值左侧不是左值x % 2double x;不合法%只接受整型n 10 2int n;合法加法先于赋值3.2 整数除法与类型提升整数除法是这份题库里出现频率最高的知识点没有之一。规则只有一条两个整型操作数做除法结果还是整型小数部分直接丢弃不四舍五入。由此推出两个结论1 / n在n的绝对值大于 1 时恒为 0因为真实结果在 0 和 1 之间取整后是 01 / 2得 0不是 0.5。要让结果带小数必须让参与运算的至少一方是浮点类型。写法上有三种效果一样int i, n 4; double a 1.0 / i; /* 1.0 是浮点字面量i 被提升为 double */ double b 1 / (double)i; /* 强制转换把 i 临时转成 double */ double c (double)1 / i; /* 转换分子同样触发浮点除法 */ /* 反例两边都是 int结果先取整再赋给 double得到 0.000000 */ double d 1 / i;参数说明类型提升的规则是「低精度向高精度靠」int和double混算时int先转成double。所以关键不是结果变量声明成什么类型而是除法执行的那一刻两个操作数是什么类型。很多题的陷阱就设在这里结果变量是double但除法两边都是int赋值发生在取整之后白搭。求1 1/2 1/3 ...前 N 项和的题四个选项的差别就落在这几个点上sum有没有初始化、sum声明成int还是double、累加项写1.0/i还是1/i、for里的分隔符是分号还是逗号。逐项排查的方法是按顺序问自己四个问题变量初始化了吗、类型够宽吗、除法是浮点还是整数、语法结构对吗。四个都对才是正确答案。sum未初始化是个隐蔽的坑。int i, sum;之后直接sum sum i;用的是不确定值结果不可预期。理论考里凡是出现「执行以下程序段sum 的值是」而sum没初始化的判定一律往「结果不确定」上靠不要真去算。3.3 赋值与相等的混淆if (x 20)这种写法在语法上合法在语义上是错的。是赋值运算符整个表达式的值是赋值之后x的值也就是 2020 非零条件恒为真else分支永远进不去x也被改成了 20。题目问「执行后 x 的值不变」判定是错——x被改写了。同类题里还有else if (x 0)的变体这时候赋值表达式的值是 0条件恒为假这个分支永远进不去x同样被改成 0。两个方向都要会判赋非零值恒真赋零值恒假。注意编译器对if (x 20)通常只给警告不给错误所以代码能编译、能运行、结果错。理论考抓的就是这个「能跑但不对」的缝隙。防这类错误有个几乎零成本的习惯写条件时把常量放左边if (20 x)。万一手滑写成if (20 x)编译器直接报错因为 20 不是左值。这个写法在团队里叫「尤达条件」看着别扭但确实能省掉一类排查成本很高的调试。3.4 scanf 与 printf 的格式串对齐格式串和变量类型必须严格对应这是输入输出部分唯一的硬规则。对应关系如下数据类型scanf 格式符printf 格式符常见错法int%d%d用%f读 intfloat%f%f—double%lf%fscanf 用%f读 doublechar%c%c用%d读字符字符串%s%s—最容易记混的一格是doublescanf读double必须用%lf因为要区分float和double的存储宽度printf输出double用%f就行因为可变参数在传递时float已经自动提升为double不需要再区分。这一格不对输入输出全是垃圾值。double x; /* 正确double 配 %lf */ scanf(%lf, x); printf(%.2f\n, x); /* 输出保留两位小数 */ /* 错误示范一double 配 %d读进来的是垃圾且可能破坏内存 */ scanf(%d, x); /* 错误示范二double 配 %f格式符宽度对不上 */ scanf(%f, x); /* 错误示范三变量是 int格式串却写 %lf */ int year; scanf(%lf, year);格式串里除了转换说明还可以夹普通字符这些字符在输入时必须原样敲进去。scanf(x%lf, x)要求用户输入x30这样的形式只敲30是匹配不上的x拿不到值。这类题判断的关键是格式串里的非格式字符属于「必须匹配的字面内容」不是提示文字。scanf里空格的处理也常考。格式串中的空格表示「跳过任意数量的空白字符」scanf(%d %lf %d, a, b, c)里两个空格的作用是允许输入时用任意多个空格、制表符或换行分隔。但格式串末尾多写一个空格会让scanf停下来等更多输入因为它在等一个非空白字符来结束「跳过空白」这个动作。printf这边重点是精度和宽度。%.2f表示小数点后保留两位%4d表示输出至少占 4 个字符宽度、右对齐、不够左边补空格%6.1f表示总宽度 6、小数一位。这类格式串在算循环执行次数、输出对齐的题里经常配套出现判断时不需要精确还原输出只要确认格式符和变量类型对得上。3.5 字符常量与 ASCII 码字符常量用一对单引号括起单个字符A、z、0、$都是合法的字符常量。字符型数据的值就是它在 ASCII 字符表里的次序值所以A的值是 65a是 970是 48。判定题里最爱考的两个反直觉点第一数字字符1的值不是数字 1是 49。1 - 0才等于 1这是把字符转成对应数值的标准写法。第二大小写字母的 ASCII 码差 32且小写比大写大。a - A 32所以a的值比Z大。D - A d - a这个表达式为真因为两边都等于 3大小写偏移量相同。排列规律要记牢三组0到9升序连续A到Z升序连续a到z升序连续。但三组之间不连续——9后面紧跟的不是AZ后面紧跟的也不是a中间夹着其他符号。字符输入输出用getchar和putchar不是只能靠printf和scanf。putchar(S)在屏幕上输出的是S不带引号引号只是代码里标记「这是字符常量」的语法符号不参与输出。getchar()从输入流读一个字符scanf(%c, ch)也能读两者在单字符场景下都能用。4. 流程控制if-else 配对、switch 穿透与 for 边界流程控制这部分题的特点是「代码看着顺结论反着来」。缩进、换行、大括号位置都会骗人唯一可信的是语法规则。复习时把每条规则还原成一句话遇到题就拿这句话去套比凭眼睛扫代码靠谱得多。4.1 悬空 else 与分支覆盖else的配对规则只有一句和之前最近的、还没有配对的if配对。缩进不参与配对纯粹是给人看的排版。int a 2, b -1, c 2; if (a b) if (b 0) c 0; else c; /* 这个 else 配的是内层 if (b 0)不是外层 */分析这段a b即2 -1为假外层if的整个语句体包含内层if-else被跳过c保持 2。如果误以为else配外层就会算成c加 1 得 3。这类题的正确做法是先在纸上给每个if标号再连线配对别用眼睛直读。分支覆盖的题考的是测试用例设计。规则是每个分支至少要被一组用例走到用例数等于需要区分的等价类数量。几个典型数字要记住分支结构分支数最少用例数说明if (grade 60)单分支22小于 60、大于等于 60if-else双分支22真假各一if-else if-else三分支33大于、小于、等于else-if五区间55小于 0、0、0 到 15、15、大于 15字符分类三分支36352 字母 10 数字 1 其他字符分类那道题容易算错因为分支只有三个但字母分支覆盖的是 52 个字符26 大写 26 小写数字分支覆盖 10 个其他分支至少 1 个加起来 63。这里的原则是用例数按「能区分出不同处理路径的输入类别」算不是按分支语句的条数算。还有一类嵌套题要注意if或else后面跟的分号会截断分支体。int x -5, y 0; if (x 0); /* 分号让 if 的分支体变成空语句 */ if (x 0) y 1; else y -1; /* 这个 else 配的是内层 if与外层无关 */ /* 结果x -5内层 if (x 0) 为假走 elsey -1 */4.2 switch 的三条硬规则与穿透switch的规则不多但每条都能单独出题。第一条case后面必须跟常量表达式不能是变量也不能是关系表达式。case choice 1:不合法因为choice 1是一个求值结果不是常量。第二条同一个switch里case的值不能重复。第三条default可以省略位置可以放在任意地方不一定在最后。执行流程分两种差别全在break上写法匹配到 case 之后的行为常见考点每个 case 带break执行对应语句段后跳出 switch与 if-else 链等价不带break从匹配位置一直往下执行穿透到后面所有 case输出结果叠加default写在中间位置不影响匹配优先级只有全部 case 不匹配才进判断等价性不带break的穿透不是 bug是有意设计的特性只是大多数场景下不符合预期。看这道题int x 1, a 0, b 0; switch (x) { case 0: b; /* x 是 1不匹配跳过 */ case 1: a; /* 匹配执行没有 break继续往下 */ case 2: a; b; /* 穿透进来继续执行 */ } /* 结果a 2, b 1 */判断这类题的方法是从匹配点开始往下数数到switch的右大括号为止中间遇到的每条语句都算执行。等价性判断题则反过来把每个switch的完整执行结果包括default兜底算出来逐一比对只有在所有输入下结果都相同才叫等价。default放在最前面但带break的写法和放在最后的写法是等价的因为匹配优先级相同。break的使用范围也要记清break可以用在switch和循环里不是只能配switchswitch里不一定非用break不可也不一定非用default不可。两条「必须」都是错的。4.3 for 三表达式的执行顺序与次数计算for (表达式1; 表达式2; 表达式3) 循环体的执行顺序固定表达式1 只在进入循环前执行一次表达式2 在每次循环体执行前求值为真就执行循环体为假就结束循环表达式3 在每次循环体执行完之后执行。int fahr; double celsius; /* 表达式1fahr 91只执行 1 次 表达式2fahr 100每次循环前判断共判断 11 次第 11 次为假 表达式3fahr每次循环体后执行共执行 10 次 */ for (fahr 91; fahr 100; fahr) celsius 5.0 * (fahr - 32) / 9.0; printf(%4d%6.1f\n, fahr, celsius);这段代码的循环次数算法是(100 - 91) / 1 1 10也就是(终值 - 初值) / 步长 1。三个数字要分清表达式2 求值 11 次10 次为真、1 次为假、循环体执行 10 次、表达式3 执行 10 次。循环结束后fahr是 101不是 100——fahr在最后一次循环体后把 100 加到了 101然后表达式2 判断 101 100 为假退出。问「循环结束时循环变量的值」的题答案永远是「越过终值的第一个值」。循环里还有两个初始化问题属于同一类错误的两个面/* 问题一sum 未初始化累加结果不可预期 */ int i, sum; for (i 1; i 10; i) sum sum i; /* 正确写法int i, sum 0; */ /* 问题二阶乘的累乘单位元是 1不是 0 */ int i; double product; product 0; /* 错0 乘任何数都是 0结果恒为 0 */ for (i 1; i 20; i) product product * i; /* 正确写法product 1; */累加的单位元是 0累乘的单位元是 1。这个「单位元」的概念比死记「加法初始化为 0、乘法初始化为 1」更牢遇到新的累运算累除、累乘方也能自己推。另外累乘的变量类型要够宽int存 13 以上的阶乘就会溢出题目里通常把product声明成double就是在提示这一点。5. 易漏点五类最容易翻车的判定这一章把散在各处的坑集中收一遍。每条按「易漏点 → 为什么漏 → 怎么补」写补法都是能立刻用上的动作不是「多练习」这种空话。5.1 易漏点一整数除法的取整发生在赋值之前为什么漏眼睛看到结果变量是double就默认整个计算是浮点的忽略了除法执行的那一刻两个操作数都是int。类型提升只发生在混算时不发生在赋值时。怎么补读到一个除法表达式先圈出两个操作数分别看它们声明成什么类型。两个都是整型、又希望得到小数结果就必须在分子或分母上加.0或强制转换。养成写完除法回头看一眼操作数类型的习惯比事后 debug 便宜得多。5.2 易漏点二格式串和变量类型错位为什么漏%d、%f、%lf长得太像double在scanf里要用%lf、在printf里用%f这个不对称最容易记混。而且类型错了代码照样编译通过只是结果变成垃圾值。怎么补把对应关系压缩成一句口诀——「读 double 带 l写 double 不带」。scanf是「读」要%lfprintf是「写」用%f。另外格式串里的普通字符比如x必须原样输入这一点在判断题里常被当成「可以省略」。5.3 易漏点三条件表达式里混进赋值号为什么漏if (x 20)和if (x 20)只差一个字符扫一眼根本分不出来。而且赋值表达式有值语法合法编译器只警告不报错。怎么补看到if、while、for的条件里出现单个立刻标红。判断结论时按「赋值表达式的值决定真假」推赋非零恒真赋零恒假同时被赋值的变量已经被改写。写代码时把常量放左边让编译器帮你拦。5.4 易漏点四switch 的穿透和 default 的位置为什么漏case标签看起来像「分支入口」直觉上以为匹配到哪个就只执行哪个忘了没有break会一路往下走。default写在最前面时又容易误以为它有更高的匹配优先级。怎么补判穿透类题从匹配的case开始往下画一条竖线画到右大括号线经过的语句全算执行。判default位置类题记住匹配规则是先比case、全不比才进default位置只影响代码可读性不影响逻辑。5.5 易漏点五循环结束后的循环变量值为什么漏算循环次数时盯着「跑了几轮」忘了问「跑完之后变量停在哪」。for (i 1; i 10; i)跑 10 轮但i结束时是 11因为i先执行、条件判断后失败。怎么补把三个量分开记——条件判断次数、循环体执行次数、循环变量终值。终值等于「最后一次让条件成立的值再走一步」。题目问「语句①执行了几次」和「循环体执行了几次」时先确认语句①在循环体内还是循环体外位置不同答案可能差一次。6. 复习清单按判定路径把判断题重走一遍复习到这一步知识点已经铺开了缺的是一条能重复走的路径。理论考的判断题和选择题绝大多数可以拆成三步判定按顺序走比凭印象答稳得多。步骤看什么典型结论第一步语法边界运算符是否完整、声明是否在前、分号位置缺乘号、声明后用、for 后多分号第二步类型匹配操作数类型、格式串、赋值目标整数除法、%lf对 double、左值检查第三步流程走向else配对、break有无、循环边界就近配对、穿透、终值越界第一步最容易得分因为它只要求你认识语法。看到5(fahr-32)/9、x y 22、x % 2x是double这类写法先别管它想算什么只看合不合法。这一步过了再往下。第二步是失分重灾区。做一道输入输出题先把变量声明抄在草稿纸左边格式串抄在右边一一连线连不上的地方就是答案。做一道表达式题把每个操作数的类型标在它头顶上除法、取模、赋值三类运算逐个核对。第三步管流程。判断题里出现嵌套if先给每个if编号再配对出现switch先看有没有break出现for先把三个表达式各执行几次列出来。这三件事都是机械动作练几遍就能形成条件反射。有一条血泪经验值得记题库里少数题目的标注和语法规则存在出入比如隐式乘法、格式串类型这类。遇到这种题考试时按题库记写代码时按规则来两套系统分开存别用其中一个去覆盖另一个。真正的能力是知道哪条是规则、哪条是题库的约定而不是把两者混成一锅。从那以后我每次做理论题前都强制走一遍「先看语法边界、再看类型匹配、最后看流程走向」这三步草稿纸上必定留三行痕迹一行都不省。这份文档我按判断题、选择题分好了块答案和题干放在一起适合对着这张清单一条条过。系统学的时候当地图用考前当查漏补缺用两种用法都不冲突。希望帮到你。本文还有配套的精品资源点击获取