E8 C++ 语法与易错点

对应大纲: 2.1.2 的全部缺口——switch/do-while、指针与引用、结构体与联合体、字符串处理、函数与传参、作用域、STL、文件读写、运算符优先级、流程图

为什么单开一份: L01L07S1S6 教的是算法,默认你语法都会。但初赛的单选题和阅读程序题大量考语法细节——本章补的就是这一层。


一、switchdo-while(Python 转过来的人必看)

switch

switch (x) {
    case 1: cout << "一"; break;
    case 2: cout << "二"; break;
    default: cout << "其他";
}

⚠️ 三条铁律:

  1. 漏了 break 会「贯穿」——从匹配的 case 开始一直往下执行,直到遇到 break 或结尾。
  2. switch 的表达式必须是整型或字符型,不能是 double、不能是 string。写 switch(3.5) 编译报错:switch quantity not an integer
  3. case 后面必须是常量,不能写区间(case 1..5 不合法),也不能写变量。

📝 case 不是「分几个格子」,是「从哪儿开始往下跑」。

⚠️ 成绩等级那种区间判断(90\ge 90 优秀、80\ge 80 良好…)用 switch 写不了,除非先 score / 10 转成整数档位。

do-while

do {
    // 循环体
} while (条件);     // ← 这个分号必须有!

while 的唯一区别:do-while 至少执行一次(先做再判断)。

⚠️ do {...} while (条件); 是整个 C++ 里唯一一个「右括号后面要加分号」的地方——所以它才被反复考。

写法 后面要分号吗
if (...) {...} ✗ 不要
for (...) {...} ✗ 不要
while (...) {...} ✗ 不要
do {...} while (...) ✓ 必须要

⚠️ 反过来:for (...); 加了分号就变成空循环体,循环白跑一遍。这是初赛阅读程序题的常客——看到 forif 后面紧跟分号,立刻警觉。


二、运算符与类型

优先级(记这几条就够)

()  []  ->  .          最高
!  ~  ++  --  (强制类型转换)  一元-
*  /  %
+  -
<<  >>
<  <=  >  >=
==  !=
&
^
|
&&
||
?:                     三目
=  +=  -=  ...         赋值(最低,右结合)

⚠️ 位运算的优先级比比较运算还低! 所以 if (x & 1 == 0) 实际是 x & (1 == 0) = x & 0 = 0。必须写 if ((x & 1) == 0)

⚠️ << 的优先级低于 +cout << a + b 没问题,但 cout << a & b 会出事。

===(每年都考)

if (num = i * i) { ... }   // 这是赋值!结果非零就永远为真
if (num == i * i) { ... }  // 这才是比较

⚠️ if (a = b) 能编译、能运行、大多数时候恒为真。 这是 C++ 最经典的坑(CSP 2024 第 19 题就考了这个空)。

整数除法与取模

表达式 结果
7 / 2 3(整除,直接砍掉小数)
7.0 / 27 / 2.0 3.5
-7 / 2 -3(向零取整,不是向下取整)
-7 % 2 -1(余数的符号跟被除数)
x % 0 运行时崩溃(不是 0!)

⚠️ n % 0n / 0 都是致命错误,程序直接崩(CSP 2019 第 16 题实测退出码 INTEGER_DIVIDE_BY_ZERO)。

自增自减

int a = 5, b = a++;   // b = 5, a = 6   (后加:先交出旧值,再加)
int c = 5, d = ++c;   // d = 6, c = 6   (前加:先加,再交出新值)

📝 ac 最后都是 6——变量自己一定加到 6,差别只在「交出去的那个值」。++ 在前就先加,在后就后加。

⚠️ 不要去碰 a = a++ + ++a 这种,那是未定义行为,初赛不考。


三、指针与引用

指针

int x = 10;
int *p = &x;     // p 存的是 x 的地址
cout << *p;      // 10  —— 解引用,取出 p 指向的值
*p = 20;         // x 变成 20
写法 改的是谁
p = q 指针本身——让 p 改指向 q 所指的地方
*p = *q 内容——把 q 指向的值抄给 p 指向的变量

⚠️ CSP 2022 第 3 题就考这一字之差p = q 之后 xy 的值一个都没变

引用

void solve(int &a, int b) {   // a 是引用,b 是副本
    a = 100;   // 外面的实参真的变成 100
    b = 200;   // 白改
}

📝 看到函数参数,第一件事:把带 & 的圈出来。 不带 & 的,函数里怎么改都跟外面无关。

⚠️ CSP 2025 第 10 题void solve(int &a, int b) 里写了「不用临时变量交换两数」的经典三行,但只有一个参数是引用——交换废了一半,结果 x=10,y=10x=10,\ y=10

数组与指针


四、结构体与联合体

struct Point { int x, y; };      // 每个成员各占各的
union Data { int n; float f; };  // 所有成员共用同一块内存
struct union
内存 各成员分别占用 全部共用一块
sizeof 各成员之和(含对齐) 最大成员的大小
有效成员 全部 只有最后写入的那个

