
你好,这是我对最近学习的C++知识和易错点的简单总结
写题思路小技巧 📝
先梳理步骤、模拟情境,能大幅减少后续思考时间。
- 先把题目整体框架捋出来:需要给哪些变量赋值、有几层循环、核心判断条件是什么;
- 用一串数字 / 字符模拟执行情境,比如先写 "i=0 时执行 XX,i=1 时执行 XX";
- 把步骤简单写下来,后续写代码时直接对照,能少走很多弯路。
一、C++ 入门 🚀
1. 头文件与命名空间
一句话理解
C++ 入门最容易漏写正确的头文件,或忘记加命名空间导致 cin/cout 用不了。
深入
刚开始写代码时,经常犯两个基础错:
- 头文件漏加:比如用 cin/cout 要加**
<iostream>**,用 abs 函数要加<cstdlib>,漏了就会报错; - 命名空间:
using namespace std;
补充✨✨✨
- 核心头文件对应场景:
- 输入输出(cin/cout)→
<iostream>- 数学函数(abs/fabs/round)→
<cmath>- 字符串函数(memset/strcpy)→
<cstring>- 标准库函数(abs/exit)→
<cstdlib>- 字符判断(islower/tolower)→
<cctype>
2. cin/cout 使用易错点
一句话理解
cin 用 >>、cout 用 <<,换行 / 空格要手动加,空格不会自动出现。
- 易错点 1:cout 打换行要加
endl(或\n),比如cout << 123 << endl;,忘加就不会换行; - 易错点 2:cout 打印空格必须手动写
" ",比如想打 "1 2 3",要写cout << 1 << " " << 2 << " " << 3;,不然会变成 "123"。
3. 全局变量 vs 局部变量
竞赛里用全局变量,省事儿还不容易出问题。
4. 进制与科学计数法
- 八进制以 0 开头、16 进制以 0x 开头
- 科学计数法用 E 表示 10 的次方,且结果是浮点型
- 进制写法:
- 八进制:
012→ 对应十进制的 10; - 16 进制:
0X1A→ 对应十进制的 26;
- 八进制:
- 科学计数法:
3.14E2= 3.14×10² = 314.0(不管 E 前是整数还是小数,结果都是浮点型);5E-3= 5×10⁻³ = 0.005。
补充
16 进制的 X 大小写都可以(0x1A/0X1A),但习惯上写小写更常见。
5. #define 定义常量
#define 是直接替换文本,不用加分号,也不关注数据类型。
深入
#define 的用法超简单:#define 名字 内容,比如:
#define PI 3.14159→ 代码里所有 PI 都会被替换成 3.14159;- 易错点:末尾不要加分号!如果写
#define PI 3.14;,替换后会变成3.14;,分号也会加进去
6. const 定义常量
const 定义常量要加类型和分号,比 #define 更严谨,常量名习惯大写。
深入
const 的语法是:const 类型 常量名 = 值;,比如:
const double PI = 3.14159;→ 必须加 double 类型,末尾加分号;- 和 #define 的区别:
- const 前面没有 #,#define 必须加 #;
- const 要指定类型,#define 只做文本替换;
- const 末尾加分号,#define 不加。
补充
⚠️ (不管 #define 还是 const 定义)习惯写成大写(比如 PI、MAX_NUM),普通变量名小写
7. 类型选择
整型用 long long、浮点型用 double,新手这么写基本不会出错。
深入
- 整型:题目经常 "挖坑",int 范围不够(大概 ±2×10⁹),用 long long(范围 ±9×10¹⁸)最靠谱,写的时候可以省略 int,直接写
long long a;; - 浮点型:float 精度不够,直接写 double 就好,我做题从没因为用 double 出过错;
- 简化 long long:用 typedef 缩写,比如
typedef long long ll;,之后直接写ll a;就可以。
8. 浮点数转换
整数转浮点型要么加小数点,要么强制类型转换,避免整数除法的坑。
深入
- 整数转浮点型:
- 直接加小数点:
6/4是整数除法(结果 1),6.0/4就是浮点除法(结果 1.5); - 强制转换:两个都是变量时,
(double)a / b(把 a 转成 double,再和 b 相除);
- 直接加小数点:
- ⚠️易错点:强制转换要加括号,比如
(double)(a + b)才是把 a+b 的结果转成浮点型,不是(double)a + b。这个真的错过
9. 取模运算符(%)
取模得余数,结果的正负号由第一个数决定。
深入
- 核心:
a % b= a 除以 b 的余数; - 正负规则:
5 % -2= 1(第一个数 5 是正,结果正);-5 % 2= -1(第一个数 - 5 是负,结果负);5 % 2= 1,-5 % -2= -1。
补充
取模的两个数必须是整型,不能对浮点型用 %(比如3.14%2会报错)。
10. = 和 ==
一个等号是赋值,两个等号是判断相等,if / 循环里千万别写混。
深入
a = 5:把 5 赋值给变量 a,不管 a 原来的值是啥;a == 5:判断 a 的值是不是等于 5,返回 true/false;- 易错点:if 里写
if(a = 5)不会报错,但逻辑错了(永远为真),新手超容易犯这个错。
二、件判断与循环 🔄
1. 悬空 else
else 永远和最近的 if 匹配,写代码加 {} 能避免逻辑混乱。
解决办法:不管代码多短,都给 if/else 加 {},比如:
2. printf 格式化输出
printf 打百分号要写 %%,打指定小数位数用 %.nf,n 是位数。
深入
- 打百分号:
printf("%.2f%%", 3.14);→ 输出 "3.14%"; - 打小数位数 :
printf("%.2f", 3.1415);→ 输出 "3.14"(保留 2 位);printf("%.1f", 2.99);→ 输出 "3.0"(保留 1 位,自动四舍五入)。
3. printf/scanf 占位符规则
- printf 的 % f 适配 float/double,
- scanf 的 % lf 只给 double 用,% f 给 float 用。
4. 绝对值函数
- 整数绝对值用 abs(头文件 cstdlib),
- 浮点型用 fabs(头文件 cmath),
返回值和输入类型一致。牛
深入
- abs:针对 int/long long,比如
abs(-5)=5,abs(5LL)=5LL; - fabs:针对 float/double,比如
fabs(-3.14)=3.14,fabs(2.5)=2.5; - 特点:输入啥类型,返回啥类型,比如给 abs 传 long long,返回也是 long long。
5. 条件操作符(三目运算符)
三目运算符是简化版 if,格式:条件?真执行:假执行
深入
- 语法:
条件 ? 表达式1 : 表达式2; - 逻辑:条件为真,执行表达式 1;条件为假,执行表达式 2;
- 例子:
int max = a > b ? a : b;→ 等价于 if (a>b) max=a; else max=b;
注意
复杂逻辑(比如多句代码)还是用 if 更清晰。
6. switch-case-break-default 语句
switch 的完整结构:
switch (变量) {
case 常量1:
代码1;
break; // 执行完case1就退出
case 常量2:
代码2;
break;
default: // 所有case都不匹配时执行
代码3;
break;
}
- 易错点:漏写 break 会 "穿透",比如 case 1 没 break,执行完 case1 会继续执行 case2;
- default 可选,但建议加,处理意外情况。
三、数组与字符串 📚
1. 数组下标
数组第一个元素下标是 0,第 N 个元素下标是 N-1!
⚠️ 数组越界是新手高频错误,编译时可能不报错,但运行时会出问题。
2. 范围 for
范围 for 能遍历数组,但不能改元素内容
深入
- 语法:
for (类型 变量名 : 数组名) { 代码 }; - 原理:创建一个临时变量,把数组元素逐个赋值给它
- 易错点:冒号(:)容易漏写,漏了直接报错。
补充
想改数组元素,要加引用:for (int &e : arr),此时 e 是数组元素的别名,改 e 就是改数组。
3. auto
auto 让系统自动推导变量类型,用在范围 for 里超方便。
深入
-
核心:auto 不用手动写类型,系统根据赋值自动判断,比如:
auto a = 5; // 推导为int auto b = 3.14; // 推导为double auto c = 'a'; // 推导为char -
常用场景:范围 for 里简化代码,比如
for (auto e : arr),不用管 arr 是 int 还是 double 数组; -
好处:懒得想变量类型时用 auto,省时间还不容易错。
4. memset
memset 按字节设值,头文件 cstring,只适合把数组设为 0。
深入
- 语法:
memset(数组名, 要设的值, 总字节数); - 原理:按字节赋值,int 占 4 字节,设为 1 的话每个字节是 1,最终值是 0x01010101=16843009,不是 1;
- 正确用法:只设 0,比如
memset(arr, 0, sizeof(arr))(sizeof (arr) 自动算总字节数)。
5. memcpy
memcpy 按字节拷贝数组,头文件 cstring,++最后一个参数是总字节数++。
深入
-
语法:
memcpy(目标数组, 源数组, 总字节数); -
和 memset 的相似点:第一个参数是要改的数组地址,最后一个是总字节数;
-
易错点:拷贝 int 数组时,总字节数不是元素个数,而是
元素个数×sizeof(int),比如memcpy(arr1, arr2, 10*sizeof(int))(拷贝 10 个 int 元素)。#include <cstring>
int main() {
int arr1[5] = {1,2,3,4,5};
int arr2[5];
memcpy(arr2, arr1, sizeof(arr1)); // 把arr1拷贝到arr2
for (auto e : arr2) {
cout << e << " "; // 输出1 2 3 4 5
}
return 0;
}
6. 数组创建技巧
- 防越界:要存 n 个元素,就创建
n+10个元素的数组,靠谱; - 全局数组:在 main 函数外创建数组,内存空间更大,不会因为数组太大崩溃
7. 四舍五入
round 是银行家取整,新手用 "+0.5 强制转 int" 更靠谱。
- round 函数(头文件 cmath ):银行家舍入(四舍六入,0.5 取偶数),比如
round(3.5)=4,round(2.5)=2; - 新手方法:
(int)(num + 0.5),比如(int)(3.5+0.5)=4,(int)(2.4+0.5)=2,逻辑简单还不容易错。
8. 比较改变前后:创建两个数组
想把数组排序后和原数组对比:
- 错:只创建一个数组,排序后原数据没了,没法对比;
- 对:创建 arr(原数组)和 arr_copy(拷贝数组),排序 arr_copy,用 arr 和 arr_copy 对比。
9. cin/cout 读写字符数组
cin/cout 只能直接读写字符数组,不能直接读写整型 / 浮点型数组
10. 输入带空格的字符串
cin/scanf 读不了带空格的字符串,用 fgets / 升级版 scanf/getchar
深入
| 方法 | 语法 | 特点 |
|---|---|---|
| fgets | fgets(arr, sizeof(arr), stdin); |
头文件 cstdio,保留 \n,自动加 \0,安全 |
| 升级版 scanf | scanf("%[^\n]s", arr); |
读至回车,自动加 \0,不能读空格开头 |
| getchar | while ((ch=getchar())!='\n') {arr[i++]=ch;} |
边读边处理,灵活,需手动初始化数组为 0 |
代码示例(getchar 边读边统计)
cpp
#include <cstdio>
int main() {
char str[1100] = {0}; // 初始化全0
int nums[26] = {0}; // 统计小写字母次数
int i = 0;
char ch;
while ((ch = getchar()) != '\n') { // 读至回车
nums[ch - 'a']++; // 统计字母次数
str[i++] = ch;
}
return 0;
}
11. strcpy 与 strcat
**只针对字符串:**strcpy 拷贝字符串、strcat 追加字符串,都只要两个参数,头文件 cstring。
深入
- strcpy:
strcpy(目标字符数组, 源字符串),把源字符串拷贝到目标数组,自动加 \0; - strcat:
strcat(目标字符数组, 追加字符串),把追加字符串加到目标数组末尾; - 和 memcpy 的区别:
- strcpy/strcat 只针对字符串,不用传字节数;
- memcpy 针对任意数组,要****传字节数。
12. 字符串打印
% c 按 ASCII 打印、% s 打印双引号内容,小写减空格(32)等于大写。
深入
核心规则:
- % c:把数字 / 字符按 ASCII 码解析,比如
%c打印 97→'a',打印 'a'-32→'A'; - % s:只能打印字符串(双引号 / 字符数组),不能打印数字(比如
printf("%s",97)报错); - 大小写转换 :小写字母 ASCII 码 - 32(空格的 ASCII 码)= 大写字母。
代码示例
cpp
printf("%c", 97);//%c会把数字识别为ASCII码值,找到对应的字符
printf("%c", 'a');//单括号字符也是ASCII码值
//printf("%c", a);//报错:a相当于是个变量
//cout << a;//报错:a相当于是个变量
cout << 'a';//字符
//cout << 'a' - 32;得到的是97-32=65,是个数字
cout << 'a' - ' ';//同上
//空格的码值是32
printf("%c", 'a' - ' ');//得到的是A
printf("\n");
//printf("%s", 97);//错误
printf("%s", "assss aa");//%s能打印双引号中的所有内容
13. islower 与 tolower
islower 判断小写字母、tolower 转小写,头文件 cctype。
深入
- islower:
islower(ch),ch 是小写字母返回非 0,否则返回 0; - tolower:
tolower(ch),把大写字母转小写,小写 / 非字母不变; - ++对应函数:isupper(判断大写)、toupper(转大写)++。
14. 循环条件易错点(\0 vs \n)
- fgets 读的字符串用!= '\n',
- scanf%[^\n] s 用!= '\0',
- getchar 要初始化数组为 0,用!= '\0'。
深入
不同输入方式的字符串结束符:
- fgets:读入的字符串末尾是
\n+\0,循环遍历用arr[i] != '\n'; - scanf ("%[^\n] s"):末尾只有
\0,循环遍历用arr[i] != '\0'; - getchar:手动读入,需先把数组初始化为 0,循环遍历用
arr[i] != '\0'。
⚠️ 忘记区分结束符会导致循环多执行 / 少执行,比如 fgets 读的字符串用!= '\0' 会把\n也当成有效字符。