从事 C/C 编程快九个年头了,今天女朋友突然问我:什么时候用 C 而不用 C ?一下子把我的思绪拉入往昔那些使用 C 和 C 的“峥嵘岁月”里。女朋友还是原来的女朋友,而我却不是当初的那个我了。
老实说,这些年用 C 和 C 分别写过很多项目,也研究像 Linux 内核、Nginx、Redis、freeswitch 等各种经典 C 项目,当然研究的 C 项目也不少(例如 MySQL 的源码),玩的不亦乐乎,常看常新,看到这些经典项目的精彩代码处时有时候喜不自禁,那真是笑看妻子愁何在,漫卷诗书喜欲狂。
贴几张图:
调试分析 C 项目 Redis 源码
调试分析 C 项目 Nginx 源码
调试分析 C 项目 MySQL Server 8.0
如果一个项目如果对内存和编译后的机器指令做精准控制,那么应该优先选择 C 而不是 C ,C 编译器在背后插入了太多的指令。举两个例子:
代码语言:javascript复制class A {
public:
A() {}
~A() {}
};
int main()
{
A a;
return 0;
}
这个例子说明了在 C 中一个对象出了其作用域(这里是 a),会自动调用其析构函数,这是编译器自动加入的:
代码语言:javascript复制 A a;
00007FF71E2918DD lea rcx,[a]
00007FF71E2918E1 call A::A (07FF71E291302h)
return 0;
00007FF71E2918E6 mov dword ptr [rbp 0E4h],0
00007FF71E2918F0 lea rcx,[a]
00007FF71E2918F4 call A::~A (07FF71E291258h) ;编译在这里自动插入调用类A的析构函数
00007FF71E2918F9 mov eax,dword ptr [rbp 0E4h]
}
再比如假设 A 类有一个实例方法 f,编译器的调用 A 方法的实现是编译器自动插入了一个 this 指针作为方法的第一个参数:
代码语言:javascript复制class A {
public:
A() {}
~A() {}
void f(int a, int b) {}
};
int main()
{
A a;
a.f(1, 2);
return 0;
}
汇编代码如下:
代码语言:javascript复制 A a;
00007FF7157D193D lea rcx,[a]
00007FF7157D1941 call A::A (07FF7157D1311h)
00007FF7157D1946 nop
a.f(1, 2); ;由于函数调用方式是__thiscall,
00007FF7157D1947 mov r8d,2 ;所以参数从右往左入栈,这是第一个参数2
00007FF7157D194D mov edx,1 ;这是第一个参数的1
00007FF7157D1952 lea rcx,[a];这是对象a的地址,即this指针
00007FF7157D1956 call A::f (07FF7157D110Eh) ;调用a的方法f
return 0;
00007FF7157D195B mov dword ptr [rbp 0E4h],0
00007FF7157D1965 lea rcx,[a]
00007FF7157D1969 call A::~A (07FF7157D1267h)
00007FF7157D196E mov eax,dword ptr [rbp 0E4h]
同样的道理,如果如果一个复杂的 C 工程中,一个类有多个复杂类型成员,而这些复杂类型成员又有多个复杂类型成员,这种层级关系比较深的情况下,上层对象的构造函数会自动触发其成员变量的构造函数的调用,而进而又触发下一级成员变量的构造函数的调用,而析构函数正好反过来,这些构造动作都是编译器自己往开发者的程序中插入的指令。如果要涉及到模板或者像智能指针包裹的对象的构造、析构和移动,那编译器自动嵌入的指令就更多。
如果你不明白我在说什么,或者想对 C 编译器到底对我们的 C 代码做了哪些事情有一个系统的了解,推荐阅读《深度探索 C 对象模型》一书。
以上,在 C 编译器中不会有的,C 编译器一般不会偷偷插入开发者不知道的指令,你写了什么代码,对应的机器指令和你预期的所差无几。这就是为什么像内核以及一些对性能和内存利用率要求比较高的项目需要使用 C 而不是 C 。
有同学可能会说,用 C 不用 C 的时候,是因为 C 支持类、支持面向对象编程,需要用到面向对象特性的时候由于 C 带类的功能,确实让我们编写面向对象程序方便一些,但不能作为用 C 而不用 C 的主要理由之一。面向对象是一种编程思想,用 C 也可以写出优雅的面向对象程序来。一些同学在在 C 这类没有类语法的语言中觉得束手束脚,尤其是在看像 Nginx、内核这样的 C 代码时。推荐《C 语言接口与实现 创建可重用软件的技术》这本书。
看完这本书,可以让你模糊面向对象编程在 C 与 C 上的区别,在看工程级的 C 代码心中也不慌了。
当然,《C 专家编程》这本书也是 C 语言的经典必读书籍,书的作者是曾经的 SUN 编译器小组的成员,真的介绍了很多 C 开发的实际经验,书中花了三章的篇幅详细阐述指针、数组以及它们二者的关系,这是 C 语言的精髓。C 语言成也指针,败也指针。
如果你平常用的 C 比较多,但是觉得 C 写的不地道,对于大型 C 项目不知道如何规划模块,不知道如何设计类关系,接口设计不优雅,推荐你阅读一下《大规模 C 程序设计》和 《C API 设计》这两本书。
最后我想说的是,C 和 C 学起来是很爽,就是头感觉凉飕飕的
。
本文是《女朋友要去 XXX 系列》第二篇,第一篇是《女朋友要去面试 C ,我建议她这么做》。
关注我,更多有趣实用的编程知识~
福利时间
抽奖送三本最近的新书《性能之巅》,我将从评论本文的读者中选择 3 位赠送此书。
这本书的内容是没得说的,书比较厚,原价要 238元,现在做活动,可以半价购买。如果想自己购买的同学可以扫描下面的二维码半价购买(或者点击文末的阅读原文),半价活动截止日期到本周一。
原创不易,点个赞呗