C++ Primer Plus 学习笔记 - 第2章 开始学习 C++
本章概览
Why This Matters: 第 2 章是从理论到实践的桥梁。理解程序的基本结构、如何与用户交互(I/O)、如何组织代码(函数),是编写任何 C++ 程序的前提。
前置知识: 学习本章前,建议先掌握第 1 章的 C++ 发展历史和编程范式基础。
核心目标:
- 理解 C++ 程序的基本构成(预处理器、main 函数、语句)
- 掌握输入输出系统(
cout/cin) - 学会声明和使用函数
C++ 程序结构:
├── 预处理器指令 (#include)
│ └── 引入标准库头文件
│
├── main() 函数
│ ├── 程序执行入口
│ ├── 返回值类型必须是 int
│ └── 函数体 { ... }
│
├── 语句 (Statements)
│ ├── 变量声明与赋值
│ ├── 输入输出 (cin/cout)
│ └── 函数调用
│
└── 自定义函数
├── 函数原型 (Prototype)
└── 函数定义 (Definition)
一、基础知识讲解 (Core Concepts)
1.1 第一个 C++ 程序
// 完整示例 - 可直接编译运行
#include <iostream> // 预处理器指令:引入 I/O 库
int main() // 主函数:程序执行入口
{
using namespace std; // 使用标准名称空间
cout << "Hello World!"; // 输出语句
cout << endl; // 换行
return 0; // 返回 0 表示程序正常结束
}
程序执行流程:
- 预处理器将
iostream的内容插入程序 - 编译器编译代码
- 操作系统调用
main()函数 - 执行函数体内的语句
return 0结束程序
1.2 main() 函数
main() 是程序的唯一入口,操作系统通过调用它来启动程序。
int main()
{
// 函数体
return 0; // 返回 0 表示成功
}
关键点:
- 返回值类型必须是
int - 如果不写
return 0;,编译器会自动补上(仅限main函数) - 返回值传递给操作系统(0 = 成功,非 0 = 错误)
1.3 预处理器指令 (#include)
#include 告诉预处理器将指定文件的内容插入到当前位置。
#include <iostream> // 系统头文件,用尖括号
#include "myheader.h" // 自定义头文件,用双引号
常用标准库头文件:
| 头文件 | 功能 |
|---|---|
<iostream> |
输入输出流(cin/cout) |
<string> |
字符串类 |
<vector> |
动态数组 |
<cmath> |
数学函数(sqrt/pow 等) |
1.4 名称空间 (Namespace)
名称空间用于避免大型项目中的名称冲突。
// 方式 1:使用整个 std 名称空间(简单但不推荐用于大型项目)
using namespace std;
cout << "Hello";
// 方式 2:使用特定名称(推荐)
using std::cout;
using std::endl;
cout << "Hello" << endl;
// 方式 3:每次都加前缀(最安全)
std::cout << "Hello" << std::endl;
最佳实践:在小型学习项目中可以用
using namespace std;,但在大型项目中应避免,以防止名称冲突。
1.5 输出:cout
cout(console output)用于向屏幕输出内容。
#include <iostream>
using namespace std;
int main() {
cout << "我有 ";
cout << 25;
cout << " 根胡萝卜。";
cout << endl; // 换行
// 可以链式调用
cout << "我有 " << 25 << " 根胡萝卜。" << endl;
return 0;
}
输出:
我有 25 根胡萝卜。
endl vs \n:
endl:换行 + 刷新缓冲区\n:仅换行(更高效)
1.6 输入:cin
cin(console input)用于从键盘读取输入。
#include <iostream>
using namespace std;
int main() {
int carrots;
cout << "你有多少根胡萝卜?";
cin >> carrots; // 读取用户输入
cout << "你有 " << carrots << " 根胡萝卜。" << endl;
return 0;
}
运行示例:
你有多少根胡萝卜?25
你有 25 根胡萝卜。
1.7 变量声明与赋值
C++ 是强类型语言,使用变量前必须声明类型。
int carrots; // 声明
carrots = 25; // 赋值
int apples = 10; // 声明 + 初始化(推荐)
1.8 函数的使用
使用标准库函数
#include <cmath>
#include <iostream>
using namespace std;
int main() {
double x = sqrt(6.25); // 调用 sqrt 函数
cout << "6.25 的平方根是 " << x << endl; // 2.5
return 0;
}
自定义函数
函数包含三个部分:原型、调用、定义。
#include <iostream>
using namespace std;
// 1. 函数原型(声明)
void Simon(int);
int main() {
// 2. 函数调用
Simon(3);
return 0;
}
// 3. 函数定义
void Simon(int n) {
cout << "Simon says touch your toes " << n << " times." << endl;
}
输出:
Simon says touch your toes 3 times.
为什么需要函数原型?
- 告诉编译器函数的参数类型和返回值类型
- 允许在定义之前调用函数
- 提高代码可读性
二、进阶应用 (Modern C++ Practice)
2.1 用 auto 简化类型声明 (C++11)
// 传统写法
double x = sqrt(6.25);
// 现代写法
auto x = sqrt(6.25); // 编译器自动推导为 double
2.2 用范围 for 循环输出 (C++11)
#include <iostream>
#include <vector>
using namespace std;
int main() {
vector<int> nums = {1, 2, 3, 4, 5};
// 传统写法
for (int i = 0; i < nums.size(); i++) {
cout << nums[i] << " ";
}
// 现代写法(更简洁)
for (auto num : nums) {
cout << num << " ";
}
return 0;
}
2.3 用 nullptr 代替 NULL (C++11)
// 传统写法
int *p = NULL;
// 现代写法
int *p = nullptr; // 类型安全的空指针
2.4 用 constexpr 定义编译期常量 (C++11)
// 传统 const
const int SIZE = 100;
// 现代 constexpr(保证编译期计算)
constexpr int SIZE = 100;
constexpr double PI = 3.14159;
三、工程陷阱与避坑指南 (Engineering Pitfalls)
3.1 忘记 using namespace std 或 std::
现象:编译错误 'cout' was not declared in this scope。
原因:cout 和 cin 在 std 名称空间中,必须显式指定。
#include <iostream>
int main() {
cout << "Hello"; // ❌ 编译错误!
return 0;
}
解决方案:
// 方案 1
using namespace std;
cout << "Hello";
// 方案 2
std::cout << "Hello";
3.2 忘记 #include <iostream>
现象:编译错误 'cout' was not declared。
原因:cout 和 cin 定义在 iostream 头文件中。
int main() {
std::cout << "Hello"; // ❌ 编译错误!
return 0;
}
解决方案:
#include <iostream> // ✅ 必须包含
3.3 cin 输入类型不匹配
现象:程序行为异常,输入被跳过。
原因:cin 期望读取整数,但用户输入了字符串。
int age;
cout << "请输入年龄:";
cin >> age; // 用户输入 "abc",cin 失败
解决方案:检查输入状态。
if (cin >> age) {
cout << "年龄:" << age << endl;
} else {
cout << "输入无效!" << endl;
cin.clear(); // 清除错误标志
cin.ignore(1000, '\n'); // 清空缓冲区
}
3.4 函数原型与定义不匹配
现象:链接错误或编译错误。 原因:函数原型的参数类型与定义不一致。
void foo(int); // 原型
void foo(double x) { // ❌ 定义参数类型不匹配
// ...
}
解决方案:确保原型和定义完全一致。
void foo(int); // 原型
void foo(int x) { // ✅ 匹配
// ...
}
四、面试高频考点 (Interview Focus)
Q1: main() 函数的返回值有什么作用?
答:main() 的返回值传递给操作系统,表示程序的退出状态:
return 0;:程序正常结束return 非0;:程序异常结束(错误码)
在 Shell 脚本中可以通过 $?(Linux)或 %ERRORLEVEL%(Windows)获取这个返回值。
Q2: using namespace std; 有什么潜在问题?
答:在大型项目中,using namespace std; 可能导致名称冲突。例如,如果你定义了一个名为 count 的变量,它可能与 std::count 冲突。
最佳实践:
- 小型学习项目:可以使用
using namespace std; - 大型项目:使用
std::前缀或using std::cout;等特定声明
Q3: endl 和 \n 有什么区别?
答:
endl:换行 + 刷新缓冲区(强制立即输出)\n:仅换行(不刷新缓冲区,更高效)
在性能敏感的场景(如大量输出),优先使用 \n。
五、总结与回顾 (Summary & Review)
核心记忆点
- 程序结构:
#include→main()→{ 语句 } - I/O 系统:
cout <<输出,cin >>输入 - 名称空间:
using namespace std;或std::前缀 - 变量:先声明类型,再赋值
- 函数:原型(声明)→ 调用 → 定义
- main() 返回值:0 = 成功,非 0 = 错误
C++ 程序基本框架
#include <iostream>
using namespace std;
int main() {
// 变量声明
int x = 10;
// 输出
cout << "x = " << x << endl;
// 输入
cin >> x;
// 函数调用
// ...
return 0;
}
下一章预告:第 3 章将深入探讨 C++ 的数据类型系统——整型、浮点型、字符型、布尔型,以及类型转换的规则!