⚠️ 访问成员:对象用 .,指针用 ->

Point p;  p.x = 1;         // 对象
Point *q; q->x = 1;        // 指针,等价于 (*q).x

CSP 2023 第 3 题就考这个:dataunion Data 类型的对象,所以写 data.value = 3.14;(不是 ->,也不是把变量名和成员名写反)。


五、字符串处理

char 数组(C 风格)

char s[100];
scanf("%s", s);          // 注意:s 前面不加 &
strlen(s);               // 长度(不含 '\0')
strcpy(a, b); strcat(a, b); strcmp(a, b);   // 需要 <cstring>

⚠️ C 风格字符串以 '\0' 结尾,所以 char s[100] 最多存 99 个字符。

string 类(C++ 风格)

#include <string>
string s;
cin >> s;                // 读一个单词
getline(cin, s);         // 读一整行
s.length()  s.size()     // 两个是同一个函数的两个名字,永远相等
s + "abc"    s += 'a'    // 可以直接拼接,也可以拼单个 char
s.substr(pos, len)  s.find("x")

⚠️ CSP 2025 第 9 题逐条考了 string 的四个说法,正确的只有「可以用 + 连接 stringchar」。其余三个(长度不可变、lengthsize 可能不同、必须以 '\0' 结尾)都是把 char 数组的知识生搬过来的错误。

📝 一句话:string 自己管长度,不靠 '\0',而且能变长。

字符判断函数(<cctype>

isdigit isalpha isupper islower toupper tolower


六、函数与作用域

概念 要点
形参 / 实参 定义时写的是形参,调用时传的是实参
传值 传副本,函数内修改不影响外面
传引用 & 传本体,函数内修改会影响外面
返回值 一个函数只能返回一个值(要多个就用引用参数或结构体)
局部变量 只在定义它的 {} 内有效,函数结束就消失
全局变量 全程序有效,自动初始化为 0

⚠️ 函数必须「先声明再使用」。 如果把函数定义放在 main 后面又没在前面加声明,编译直接报错 'xxx' was not declared in this scope(CSP 2021 第 16 题实测确认)。

📝 全局数组自动清零,局部数组不会。 int a[100]; 写在 main 里面,里面装的是垃圾值;写在 main 外面就全是 0。这也是竞赛里习惯把大数组开在全局的原因之一。

递归

两个必备零件:① 递归出口(边界条件)② 每次调用规模都要变小。

⚠️ 少一个就是死循环 / 栈溢出。

📝 初赛的递归题一律列表格从小往大填,别在脑子里展开调用树。


七、STL 常用(大纲 2.1.2-13)

名字 用途
min max swap 基础
sort(a, a+n) 排序,O(nlogn)O(n\log n)不稳定
stable_sort 稳定排序
lower_bound / upper_bound 二分查找(要求已排序)
vector 变长数组
stack / queue 栈 / 队列

详见 S4_vector与stack.md


八、文件读写(大纲 2.1.2-12)

复赛必须用文件输入输出(第一轮不考实操,但会考概念):

freopen("data.in", "r", stdin);
freopen("data.out", "w", stdout);

详见 freopen函数用法教程.md

📝 概念题要点:文本文件 vs 二进制文件;文件重定向的作用(不改 cin/cout 的写法就能读写文件)。


九、数学库函数(大纲 2.1.2-5,<cmath>

函数 作用 ⚠️
abs / fabs 整数 / 浮点绝对值
floor / ceil 下取整 / 上取整 返回值是 double
round 四舍五入
sqrt 平方根 负数返回 nan,不报错
pow(a, b) 返回 double,有精度问题
log / exp 自然对数 / 指数
sin cos tan 三角函数 参数是弧度不是角度

⚠️ pow(result, 2) 返回的是 doublecout 打印时可能和整数不一样。CSP 2024 第 18 题就利用了「问的是函数返回值还是程序输出」的差别——输出是 pow(f,2),返回值是 f,差一个平方。


30 秒自测

  1. do {...} while (x); 后面要不要分号?for (...) {...} 呢?
  2. switch 的表达式可以是 double 吗?漏了 break 会怎样?
  3. if (x & 1 == 0) 有什么问题?
  4. int a=5, b=a++; 之后 ab 各是多少?
  5. p = q*p = *q 有什么区别?
  6. structunionsizeof 分别怎么算?
  7. stringlength()size() 会不同吗?string 要不要以 '\0' 结尾?
  8. 全局数组和局部数组,哪个自动清零?
参考答案
  1. do-while 分号;for 不要(加了就是空循环体)
  2. 不可以(必须整型或字符型);漏 break 会一直往下贯穿执行
  3. 位运算优先级低于 ==,实际算的是 x & (1==0) = x & 0 = 0;要写 ((x & 1) == 0)
  4. a = 6b = 5
  5. 前者改指针指向,后者改指向的内容
  6. struct 是各成员之和(含对齐);union最大成员的大小
  7. 永远相同不需要string 自己管长度)
  8. 全局数组自动清零