简介:这份资源是华南理工大学《C++程序设计基础》A卷期末试卷的PDF电子版,面向正在学习C++语法、准备课程考试或需要刷题巩固基础的高校学生。试卷为闭卷形式,满分100分、考试时间120分钟,共四大题,覆盖常量与变量、逻辑运算符、自增自减、条件运算符、for与while循环、switch语句、函数原型与重载、const引用、数组与指针、字符串处理及结构体等核心考点,选择题部分还配有逐题解析,便于对照理解易错点。资源包内共1个PDF文件,大小约288KB,轻量便携,适合打印或电子设备随时查阅。目前已有116人学习浏览。读者可借助这份真题检验对C++基础语法的掌握程度,通过解析厘清运算符优先级、循环终止条件、指针与数组地址表示等常见失分点,并据此定位薄弱环节进行针对性复习。
1. 一份C++期末卷子,为什么值得当成“知识地图”来看
很多人第一次看到“C++程序设计基础A卷”这种标题,第一反应是:这不就是一份考过的旧卷子吗,跟我有什么关系?其实换个角度看,一份结构完整的期末试卷,本质上是一张被压缩过的知识地图——出题人把一学期里最想让你掌握的东西,按分值摆在了你面前。哪里考选择题、哪里考读程序写结果、哪里考填空补全、哪里考独立编程,比例本身就是一种信号。
这份卷子把C++入门阶段的核心概念几乎扫了一遍:常量与变量、运算符优先级、逻辑表达式、循环与分支、函数原型与重载、数组与指针、结构体、动态内存。它不深,但覆盖面广,特别适合用来做一件事:检验自己到底是“看懂了”还是“真会用”。下面就从这份卷子出发,把几个最容易让人懵的概念一个个讲透。
2. 从选择题看C++的“地基”:常量、运算符与表达式
2.1 常量为什么“不合法”:名字和值的边界
卷子第一题问哪个是不合法的常量,选项里有0xab、"student"、618E3和count。前三个分别是十六进制整数、字符串字面量、科学计数法浮点数,都是“写死在代码里、程序运行中不会变”的值。而count看起来像个标识符,也就是变量名,它本身不是常量。
打个比方:常量像是刻在石头上的字,变量像是写在白板上的字。你指着一块白板说“这是一块石头”,显然不对。C++里判断一个东西是不是常量,关键看它是不是“字面量”或者被const修饰过。count既没有引号也没有数字特征,编译器会把它当成一个还没声明的名字,所以它不合法。
这里有个容易踩坑的点:618E3这种写法很多人没见过,以为带字母就是错的。其实E表示科学计数法的指数,618E3就是 618×10³。C++对数值字面量的写法比较宽容,十六进制、八进制、浮点科学计数都认,唯独不认“光秃秃一个单词”。
2.2 短路求值:--a && --b到底执行了几次
第二题int a=1,b=1; --a && --b;问执行后 a 和 b 的值。很多人会下意识觉得两个都自减了,答案选 00。但正确答案是 a=0、b=1。
原因在于逻辑与&&的短路特性:先算左边--a,a 从 1 变成 0,结果为假;既然左边已经是假,整个与表达式一定是假,右边--b根本不会执行。所以 b 保持 1。
通俗地说,这就像两个人接力搬东西,第一个人发现门锁着,直接回头说“搬不了”,第二个人连门都不用走到。短路求值在真实代码里非常有用,比如if (p != nullptr && p->value > 0),前半句挡住空指针,后半句才不会崩。这也是为什么很多老手写判断时习惯把“最可能失败、最便宜”的条件放左边。
2.3 自增自减的“前后之分”与非法组合
第四题问哪个表达式正确,选项里出现了++(a++)、a++++b、++(++a)、++a++b这类写法。这里考的是自增运算符的操作数必须是“左值”,也就是能放在等号左边、有确定内存位置的东西。
a++的结果是一个临时值,不是左值,所以不能再对它++。a++++b会被编译器拆成(a++)++b,同样非法。++a++b则是两个运算符挤在一起没有分隔,语法上就说不通。只有++(++a)是合法的:++a返回的是 a 本身的引用,还是左值,可以再自增一次。
这类题看着像文字游戏,但它背后是C++一个很硬的原则:运算符不是随便拼的,每个运算符对操作数有类型要求。初学者写代码时如果遇到“lvalue required”的报错,多半就是动了临时值。
2.4 条件运算符与逗号表达式的优先级
第五题x=1,y=2,表达式x>y? x++: y++, x+y的值。这里有两个坑:一是条件运算符?:的优先级高于逗号,所以先算x>y? x++: y++;二是逗号表达式从左到右依次求值,整个表达式的值是最后一个子表达式的值。
x=1、y=2,x>y为假,执行y++,y 变成 3,这个子表达式的值是自增前的 2。然后算x+y,此时 x 还是 1,y 是 3,结果是 4。所以答案选 B。
提示:逗号表达式在真实工程里很少用来“算值”,更多出现在
for循环的更新部分,比如for(i=0,j=n-1; i<j; i++,j--)。把它和条件运算符混在一起考,纯粹是为了逼你记住优先级表。
3. 循环、分支与函数:控制流的“常见翻车点”
3.1for循环里那个“赋值”而不是“比较”
第六题for (i=10; i=count; i--)问循环次数。很多人扫一眼以为条件是i==count,其实写的是i=count,一个等号。赋值表达式的值是赋进去的那个值,只要 count 非零,条件就恒为真,循环会一直跑下去。但题目里 count 初始为 1,循环体里还有count--,跑一次后 count 变 0,下一轮条件i=0为假,循环结束。所以实际只执行 1 次。
这道题是典型的“血泪经验”型考点:把==写成=是C++里最古老的 bug 之一,编译器通常只给个警告。现在很多团队要求写if (0 == x)这种“常量在左”的写法,就是为了让误写成赋值时直接编译报错。
3.2switch的穿透:没有break会怎样
第八题m=2,switch里 case 1、case 2、default 都没有break。程序从 case 2 匹配进去,输出"2",然后不跳出,继续执行 default,输出"default",最终屏幕上是2default。
switch的穿透(fall-through)不是 bug,是语言特性。它允许你把多个 case 合并处理,比如:
switch (grade) { case 'A': case 'B': cout << "合格"; // A 和 B 都走这里 break; case 'C': cout << "待定"; break; default: cout << "不合格"; }但如果你忘了写break,程序就会“顺流而下”。现代编译器一般会给出-Wimplicit-fallthrough警告,看到这个警告别忽略,十有八九是漏了break。
3.3 函数原型:默认参数和参数名的规矩
第九题问哪个函数原型有错,答案是Func(int a, b, c);。C++要求每个参数都必须写类型,不能像某些语言那样只写第一个。int a, b, c在变量声明里合法,但在函数参数列表里,b和c前面没有类型,编译器不知道它们是什么。
另外几个选项里,Func(int&, int*)是引用加指针,Func(int*, int*)是两个指针,Func(int=1, int=2)是带默认参数,都合法。默认参数还有一条规矩:一旦某个参数给了默认值,它右边的所有参数都必须有默认值。所以Func(int=1, int)是非法的,而Func(int, int=2)合法。
3.4const引用:为什么func(MAX)能过、func(&PI)不能过
第十题和第十一题连着考const。const int MAX=1000;传给func能正确执行,说明func的参数类型必须能接住一个常量。int func(int&)不行,因为普通引用可能修改原值,编译器不允许把常量绑到可写引用上;int func(const int&)可以,常量引用承诺“只读”。指针版本同理,const int*能接&MAX,int*不行。
第十一题里const double &配int*,选项 B 写fun1(&PI, *x):&PI是const double*,不能传给const double&(引用要的是值或对象,不是指针),*x是 int 值,不能传给int*。两个参数都错位了。
这类题的现实意义在于:当你写一个“只读”函数时,参数尽量用const引用,这样既能避免拷贝,又能接受常量实参,还能向调用者承诺“我不会改你的数据”。
4. 数组、指针与字符串:最容易混淆的“地址游戏”
4.1 二维数组的地址表示:a[0]、*a、&a[0][0]有什么区别
第十五题int a[2][2]={1,2,3,4};问哪个不能表示a[0][0]的地址。a[0]是第一行的数组名,退化成指向首元素的指针,等价于&a[0][0];&a[0][0]直接取地址;*a对二维数组名解引用,得到第一行,同样退化成&a[0][0]。而*a如果单独看,它的“值”是第一行的首地址,但题目问的是“地址的表示”,*a和a[0]在数值上相同。
真正不能表示的是选项里那个*a吗?需要仔细看:a[0]是int*,&a[0][0]是int*,*(a[0])是a[0][0]的值,也就是整数 1,不是地址。所以不能表示地址的是*(a[0])。
这里的关键是分清“数组名退化”和“解引用取值”。二维数组可以想成一栋楼:a是楼,a[0]是第一层,a[0][0]是第一层第一个房间。a[0]作为楼层名,可以当“第一层入口地址”用;但*(a[0])是走进第一个房间拿出来的东西,是值不是门牌号。
4.2 指针运算:pAry+1和pAry[1]是不是一回事
第十八题int Ary[5]={1,2,3,4,5}, *pAry=Ary;问哪个不能表示数组元素。*pAry是Ary[0],pAry[0]也是Ary[0],pAry[pAry-Ary]里pAry-Ary是 0,等价于pAry[0]。而pAry+1是指针,指向Ary[1],它本身不是元素值。
指针和数组的关系可以这样记:p[i]是语法糖,编译器把它翻译成*(p+i)。所以pAry[1]和*(pAry+1)完全等价,都是第二个元素的值。但pAry+1只是地址,想拿值还得解引用。
4.3 字符串长度:转义字符和\0怎么数
第十六题char *str="\nThank\10you,\0sir! ";问strlen。strlen数到第一个\0为止,不包含\0。逐个看:\n是一个字符,Thank五个,\10是八进制转义,表示 ASCII 码 8,算一个字符,you,四个,然后\0出现,到此为止。合计 1+5+1+4=11?选项里有 11,但还要注意\10后面紧跟you,八进制转义最多吃三位,\10已经两位,遇到y不是八进制数字就停,所以没问题。总数是 11,对应选项 D。
注意:八进制转义
\10和十进制 10 不是一回事,它是八进制的 8。写字符串时如果\后面跟数字,一定要清楚它吃几位,否则很容易多算或少算。
4.4 结构体指针:->和.的使用边界
第二十题Person person[2], *pPer=person;问哪个引用形式错误。person[1].code是数组元素用点,正确;pPer->code是指针用箭头,正确;(*pPer).code是先解引用再点,也正确。而person[1]->code错在person[1]是结构体对象不是指针,不能用箭头。
箭头->本质上是(*p).member的简写,只能用在指针上。这个规则简单,但初学时容易因为“数组名像指针”而误用。记住一句话:左边是指针用箭头,左边是对象用点。
5. 常见误区与易混概念:这几组东西别再搞反了
5.1 常量 vs 字面量 vsconst变量
易混点:三者都“不变”,但层次不同。字面量是直接写出来的值,比如42、"abc";常量是广义的“不可变值”,包括字面量和const修饰的变量;const变量是带名字的只读变量,它在内存里有位置,只是编译器禁止你改。
为什么混:日常说话时“常量”被泛用,考试里却要区分“合不合法”。怎么区分:看有没有名字、有没有类型修饰。count没声明,既不是字面量也不是const变量,所以不合法。
5.2 数组名 vs 指针变量
易混点:数组名在很多场合退化成指针,导致初学者以为它就是个指针。但数组名不是左值,不能被赋值,sizeof得到的是整个数组大小;指针变量可以改指向,sizeof得到的是指针本身大小。
为什么混:函数传参时数组退化成指针,行为看起来一样。怎么区分:问自己“能不能a = b”。数组名不能,指针能。
5.3 函数重载 vs 默认参数
易混点:两者都能让“同一个名字接不同调用”,但机制不同。重载是多个函数同名、参数列表不同;默认参数是一个函数,部分参数有缺省值。
为什么混:都能实现“少传参数也能调”。怎么区分:重载的函数在编译后是不同的符号,默认参数只是一个函数。卷子第十四题里int same(int,int=1);和int same(int,int=3);不能共存,因为参数列表相同,默认值不同不构成重载,反而会造成调用歧义。
5.4 值传递 vs 引用传递 vs 指针传递
易混点:三者都能把数据送进函数,但效果不同。值传递拷贝一份,函数内改不影响外面;引用传递是别名,改就是改原对象;指针传递传地址,函数内通过*p改也能影响外面,但指针本身可以改指向。
为什么混:引用和指针在“能改原值”上表现相似。怎么区分:看函数签名里有没有&或*,以及调用时传的是对象还是地址。卷子里fun1(int[], int, double&)的第三个参数就是引用,用来把平均值“带回来”。
5.5strlenvssizeof对字符串
易混点:strlen数到\0前的字符数,sizeof数整个数组占多少字节。对char str[]="Hello!",strlen是 6,sizeof是 7(含\0)。对指针char *p="Hello!",sizeof(p)是指针大小,通常是 4 或 8,跟字符串长度无关。
为什么混:都返回一个“长度”数字。怎么区分:问“这个数字在编译期能定吗”。sizeof能,strlen要运行到\0才知道。
6. 什么时候该用这份卷子,什么时候别把它当全部
这份A卷最适合的场景很明确:你已经学过一遍C++基础语法,想快速自查“哪些点只是眼熟、哪些点真能写对”。它的选择题和读程序题覆盖了短路求值、switch穿透、指针退化、const绑定这些高频翻车点,做完一遍能把模糊地带照出来。填空和编程题则逼你动手补全函数、写排序、写逆置,属于“看会了不等于写会了”的检验。
但它也有明显的边界。第一,它不考面向对象的核心内容,类、继承、多态、模板基本没出现,所以别拿它当“C++全貌”来复习。第二,题目偏应试,有些写法(比如++(++a))在工程里几乎不会出现,花太多时间钻这类边角料性价比不高。第三,它没有工程环境下的调试、编译错误阅读、内存泄漏排查,而这些恰恰是真实写代码时最耗时间的部分。
我自己的习惯是:拿这类卷子当“体检表”,限时做完,错题按知识点归类,而不是按题号抄答案。比如短路求值错了一次,就把所有带&&、||的表达式重新过一遍,再写两段小代码验证。错题归类比刷十套卷子都管用,这是我踩过坑之后才养成的习惯。
提示:做读程序写结果题时,别在脑子里跑,拿张纸把变量值一行行写下来。C++的求值顺序和短路规则,心算十次错三次,手写一次就记住了。
如果你手头正好缺一份结构完整的基础练习,这份卷子可以当作起点:先做选择题暴露概念盲区,再挑填空和编程题练手,最后对照解析把每个“为什么”讲给自己听。希望帮到你。
本文还有配套的精品资源,点击获取