C++ 程序设计入门到实用:语法、函数、数组、算法、类与指针
这篇笔记根据 AMA2222 Principles of Programming 的课件整理,目标不是把 C++ 语法逐条背下来,而是建立一条能真正写程序的路线:先理解输入、输出和变量,再用条件与循环控制程序流程,然后用函数拆分问题,用数组和字符串保存数据,用搜索、排序、递归训练算法思维,最后进入类、继承、运算符重载、指针和动态内存。
如果你是从 Python 转到 C++,最重要的差异是:C++ 更强调类型、内存、编译和程序结构。Python 里很多事情解释器帮你处理了,C++ 会要求你明确地告诉电脑变量是什么类型、数组有多大、函数返回什么、对象如何初始化,以及什么时候申请或释放内存。
参考资料下载
原始课件和复习资料我也保留在博客里,方便对照阅读:
- AMA2222 全部资料压缩包
- Chapter 1: 输入输出、变量、算术与文件 I/O
- Chapter 2: 条件判断与逻辑表达式
- Chapter 3: 循环控制
- Chapter 4: 函数、作用域与 lambda
- Chapter 5: 字符与字符串
- Chapter 6: 数组与二维数组
- Chapter 7: 搜索、排序与基础算法
- Chapter 8: 递归、分治与贪心
- Chapter 8b: 递归与分治补充
- Chapter 9: 面向对象程序设计
- Chapter 10: 指针与动态内存
- Chapter 11: 模拟、数值方法与应用
- Quiz 2 review + extra questions
1. 一段 C++ 程序到底由什么组成
最小 C++ 程序通常长这样:
1 |
|
这里每一行都有意义:
#include <iostream>:引入输入输出库,否则不能使用cin和cout。using namespace std;:让你可以直接写cout,不用写std::cout。int main():程序入口。运行程序时,电脑从main开始执行。{ ... }:代码块。C++ 用花括号表示一组语句的范围。cout << ...:输出到屏幕。return 0;:告诉系统程序正常结束。
C++ 的语句通常以分号结尾。初学时很多报错都来自少写分号、括号不配对、变量没有声明、类型不匹配。
2. 输入、输出、变量与类型
程序可以用 IPO 模型理解:
1 | Input -> Process -> Output |
例如输入两个整数,判断第一个是否能被第二个整除:
1 |
|
这里有几个关键点:
int m, n;是变量声明。C++ 需要先声明变量,再使用变量。cin >> m >> n;会从键盘读取两个值。%是取余数。m % n == 0表示整除。- 判断除法前先检查
n != 0,否则可能出现除以 0 的错误。
常用基础类型:
| 类型 | 用途 | 示例 |
|---|---|---|
int |
整数 | int age = 20; |
double |
小数 | double gpa = 3.7; |
char |
单个字符 | char grade = 'A'; |
bool |
真/假 | bool passed = true; |
string |
字符串 | string name = "Richard"; |
注意 char 和 string 不一样。'A' 是单个字符,用单引号;"A" 是字符串,用双引号。
3. 条件判断:让程序做选择
条件判断是程序从“顺序执行”走向“有逻辑”的第一步。
1 | if (condition) { |
一个 BMI 分类例子:
1 |
|
条件判断里最常用的运算符:
| 运算符 | 含义 |
|---|---|
== |
等于 |
!= |
不等于 |
<, <=, >, >= |
大小比较 |
&& |
并且 |
| ` | |
! |
取反 |
初学最容易犯的错是把 == 写成 =:
1 | if (x = 5) { // 错误倾向:这是赋值,不是判断相等 |
另一个建议是:即使 if 后面只有一行,也尽量写花括号。这样以后加代码时更不容易出错。
4. 循环:重复执行一段逻辑
循环用于处理“重复做某件事”的场景,比如求和、遍历数组、生成序列、模拟实验。
for 循环
for 适合循环次数已知的场景:
1 | int sum = 0; |
for 的结构是:
1 | for (初始化; 循环条件; 每轮更新) { |
while 循环
while 适合循环次数不确定的场景:
1 | int n; |
这个程序每次把数字去掉最后一位,直到数字变成 0。它本质上是在数整数有多少位。
循环常见坑
- 忘记更新循环变量,导致无限循环。
- 数组下标写到边界外,例如访问
a[n],但合法下标只有0到n - 1。 for (int i = 0; i <= n; i++)和for (int i = 0; i < n; i++)差一个元素,要非常小心。
5. 函数:把问题拆成小块
函数让程序更清晰。你可以把一个大问题拆成多个小问题,每个函数负责一件事。
1 | double getArea(double width, double height) { |
函数一般包含:
1 | 返回类型 函数名(参数列表) { |
例如:
1 | bool isEven(int x) { |
这个函数表达力很强。调用处写:
1 | if (isEven(n)) { |
比直接写 % 2 更容易读。
局部变量与全局变量
函数里面定义的变量叫局部变量,只在函数内部有效:
1 | void printSquare(int x) { |
y 只能在 printSquare 内使用。初学阶段建议少用全局变量,因为全局变量会让程序状态变得难追踪。
值传递与引用传递
普通参数默认是值传递,函数拿到的是一份副本:
1 | void addOne(int x) { |
调用 addOne(a) 不会改变 a 本身。如果想改变原变量,可以用引用:
1 | void addOne(int& x) { |
int& x 表示 x 是外部变量的别名。
6. 字符与字符串:文本处理的基础
char 表示单个字符,背后有 ASCII 编码。比如:
1 | char letter = 'A'; |
这解释了为什么字符可以做加减法:C++ 会把字符转成对应的 ASCII 数字。
字符串用 string:
1 |
|
如果要读取一整行,使用 getline(cin, name)。如果用 cin >> name,遇到空格就会停止。
常见字符串操作:
1 | string s = "programming"; |
一个统计字符串中字母数量的例子:
1 | int countLetterA(string s) { |
这段代码用到了 range-based for。它的意思是:依次取出字符串里的每个字符。
7. 数组:保存一批同类型数据
数组用于保存多个同类型值:
1 | int scores[5] = {80, 75, 90, 88, 92}; |
数组下标从 0 开始:
1 | cout << scores[0] << endl; // 第一个元素 |
求数组平均值:
1 | double average(int a[], int size) { |
注意函数参数里传数组时,通常也要传数组长度。因为函数内部不知道数组到底有多少个元素。
数组传入函数时会被修改
数组传给函数时,本质上传的是起始地址,所以函数里改数组,外面的数组也会变:
1 | void doubleAll(int a[], int size) { |
这个和普通 int 的值传递不同,是 C++ 初学者非常容易混淆的地方。
反转数组
1 | void reverseArray(int a[], int size) { |
这个例子很重要,因为它训练了两个能力:
- 用下标控制位置。
- 用临时变量交换两个元素。
8. 二维数组:矩阵与表格数据
二维数组适合表示矩阵、棋盘、表格:
1 | int matrix[2][3] = { |
遍历二维数组:
1 | for (int i = 0; i < 2; i++) { |
如果把二维数组传入函数,列数通常要写清楚:
1 | void printMatrix(int a[][3], int rows) { |
这是因为 C++ 需要知道每一行有多少列,才能正确计算 a[i][j] 的内存位置。
9. 搜索与排序:从语法进入算法
学 C++ 不能只学语法,还要开始训练算法思维。最基础的算法包括搜索和排序。
线性搜索
线性搜索就是从头到尾找:
1 | int linearSearch(int a[], int size, int target) { |
如果找到,返回下标;如果找不到,返回 -1。
二分搜索
二分搜索要求数组已经排好序:
1 | int binarySearch(int a[], int size, int target) { |
二分搜索的核心思想是每次排除一半候选区域。它比线性搜索快很多,但前提是数据有序。
冒泡排序
冒泡排序不断比较相邻元素,把较大的元素往后推:
1 | void bubbleSort(int a[], int size) { |
初学时不要只背排序代码,要能说出每层循环在做什么:
- 外层循环表示已经完成了多少轮。
- 内层循环负责比较相邻元素。
- 每一轮结束后,当前未排序部分最大的元素会到达正确位置。
10. 递归、分治与贪心
递归是函数调用自己。递归一定要有两个部分:
- base case:最小问题,直接返回。
- recursive case:把问题变小,然后调用自己。
例如计算数字位数:
1 | int countDigits(int n) { |
如果输入 345678:
1 | countDigits(345678) |
再看一个递归数组求和:
1 | int sumArray(int a[], int size) { |
分治思想是:
1 | divide -> solve subproblems -> combine |
很多经典算法都可以这样理解,比如归并排序、快速排序、二分搜索。
贪心算法则是每一步都做当前看来最好的选择。贪心不是所有问题都正确,但在零钱兑换、区间选择、最小生成树等问题里很重要。初学时要记住:贪心需要证明,不能只凭直觉。
11. 类与对象:C++ 的面向对象核心
类是对象的模板。对象有状态和行为:
- 状态:数据字段,比如矩形的
width和height。 - 行为:成员函数,比如计算面积。
一个矩形类:
1 | class Rectangle { |
使用类:
1 | int main() { |
这里的重点是封装。课件里一开始会把数据字段写在 public,方便理解;但真实项目中更推荐用 private 保护数据,再通过成员函数访问。
构造函数
构造函数用于初始化对象。它有三个特点:
- 名字必须和类名一样。
- 没有返回类型,连
void都不写。 - 创建对象时自动调用。
1 | Rectangle r1; // 调用无参数构造函数 |
setter 与 getter
如果要控制数据合法性,可以写 setter:
1 | void setWidth(double w) { |
这比直接让外部修改 width 更安全。
12. 运算符重载:让对象像数字一样运算
假设我们定义一个分数类:
1 | class Fraction { |
现在可以这样写:
1 | Fraction r1(3, 4); |
operator* 的意思是:当两个 Fraction 对象使用 * 时,执行我们自定义的乘法逻辑。
运算符重载不是为了炫技,而是为了让对象的使用方式符合直觉。比如分数加法、复数乘法、向量加法,都适合用运算符重载。
13. 继承:复用已有类的属性和行为
继承可以让一个类基于另一个类扩展:
1 | class Person { |
Student 是 Person 的子类,它继承了 Person 的名字字段和函数,又新增了学生编号。
理解继承时要注意:
public继承表示“is-a”关系:Student is a Person。- 子类可以复用父类代码。
- 子类构造函数通常要调用父类构造函数。
- 不要为了省几行代码乱用继承。真实设计里,组合有时比继承更合适。
14. 指针与动态内存:C++ 最需要耐心的部分
指针保存的是地址。
1 | int x = 10; |
几个符号要分清:
| 符号 | 含义 |
|---|---|
&x |
取变量 x 的地址 |
int* p |
声明一个指向 int 的指针 |
*p |
解引用,取出指针指向的值 |
指针和数组
数组名经常可以看成数组首元素地址:
1 | int a[3] = {10, 20, 30}; |
这解释了为什么数组传入函数后会被修改:函数拿到的是数组起始地址。
动态内存
如果程序运行时才知道需要多少空间,可以动态申请:
1 | int n; |
new 申请内存,delete[] 释放数组内存。如果申请了不释放,就可能造成内存泄漏。
现代 C++ 更推荐使用 vector、string、智能指针等工具减少手动内存管理,但学习指针仍然很重要,因为它能帮助你理解数组、对象、引用和底层性能。
15. 文件读写:让程序处理真实数据
控制台输入适合小练习,文件输入适合真实数据。
1 |
|
实际写文件程序时,要检查文件是否成功打开:
1 | if (!fin) { |
文件读写对数据分析和算法题都很有用。以后做项目时,输入可能来自 CSV、日志、文本语料或实验结果,文件处理能力会直接影响工程效率。
16. 随机模拟与数值方法
最后几章会进入模拟和应用。模拟的核心思想是:用程序重复实验,观察统计结果。
一个简单的随机投点估计圆周率思路:
1 |
|
这类方法叫 Monte Carlo simulation。它不一定给出精确答案,但样本足够多时可以得到不错的近似。机器学习、金融、优化和强化学习里都经常能看到类似思想。
17. 从这门课到算法实习的学习路线
如果目标是算法、NLP、搜索推荐或 AI infra,C++ 不只是考试语言,它还训练几种很硬的能力:
- 精确理解类型和内存。
- 用函数拆分复杂逻辑。
- 能写数组、字符串、搜索、排序、递归。
- 理解对象、封装、继承和接口。
- 对性能有初步直觉。
建议按下面顺序复习:
- 先写熟
cin、cout、变量、if、for、while。 - 每天做 2 到 3 道数组或字符串题,重点练下标和边界。
- 把线性搜索、二分搜索、冒泡排序、选择排序、递归求和自己默写出来。
- 用类写 3 个小对象:
Rectangle、Fraction、Student。 - 最后再学指针和动态内存,不要一开始就被它卡住。
18. 一套最小练习清单
下面这些题如果能独立写出来,说明 C++ 入门已经比较稳:
- 输入两个整数,输出较大值。
- 输入三个角度,判断是否能构成三角形,并判断锐角、直角、钝角。
- 输入一个整数,统计它有多少位。
- 输入一个字符串,统计其中大写字母、小写字母、数字的数量。
- 写函数判断一个数是否为质数。
- 用数组保存 10 个分数,输出最大值、最小值、平均值。
- 写函数反转数组。
- 写线性搜索和二分搜索。
- 写冒泡排序或选择排序。
- 用递归计算阶乘、数字位数、数组求和。
- 定义
Rectangle类,支持面积、周长、判断正方形。 - 定义
Fraction类,支持乘法和打印。 - 用
new创建动态数组,读入若干数字并求平均,最后释放内存。 - 从文件读取整数,输出总和到另一个文件。
19. 常见错误速查
编译错误
如果程序编译不过,优先检查:
- 是否漏了分号。
- 变量是否声明过。
- 函数返回类型和
return是否匹配。 - 花括号是否配对。
- 是否忘了
#include <string>、#include <fstream>等头文件。
运行错误
如果程序能编译但运行崩溃,优先检查:
- 数组是否越界。
- 是否除以 0。
- 指针是否没有初始化。
- 动态数组是否释放方式错误。
逻辑错误
如果程序结果不对,优先检查:
- 循环边界是
< n还是<= n。 if条件是否写反。- 整数除法是否导致小数被截断。
- 函数参数是值传递还是引用传递。
- 排序或搜索前提是否满足,例如二分搜索要求数组有序。
20. 最后的理解方式
C++ 入门最好的学习方式不是一直看语法,而是反复写小程序。每个知识点都可以对应一个“可运行”的小练习:
- 条件判断对应分类问题。
- 循环对应累计、统计、模拟。
- 函数对应拆解任务。
- 数组对应批量数据。
- 字符串对应文本处理。
- 搜索排序对应算法基础。
- 类对应现实对象建模。
- 指针对应内存和底层机制。
把这些串起来,C++ 就不再是一堆孤立语法,而是一套控制计算机完成任务的工具。对后续学习数据结构、算法、系统、推理优化和高性能工程来说,这套基础非常值得认真打牢。
C++ 程序设计入门到实用:语法、函数、数组、算法、类与指针
https://richardf123.github.io/2026/06/24/ama2222-cpp-programming-guide/