C 与 C++:一对父子的渐行渐远
Posted on Sat 17 January 2026 in Programming
"C makes it easy to shoot yourself in the foot; C++ makes it harder, but when you do it blows your whole leg off." —— Bjarne Stroustrup
开篇:你以为的 C++,可能已经是"古董"了
如果你是 90 后程序员,你学 C++ 的时候,教材上写的可能还是 C++98。
那时候的 C++,确实像是"C with Classes"——多了个 class,多了个 new/delete,多了个虚函数,仅此而已。
但如果你现在还抱着这种认知,看到一段现代 C++ 代码,你可能会怀疑人生:
auto result = std::ranges::views::filter(numbers, [](int n) { return n > 0; })
| std::ranges::views::transform([](int n) { return n * 2; });
这是 C++ 吗?怎么看起来像 Python 和 Rust 的私生子?
没错,这就是现代 C++(C++20)。
今天,我们就来聊聊: 1. C 和 C++ 到底有哪些本质区别? 2. 从 C++11 到 C++23,这门语言经历了怎样的"文艺复兴"? 3. 作为开发者,我们该怎么跟上这趟"现代化列车"?
一、C 和 C++:设计哲学的分道扬镳
1.1 C:信任程序员,给你一把手术刀
C 语言的设计哲学是:程序员知道自己在做什么。
它提供了最底层的控制能力——指针、内存、位操作。它不会阻止你做任何事情,包括把自己炸死。
// C 语言:我相信你
int* p = (int*)malloc(100); // 可能返回 NULL
*p = 42; // 如果 p 是 NULL,炸了
free(p);
*p = 0; // Use after free,炸了
C 的优点是简单、透明、可预测。你写什么,编译器就生成什么,没有隐藏的魔法。
1.2 C++:我帮你管,但你得学我的规矩
C++ 的设计哲学是:零成本抽象(Zero-cost Abstraction)。
它想给你更高级的抽象(类、模板、RAII),但又不想牺牲性能。结果就是——语言变得极其复杂。
// C++ 语言:我帮你管资源
auto p = std::make_unique<int>(42); // 自动管理内存
// 离开作用域自动释放,不用操心
C++ 的优点是更安全、更高效(在正确使用的前提下)。缺点是学习曲线陡峭,坑多。
1.3 核心差异一览
| 特性 | C | C++ |
|---|---|---|
| 编程范式 | 过程式 | 过程式 + 面向对象 + 泛型 + 函数式 |
| 内存管理 | 手动 (malloc/free) |
手动 + RAII + 智能指针 |
| 类型系统 | 弱类型检查 | 强类型检查 + 模板 |
| 异常处理 | 无(靠返回值) | 有 (try/catch/throw) |
| 函数重载 | 不支持 | 支持 |
| 命名空间 | 无 | 有 (namespace) |
| 标准库 | 极简(stdio, stdlib) | 丰富(STL, |
| 编译速度 | 快 | 慢(尤其是模板多的时候) |
二、代码对比:同一个问题,两种写法
场景:读取文件,统计单词频率
C 语言版本:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#define MAX_WORD_LEN 100
#define MAX_WORDS 10000
typedef struct {
char word[MAX_WORD_LEN];
int count;
} WordCount;
WordCount words[MAX_WORDS];
int word_count = 0;
int find_word(const char* word) {
for (int i = 0; i < word_count; i++) {
if (strcmp(words[i].word, word) == 0) return i;
}
return -1;
}
void add_word(const char* word) {
int idx = find_word(word);
if (idx >= 0) {
words[idx].count++;
} else if (word_count < MAX_WORDS) {
strcpy(words[word_count].word, word);
words[word_count].count = 1;
word_count++;
}
}
int main() {
FILE* f = fopen("input.txt", "r");
if (!f) { perror("fopen"); return 1; }
char word[MAX_WORD_LEN];
while (fscanf(f, "%99s", word) == 1) {
// 转小写
for (char* p = word; *p; p++) *p = tolower(*p);
add_word(word);
}
fclose(f);
for (int i = 0; i < word_count; i++) {
printf("%s: %d\n", words[i].word, words[i].count);
}
return 0;
}
现代 C++ 版本(C++17):
#include <iostream>
#include <fstream>
#include <map>
#include <string>
#include <algorithm>
#include <cctype>
int main() {
std::ifstream file("input.txt");
if (!file) { std::cerr << "Cannot open file\n"; return 1; }
std::map<std::string, int> word_count;
std::string word;
while (file >> word) {
// 转小写
std::transform(word.begin(), word.end(), word.begin(),
[](unsigned char c) { return std::tolower(c); });
word_count[word]++;
}
// file 自动关闭(RAII)
for (const auto& [w, count] : word_count) { // C++17 结构化绑定
std::cout << w << ": " << count << '\n';
}
}
差异对比:
| 方面 | C | C++ |
|---|---|---|
| 代码行数 | ~50 行 | ~25 行 |
| 内存管理 | 手动定义数组大小 | std::map 自动扩容 |
| 资源释放 | 手动 fclose |
RAII 自动释放 |
| 查找复杂度 | O(n) 线性查找 | O(log n) 红黑树 |
| 可读性 | 需要理解底层实现 | 意图更清晰 |
三、现代 C++ 的"文艺复兴"(C++11 到 C++23)
从 C++11 开始,C++ 标准委员会终于开窍了——他们不再只顾着加复杂的元编程特性,而是开始关注易用性和安全性。
3.1 C++11:革命的起点
这是现代 C++ 的元年。如果你只能学一个版本,学 C++11。
| 特性 | 说明 | 示例 |
|---|---|---|
| auto | 类型推导 | auto x = 42; |
| 智能指针 | 自动内存管理 | std::unique_ptr, std::shared_ptr |
| Lambda | 匿名函数 | [](int x) { return x * 2; } |
| nullptr | 类型安全的空指针 | 替代 NULL |
| 范围 for | 简化遍历 | for (auto& item : container) |
| 移动语义 | 避免不必要的拷贝 | std::move() |
| 右值引用 | 支持移动语义 | T&& |
| constexpr | 编译期计算 | constexpr int fib(int n) {...} |
// C++11 之前
std::vector<int>::iterator it = vec.begin();
for (; it != vec.end(); ++it) { ... }
// C++11 之后
for (auto& item : vec) { ... }
3.2 C++14/17:小步快跑
| 版本 | 关键特性 |
|---|---|
| C++14 | 泛型 Lambda、auto 返回类型推导、std::make_unique |
| C++17 | if constexpr、结构化绑定、std::optional、std::variant、std::filesystem |
// C++17 结构化绑定
std::map<std::string, int> scores = {{"Alice", 90}, {"Bob", 85}};
for (const auto& [name, score] : scores) {
std::cout << name << ": " << score << '\n';
}
// C++17 std::optional
std::optional<int> find_value(int key) {
if (key == 42) return 100;
return std::nullopt; // 明确表示"没有值",比返回 -1 安全
}
3.3 C++20:质的飞跃
C++20 是自 C++11 以来最大的一次更新。它引入了四大特性:
- Concepts(概念):给模板加约束,编译错误终于能看懂了
- Ranges(范围):函数式风格的数据处理
- Coroutines(协程):原生异步支持
- Modules(模块):替代
#include,加速编译
// Concepts:让模板错误信息变得人类可读
template<typename T>
concept Addable = requires(T a, T b) { a + b; };
template<Addable T>
T add(T a, T b) { return a + b; }
// Ranges:管道式数据处理
#include <ranges>
auto result = numbers
| std::views::filter([](int n) { return n > 0; })
| std::views::transform([](int n) { return n * 2; });
// Coroutines:异步生成器
generator<int> fibonacci() {
int a = 0, b = 1;
while (true) {
co_yield a;
auto tmp = a;
a = b;
b = tmp + b;
}
}
3.4 C++23:持续打磨
C++23 没有 C++20 那么"革命",但也有不少实用改进:
| 特性 | 说明 |
|---|---|
std::expected |
比 std::optional 更好的错误处理,类似 Rust 的 Result |
std::print / std::println |
终于有像样的格式化输出了(类似 Python 的 print) |
std::mdspan |
多维数组视图 |
import std; |
标准库模块化 |
if consteval |
区分编译期和运行期执行 |
// C++23: std::expected 错误处理
std::expected<int, std::string> parse_int(std::string_view s) {
if (s.empty()) return std::unexpected("empty string");
// ...
return 42;
}
// C++23: std::print
std::print("Hello, {}!\n", "World"); // 终于不用 cout << 了
四、C++26 及未来:路在何方?
C++ 标准委员会目前正在推进 C++26,重点方向包括:
- 静态反射 (Static Reflection):在编译期获取类型信息,元编程的终极形态
- Contracts(契约):前置条件、后置条件、断言的标准化
- Pattern Matching(模式匹配):类似 Rust/Haskell 的
match语法 - Executors(执行器):标准化的异步执行框架
// 可能的 C++26 模式匹配语法(提案中)
inspect (value) {
0 => { return "zero"; }
1 => { return "one"; }
x if (x < 0) => { return "negative"; }
_ => { return "other"; }
};
五、选 C 还是 C++?
这个问题没有标准答案,取决于你的场景:
| 场景 | 推荐 | 理由 |
|---|---|---|
| 嵌入式 / 裸机开发 | C | 资源受限,需要完全控制 |
| 操作系统内核 | C | Linux、Windows 内核都是 C |
| 高性能服务器 | C++ | 需要抽象但不能牺牲性能 |
| 游戏引擎 | C++ | Unreal Engine、Unity 核心都是 C++ |
| 量化交易 | C++ | 低延迟 + 复杂业务逻辑 |
| 简单脚本 / 工具 | 都不推荐 | 用 Python/Go 吧 |
我的建议: - 如果你是 C 程序员,想提升抽象能力 → 学现代 C++ - 如果你是 Java/Python 程序员,想学底层 → 先学 C,再学 C++ - 如果你只想快速出活 → Rust 或 Go 可能是更好的选择
总结
| 概念 | 说明 |
|---|---|
| C | 简单、透明、危险的"手术刀" |
| C++ | 强大、复杂、需要正确使用的"瑞士军刀" |
| C++11 | 现代 C++ 的起点,必学 |
| C++17 | 实用特性最多的版本 |
| C++20 | 革命性更新:Concepts, Ranges, Coroutines, Modules |
| C++23 | 持续打磨:std::expected, std::print |
Checklist:升级到现代 C++ 的第一步
- [ ] 把
new/delete换成std::make_unique/std::make_shared - [ ] 用
auto减少冗长的类型声明 - [ ] 用范围 for 替代迭代器 for
- [ ] 用 Lambda 替代函数指针
- [ ] 用
std::optional替代"魔法值"(如-1表示无效) - [ ] 打开编译器的 C++17/20 标准:
-std=c++20 - [ ] 试着用 Ranges 重写一段数据处理代码
扩展阅读
- C++ Reference — 最权威的 C++ 文档
- Bjarne Stroustrup: C++ 设计与演化
- C++ Core Guidelines — 现代 C++ 最佳实践
- Compiler Explorer (Godbolt) — 在线查看 C/C++ 汇编输出
本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议进行许可。