前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >女朋友问我:什么时候用 C 而不用 C++?

女朋友问我:什么时候用 C 而不用 C++?

作者头像
范蠡
发布2022-08-26 12:51:31
3740
发布2022-08-26 12:51:31
举报

从事 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元,现在做活动,可以半价购买。如果想自己购买的同学可以扫描下面的二维码半价购买(或者点击文末的阅读原文),半价活动截止日期到本周一。

原创不易,点个赞呗

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2022-07-31,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 高性能服务器开发 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
云数据库 Redis
腾讯云数据库 Redis(TencentDB for Redis)是腾讯云打造的兼容 Redis 协议的缓存和存储服务。丰富的数据结构能帮助您完成不同类型的业务场景开发。支持主从热备,提供自动容灾切换、数据备份、故障迁移、实例监控、在线扩容、数据回档等全套的数据库服务。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档