简介:这是一份2024年信息素养大赛C++算法创意实践挑战赛小学组初赛真题解析,面向具有C++启蒙基础的小学生及竞赛辅导教师。文档以单选题与判断题形式呈现,覆盖变量定义、输入输出、布尔表达式、算术与逻辑运算、条件与循环结构、质数判断等考点,并附背景说明帮助理解题意,适合考前自测、专项练习和课堂讲解。资源包为单个docx文档,大小337KB,内容结构化呈现,便于直接阅读、打印或二次编排。目前已有1528人学习浏览,对于准备信息素养大赛或日常编程思维训练而言,是兼具针对性与实用性的练习素材。
1. 一份真题摆在你面前:它到底在考什么
刚接触C++的人,第一次拿到这份小学组初赛真题,反应多半和我当初一样:题目能看懂一半,代码看着眼熟,真要选答案却心里发虚。其实这套题考的东西非常集中——就是C++最基础的语法、运算符、条件判断、循环,加上一点最朴素的算法逻辑。它不是让你写多长的程序,而是看你对这些基础点有没有真正理解。
这份真题的真正价值,不是“刷完对答案”,而是帮你做一次全面的体检:哪些语法你只是“见过”,哪些你已经真正会用。对零基础新手来说,它是最好的入门路线图——题目里出现的每一个知识点,都是你接下来要逐个攻克的关卡。下面我就按这份真题的考查顺序,带你从头走一遍。
2. 先热身:变量、输入输出和整数运算
很多新手拿到C++第一件事就是背语法,结果背了忘、忘了背。其实不如直接拿真题里的题目当坐标,把每个知识点“钉”在具体题目上。这一章,我们先把最基础的三个东西讲透:变量、cout 输出、整数运算。
2.1 变量是个“贴了标签的盒子”,命名规则要记牢
先看真题第8题:以下正确的变量命名是( )。选项有2things、_myVariable、my-varilable等。正确答案是_myVariable。为什么?因为C++规定,变量名只能由字母、数字、下划线组成,而且不能以数字开头,也不能包含减号。2things以数字开头,不合法;my-varilable里的减号会被当成减法运算符,也不合法。
你可以把变量想象成一个贴了标签的盒子:标签就是变量名,盒子里面存的值就是数据。命名规则就是“标签怎么写才不会被系统认错”。我第一次学的时候总记不住,后来就只记一句话:字母或下划线开头,后面随便接字母、数字、下划线,中间不能有空格和符号。
第7题问“下列关于变量的叙述,不正确的是”,答案是“变量没有定义也能够使用”。这个坑很多新手都踩过——在C++里,变量必须“先声明,后使用”。声明就是告诉系统“我要用一个叫x的盒子”,使用就是往盒子里存值或取值。没声明就用,编译器会直接报错。第20题的判断题“所有变量都必须在使用前声明其数据类型”是正确的,说的也是这件事。
下面是一段演示变量定义和命名规则的代码:
#include <iostream> using namespace std; int main() { int n = 5; // int类型,盒子n,初始值是5 int total = 0; // 再定义一个盒子total,用来累加 double score = 91.5; // 小数要放在double类型的盒子里 total = total + n; // 把n的值加到total上 cout << total << endl; // 输出total当前的值 return 0; }这里的int表示整数类型,double表示浮点数(即小数)类型。每个变量定义时先写类型,再写变量名,最后用等号给它一个初始值。total = total + n这句的意思是“把 total 当前的值加上 n,再存回 total”,别把它当成数学等式,它是赋值操作。
2.2 输出只有一个主角:cout
第4题问C++中输出指令是什么,正确答案是cout。第10题问“如果想在终端输出变量 x 的值,正确的代码是”,答案是cout << x。第18题判断题说“cout 用于输入,而 cin 用于输出”,这是错误的,正好方向反了。
初学者最容易搞混的就是<<和>>的方向。我的记法是:数据流是从变量流到屏幕的,所以cout <<就像把数据“倒进”屏幕里。相反,cin >>是把键盘输入的数据“倒进”变量里,所以箭头指向变量。你只要记住“输出看cout,箭头朝外;输入看cin,箭头朝内”,就不会再翻车。
看看第11题,程序输出结果是“Hello, world!”,这其实就是cout << "Hello, world!"的典型效果。字符串要放在双引号里,它会原样输出到屏幕上。如果想输出变量值、数字、字符串混合的内容,只要用多个<<连接起来就行:
#include <iostream> using namespace std; int main() { int age = 10; cout << "我的年龄是" << age << "岁" << endl; // 输出结果:我的年龄是10岁 return 0; }这里endl表示换行,让光标移到下一行。cout后面可以接字符串、变量、数字,这些内容按顺序拼在一起输出。注意别把<<写成>>,那是输入操作,编译会报错。
2.3 整数运算的三个雷区:、% 和运算顺序
整数运算是这份真题的重头戏。第1题int n = 572765; cout << n/10%10;,这个式子要分两步看:先算n/10,也就是 572765 除以 10,因为两个都是整数,结果会直接舍去小数部分,得到 57276;然后再算57276%10,%是取余运算符,得到个位数字 6。所以答案是 B。很多新手栽在这里,是因为没搞懂整数除法“丢小数”的规则。
第3题问的是int x = 7; int y = x/2*3;,很多人想当然算 7 除以 2 等于 3.5,乘 3 等于 10.5,但答案却是 y 等于 9。为什么?因为 x 是 int 类型,x/2先执行,7 除以 2 在整数运算里结果是 3,小数部分被直接丢掉,然后再乘 3,得到 9。
这里有个新手常忽略的运算优先级问题:*、/、%的优先级相同,结合方向是从左到右,所以x/2*3是先除后乘。如果想让结果带小数,就要把变量声明成 double 类型。真题故意设计成整数,就是为了考察你对“整数除法会截断”这个特性的掌握。我在初学的时候也在这里翻过车,后来每次做运算题都会先在草稿纸上标注“这一步是整数运算还是浮点运算”,基本就不会错了。
3. 条件、循环与布尔:程序开始“讲逻辑”了
基础语法只是砖块,条件判断和循环才是让程序“活起来”的骨架。这一章我们从真题里的条件题、循环题、布尔题入手,把程序的“逻辑思考”过程讲明白。
3.1 条件判断与布尔表达式:只有“真”和“假”两个结果
第2题是这道真题里综合性比较强的一道:两个城市 A 和 B,温度都在 -50 到 50 摄氏度之间,当且仅当只有一个城市的温度低于 0 时输出 1。题目给了补全条件的代码,答案是a<0 b>=0,意思是一个小于零、另一个大于等于零,就满足“只有一个低于0”的条件。
这道题考的是两件事:一是比较运算符>、<、>=、<=的用法;二是逻辑运算,这里两个条件之间是“并且”的关系,用&&连接。第16题判断题“在C++中,&&和||分别代表逻辑与和逻辑或运算”是正确的,说的就是这个。&&表示两个条件同时成立整体才为真,||表示只要其中一个成立整体就为真。
布尔类型用bool关键字表示,第6题问“表示布尔数据类型的关键字”,答案就是bool。bool 变量只有两个取值:true(真)和false(假)。写程序时,条件判断的结果本身就是 bool 值,你可以把它存进变量,也可以直接放在 if 的括号里:
#include <iostream> using namespace std; int main() { int a = -3, b = 5; bool flag = (a < 0 && b >= 0); // 括号里是条件,结果是true或false if (flag) { cout << "只有一个城市低于0度,输出1" << endl; } else { cout << "不满足条件,输出0" << endl; } return 0; }这里(a < 0 && b >= 0)就是布尔表达式,它的结果不是 1 就是 0。注意&&的两个条件必须都满足,整体才为真;如果 a 是 -3、b 是 5,那么这个表达式的结果是true,程序会输出第一行。刚开始写条件判断时,建议把条件单独写在括号里,再用变量接住,这样逻辑更清晰,也方便调试。
3.2 for 循环:不是“至少执行一次”,是“先判断再执行”
第12题考的是输出 1 到 10 之间的所有偶数,补全if (i % 2 == 0)。为什么是这个?因为判断一个数是偶数,就看它除以 2 的余数是不是 0,余数是 0 就是偶数,所以条件是i%2==0。这题同时考察了%运算符和 for 循环的组合使用。
第17题的判断题是个经典陷阱:“在C++中,for 循环至少执行一次。”这句话是错的。for 循环的执行流程是:先初始化变量,再判断条件是否成立,条件成立才进入循环体。如果初始条件就不满足,循环体一次都不执行。它不是 do-while,do-while 才保证至少执行一次。我第一次学的时候就把这两个搞混,后来每次写循环都强迫自己默念一遍“先判断,后执行”。
下面是一个标准的 for 循环输出偶数的完整代码:
#include <iostream> using namespace std; int main() { for (int i = 1; i <= 10; i++) { if (i % 2 == 0) { // 判断i是不是偶数 cout << i << " "; // 输出偶数,加一个空格隔开 } } cout << endl; return 0; }for 循环的三个部分用分号隔开:第一部分int i = 1是初始化循环变量;第二部分i <= 10是循环继续的条件;第三部分i++是每轮循环结束后让 i 加 1。程序执行时会反复检查条件,条件为真就进入循环体执行里面的代码,执行完第三部分,再检查条件,直到条件为假才停下来。如果只写if (i % 2 == 1)或i%2,输出的就是奇数或 1 到 10 所有数字,不是题目要求的偶数。
3.3 逻辑运算符的组合:条件不是越多越好
真题目里还有一个容易忽略的地方:当条件变多时,新手容易写出逻辑混乱的表达式。比如判断“温度在 -50 到 50 之间”,很多人会写成-50 <= t >= 50,这在C++里是错误写法,必须拆成t >= -50 && t <= 50。两个条件之间用&&连接,才表示“同时满足”。
再比如质数判断那题(第15题),补全的答案涉及n % i == 0和布尔变量isprime的配合。判断一个数不是质数,其实就是看有没有一个大于 1 且小于它本身的数能整除它。这个逻辑放到代码里,就是用一个布尔变量记录“我还没找到能整除它的数”,默认假设它是质数,一旦发现n % i == 0,就把标记改成“它不是质数”。这个“先假设、再推翻”的思路,是算法题里非常常用的模型。
第14题的程序输出结果,考的是循环体内多个语句的执行顺序,观察点在于:循环体里如果有多个操作,它们是按顺序执行的,每轮循环都完整走一遍。我看过不少同学在这一题上丢分,不是不会语法,而是没耐心把循环一轮一轮地在草稿纸上推演。遇到循环题不要心算,老老实实列一张表:第几轮、i 是多少、输出什么,马上就能看明白。
4. 避坑指南:五个最容易翻车的瞬间
搞定了基本语法,接下来要面对的是新手最容易踩的坑。这一章我总结了五个高频翻车现场,全部来自真实做题和写代码的“血泪经验”。每条都按“现象、原因、解决”来讲,你只要对照着自查,就能少走很多弯路。
4.1 编译报错不等于你写错了,先看行号再看单词
现象:写完代码一编译,屏幕上冒出一堆英文提示,第一反应是“完了,我是不是不适合学编程”。有些同学看到 error 就直接关掉编译器,或者把整个代码推倒重写。
原因:编译器的报错信息是机器生成的,语气生硬,很多词是专业术语。新手不熟悉这些术语,就会误以为是自己“完全错了”,其实大部分报错只是一个小问题,比如少打一个分号、变量名拼写不一致。
解决:看到报错,先看error前面的文件名和行号,定位到具体一行;再看报错信息里含有的词,比如expected ‘;’是说你漏了分号,undeclared identifier是说某个变量没声明就用了。只要把报错信息当“线索”而不是“判死刑”,你很快就能习惯。我每次教新手,都让他们先把报错信息抄下来,圈出不认识的单词,再对照代码猜含义,这样几轮下来就看懂大半了。
4.2 变量命名“合法”不等于“能跑”
现象:把变量命名为2things、my-var、@cnt,一编译就报语法错误。尤其是my-var,你觉得自己明明写得很清楚,系统却不认。
原因:变量名要遵守C++的标识符规则,数字不能开头,减号、@ 这些符号根本不允许出现在变量名里。很多人把“编程里的名字”和“生活中的名字”混为一谈,认为只要自己能看懂就行,但编译器不这么认为。
解决:记牢这条规则,变量名只能由字母、数字、下划线组成,首字符必须是字母或下划线。命名的时候,建议用能表达含义的英文单词或缩写,比如cnt表示计数器、sum表示总和。写代码前先在纸上列几个候选名字,习惯之后就不会再犯这个低级错误。
4.3 cout 和 cin 的方向总是搞反
现象:写cout >> x或cin << x,编译报错,或者运行时程序行为完全不对劲。这是新手的“高频动作”,几乎每个人都要错一次。
原因:>>和<<在数学里是“大于号”和“小于号”,本身容易混淆方向;再加上 cin 和 cout 的名字长得又像,初学者很难记住到底谁配谁。
解决:记一个口诀:输出是“送出去”,箭头朝外,cout << x;输入是“收进来”,箭头朝内,cin >> x。如果有选项题考这个,你只要看箭头是向外的还是向内的,就能判断。第10题和第18题考的就是这个点,方向搞对了,分数就到手了。
4.4 整数除法会“舍掉”小数,不是四舍五入
现象:算7 / 2,你认为是 3.5,但程序输出 3。换成y = x / 2 * 3,你算出来是 10.5,程序却给你 9,你觉得是玄学。
原因:两个 int 类型的整数做除法,结果还是整数,小数部分直接被丢弃,而且不是四舍五入,是直接截断。这个规则和数学里的除法不一样。
解决:写代码之前,先看一眼参与运算的变量是什么类型。都是整数,就要按整数运算规则来。如果确实需要小数结果,把其中一个变量声明成double,或者在运算时写成x * 1.0 / 2,强制转成浮点运算。每次做题时,我习惯在式子旁边标注“整”或“浮”,提醒自己这一句会丢不丢小数。
4.5 变量不初始化,输出的值像变了魔法
现象:定义一个int x;然后直接cout << x;,输出的数字每次运行都不一样,有时是 0,有时是 423465,看起来像“随机数”。
原因:变量定义后如果不初始化,它的值是不确定的,存的是这块内存地址里之前残留的数据。第19题判断题说“变量声明后,如果不初始化,其值是确定的”,这是错误的,正是这个道理。
解决:定义一个变量时就顺手给它一个初值。计数的写int cnt = 0;,累加的写int sum = 0;,不知道给什么就先给 0。这个习惯一开始可能觉得多余,但等程序规模变大、数据一多,没有初值的变量就是一颗不定时炸弹。从那以后我每次定义变量都会顺手补上= 0,宁可多敲几个字符,也不想让“玄学值”半夜半夜地折磨自己。
5. 算法题的拆解方法:从“神奇口袋”和“质数”学起来
真题里有两道“看起来很难”,其实考得并不复杂的算法题:神奇口袋和质数判断。很多新手一看到这类题就发怵,觉得“算法”两个字很吓人,其实算法就是一个“把问题翻译成代码步骤”的过程。这一章我们就用这两道题,把拆解方法彻底讲明白。
5.1 神奇口袋:先把题目翻译成“变量和操作”
第5题描述了这样一个场景:有一个口袋,只能装“平方数”,装进去的数字累加起来不能超过负载量 L。给定 n 个数,从左往右筛选平方数,能装几个就输出几个。举例中,L 是 100,5 个数是 49、19、36、25、73,其中平方数是 49 和 36,累加得到 85,没超过 100,所以能装 2 个。
新手会觉得这题无从下手。其实拆解之后就四步:读一个数,判断它是不是平方数,如果累加上去会超过 L 就停止,否则装进去并计数。题目要补全的代码,就是“判断平方数”“判断累加是否超限”“超限后跳出循环”这三个位置。
什么是平方数?就是一个整数乘以自己得到的结果,比如 1、4、9、16、36、49。判断的方法,就是从小到大找一个数 j,看j * j是否等于 x。下面是这道题一个可运行的完整写法:
#include <iostream> using namespace std; int main() { int L, n, x; cin >> L >> n; // 输入负载量和数字个数 int sum = 0; // 已装入口袋的平方数之和 int cnt = 0; // 装了几个平方数 for (int i = 0; i < n; i++) { cin >> x; // 依次读取每个数 // 判断x是不是平方数:从1开始试到x for (int j = 1; j * j <= x; j++) { if (j * j == x) { // 找到平方数j*j正好等于x if (sum + x <= L) { // 还能装下 sum += x; // 装进去,累加和更新 cnt++; // 计数器加1 } else { // 再加就超负载了,后面的也不用看了 return 0; // 直接结束程序 } break; // 已经判断完x,不用继续找j } } } cout << cnt << endl; // 输出最终能装下的个数 return 0; }这段代码里,外层 for 循环负责“从左往右读 n 个数”,内层 for 循环负责“找一个 j 使得 j 的平方正好等于 x”。当j * j == x时,说明 x 是平方数;然后再判断“当前累加和 + x”是否小于等于负载量 L,如果是,就装进去并计数;如果不是,说明已经装不下了,直接结束程序。注意这里每个平方数只判断一次,找到 j 后立刻break跳出内层循环,避免白白往下算。
这道题考察的真正能力,是把生活化的描述“翻译”成变量和运算。口般怎么描述,代码就怎么写:口袋的负载量是 L,已装的总和是 sum,计数器是 cnt,一个个看是循环,装不下就停是 break。翻译清楚了,代码就出来了。
5.2 质数判断:用布尔变量“先假设,再推翻”
第15题是经典的质数判断。质数就是只能被 1 和它本身整除的正整数,比如 2、3、5、7、11。题目要补全两处代码,答案是bool isprime = true;和n % i == 0。为什么是这两处?我们用完整逻辑来分析。
判断一个数 n 是不是质数,最朴素的思路是:从 2 开始,一直试到比 n 小的数,看有没有一个 i 能整除 n。如果没有,n 就是质数;一旦找到,n 就不是质数。这里引入一个布尔变量isprime,开始时假设它是质数(值为 true),然后逐个尝试,发现能整除就改成 false,并立刻结束检查:
#include <iostream> using namespace std; bool isPrime(int n) { if (n <= 1) return false; // 0和1不是质数 bool isprime = true; // 先假设n是质数 for (int i = 2; i * i <= n; i++) { // 只用试到√n if (n % i == 0) { // 找到一个能整除n的数 isprime = false; // 推翻假设 break; // 不用再试了 } } return isprime; } int main() { int n; cin >> n; if (isPrime(n)) { cout << "是质数" << endl; } else { cout << "不是质数" << endl; } return 0; }这里有个提高效率的小技巧:检查范围不用从 2 到 n-1,只需要试到√n,也就是循环条件写的i * i <= n。因为如果 n 有一个大于√n的因数,那必然还有一个小于√n的因数成对出现。比如 n 是 100,我们只需要试到 10 就能发现 10 整除 100;如果试到 10 还没找到能整除的,那更大的数也找不到了。
这道题在真题里作为“补全代码”出现,本质上考的也是“先假设、再推翻”的模型。我把这个模型称为“布尔标志法”,它是算法入门第一个要养成的思维习惯:用一个变量记录当前状态,初始状态设为“还没找到反例”,一旦出现反例就更新状态。不只质数判断,后面你会遇到很多问题都用得上这个思路。
6. 下一步学什么:三条值得走的路
如果你已经能看懂上面这些代码,并且把真题里的题都过了一遍,说明你已经有了一个非常扎实的起点。接下来要做的,不是继续刷题,而是有方向地往前走。我给三条路,你可以按自己的情况选。
第一条路:把输入输出这块地基打得再稳一点。多练cin和cout的不同组合,尤其是输出多个变量和字符串混合的情况,以及如何用setprecision控制小数位数。真题只考了最基础的用法,但你以后写的程序不可能永远只输出整数,提前熟悉这些细节,后面写大程序会顺畅很多。
第二条路:练习“循环加条件”的组合题。比如“求 1 到 100 以内所有能被 3 整除但不能被 5 整除的数字之和”“打印一个用星号组成的三角形”。这类题没有新语法,就是反复练 for 循环和 if 条件的配合。我当初就是从这类题开始,才慢慢体会到“程序就是把复杂问题分解成重复步骤”这句话的真正含义。
第三条路:接触更系统的算法题。当你觉得 for 循环已经得心应手了,就可以去挑战求最大公约数、排序、找最大值这类经典入门题。这些题会用到更复杂的逻辑,也会让你开始思考“时间复杂度”是什么。不过你要记住一句话:算法题刷得再多,都不如先把每一道题从头到尾亲手写一遍、亲手调试一遍来得实在。我见过太多人收藏了无数题单,真到考试时却连最简单的 for 循环都会漏掉括号。
如果你现在问我最想给新手一个什么建议,我会说:每次写完一段代码,不要急着看答案,先自己盯着代码在草稿纸上推演一遍,模拟程序的每一步执行,想象自己是电脑。这个习惯我坚持了很长时间,它让我几乎不再犯“我以为是这样”的低级错误。编程不是靠背,是靠“亲手跑通”。哪怕是你觉得最简单的代码,也请一定敲进编译器里运行一次,亲眼看到输出结果,那种踏实感是任何笔记都给不了你的。希望帮到你,接下来就从一个你最感兴趣的题目开始吧。
本文还有配套的精品资源,点击获取