C++这么难,为什么我们还要学习C++?

2023-11-06


前言

C++ 可算是一种声名在外的编程语言了。这个名声有好有坏,从好的方面讲,C++ 性能非常好,哪个编程语言性能好的话,总忍不住要跟 C++ 来单挑一下;从坏的方面讲,它是臭名昭著的复杂、难学、难用。当然,这样一来,熟练的 C++ 程序员也就自然而然获得了 “水平很高” 的名声,所以这也不完全是件坏事。

不管说 C++ 是好还是坏,不可否认的是,C++ 仍然是一门非常流行且非常具有活力的语言。继沉寂了十多年,并终于发布语言标准的第二版 —— C++11,再那之后,C++ 以每三年一版的频度发布着新的语言标准,每一版都在基本保留向后兼容性的同时,提供着改进和新功能。

1. 为什么难学

每次提到 C++ 编程,无论你是使用 C++ 的开发者,还是使用其他编程语言和开发环境的开发者,我们对 C++ 的评价往往都是 “复杂且难学”。为什么 C++ 会留下这样的口碑?追根溯源,主要有两个原因。

第一个原因是 C++ 的包容性,即向前兼容。

C++ 类似 Objective-C,是 C 语言的超集,它希望尽量向下兼容 C 的一切语法和特性(在 C99 标准之前甚至是完全兼容),因此足够接近硬件底层。但这是把双刃剑。

虽然 C99 之前语法足够简单,但实际使用的复杂性并不低,而 C++ 为了兼容 C 语言的语法付出了很大的代价,并在此基础上设计并发展出了多范式的编程模型,这意味着可以继续采用面向过程的编程模式,也可以转向面向对象。与此同时,现代 C++ 还提供了一组函数式编程工具。

因此,在现代 C++ 得到发展以前,实际开发时到底要选用何种范式或者如何合理组合,一直让我们很头痛。

C++ 兼容 C 有什么代价呢?比如,C 的指针类型声明就备受 C++ 之父 Bjarne Stroustrup 诟病,但是为了向前兼容,不得不在这种声明模式下继续扩展。

第二个原因是 C++ 的设计哲学,“不为任何抽象付出不可接受的多余运行时性能损耗”。

纵观 C++40 多年的演进历程,可以发现每一次演进所支持的都是和编译时相关的新特性,而相对来说,运行时特性非常少,除了在面向对象的编程模型基础上提出的多态以外,几乎再无运行时特性(其他的均以库的形式提供)。这是因为 C++ 是零成本抽象,也就是说,开发者在使用 C++ 表达抽象概念时,无需忍受多余的运行时性能开销。

因此,虽然 C++ 具备很多高级抽象的语法特性,但在设计与具体使用过程中,我们仍然需要考虑各种各样的问题,包括基础对象内存模型、虚函数的设计、基于模板的泛型系统、基于模板的静态反射体系,以及到目前为止都是由编译器决定可选的垃圾回收(在其他现代语言中可以说是必备的特性了),这就让我们学习和使用 C++ 变得更复杂了。

在这里插入图片描述

的确,这真够复杂的。一门编程语言必定有其局限性,这也是为什么 “更为现代” 的 Go 和 Rust 出现了,试图解决一些问题,特别是安全性方面。

不过作为语言的使用者,你肯定会问,那今后的 C++ 学习和使用会有哪些变化呢?这个问题,有人曾经问过 C++ 之父 Bjarne Stroustrup。

诸如 Go 和 Rust 编程语言新贵,它们在发力解决安全性和易用性方面的问题,规避缓冲区溢出这样的漏洞,甚至 Linux kernel 也开始考虑或采纳对 Rust 的支持,您是否觉得这会成为 C++ 的一个潜在的巨大威胁和挑战?

他的回答简单明了。

“每隔几年,就会出现 C++ 的挑战者,我相信它们一定会有支持者。但是,C++ 的独特的语言特性、应用场景,以及 C++ 标准发展的方向,会让 C++ 继续茁壮成长。”

我特别喜欢这个回答。是啊,劣势固然存在,但 C++ 经过历史的检验,在高性能计算、低延迟处理、图形学领域以及机器学习等前沿技术领域有着难以替代的优势。

C++ 的 “复杂且难学” 一定程度上取决于向前兼容的能力和设计哲学,但正因如此,维护多年的系统仍然能与全新开发的系统友好地对接和集成,C++ 的包容性和多样性也让它极具发展力。

自 C++11 标准诞生以来,我们正式迈入现代 C++ 世界,而 C++20 及后续演进标准作为继 C++11 之后的又一次重大变革,给我们带来了新思想、新工具,让我们从容面对以往难以解决的问题。

2. C++的意义

C++ 程序员应该都听到过下面这种说法:

  • C++ 是一门多范式的通用编程语言。

多范式,是因为 C++ 支持面向过程编程,也支持面向对象编程,也支持泛型编程,新版本还可以说是支持了函数式编程。同时,上面这些不同的范式,都可以在同一项目中组合使用,这就大大增加了开发的灵活性。因此,C++ 适用的领域非常广泛,小到嵌入式,大到分布式服务器,到处可以见到 C++ 的身影。

下面是一些著名的用到 C++ 的场合:

  • 大型桌面应用程序(如 Adobe Photoshop、Google Chrome 和 Microsoft Office)
  • 大型网站后台(如 Google 的搜索引擎)
  • 游戏(如 StarCraft)和游戏引擎(如 Unreal 和 Unity)
  • 编译器(如 LLVM/Clang 和 GCC)
  • 解释器(如 Java 虚拟机和 V8 JavaScript 引擎)
  • 实时控制(如战斗机的飞行控制和火星车的自动驾驶系统)
  • 视觉和智能引擎(如 OpenCV、TensorFlow)
  • 数据库(如 Microsoft SQL Server、MySQL 和 MongoDB)

有些同学可能会觉得,这些应用场景似乎和平时的开发场景有点远啊!你的感觉是对的。有些传统上使用 C++ 的场合现在已经不一定使用 C++,最典型的是个人电脑上的桌面应用。以前 Windows 下开发桌面应用常常用 MFC,微软的 C++ 框架。目前很流行的 Visual Studio Code 主要是用 TypeScript 写的,不是 C++。

C++ 的传统领域有被侵蚀的风险,那是因为和它相竞争的语言远远不止一个,可以说是上下夹攻。

  • 如果专注性能和最小内存占用的话,C 仍然是首选——嵌入式领域用 C 非常多,而 Linux 也是用纯 C 写的。
  • 如果专注抽象表达和可读性的话,那 Python 之类的脚本语言则要方便得多。
  • 图形界面(GUI)编程传统上是 C++ 的地盘,但近年来 C# 和 JavaScript 占领了很大一部分市场。
  • 游戏算是 C++ 的经典强项了,但有了 C++ 写的游戏引擎,游戏用 C# 写也没啥问题了——你可能不一定知道,Unity 游戏引擎上的首选开发语言是 C#,而王者荣耀是用什么游戏引擎呢?答案正是 Unity —— 所以王者荣耀可以认为是用 C# 开发的。
  • 还有,Go 和 Rust 也加入了战团,对 C++ 形成了一定的竞争……

目前,跟 C++ 定位差不多、能有直接竞争关系的,也就是既支持高度抽象、又追求高性能的通用编程语言,其实只有 Rust 一种。而 Rust 远没有达到跟 C++ 一样的成熟和普及程度。这也可以从 TIOBE 的排名看出来:C++ 是第 4 位,而 Rust 是第 25 位。

另外,和 C 的兼容性,也是 C++ 的一大优势。虽然现在很多大型程序都混杂了多种语言,但在小项目里,减少语言的数量可以简化开发和部署。

3. 什么时候该用C++

C++ 既然性能又好,又支持抽象,为什么没有更流行呢?

因为代价更高。C++ 是一种复杂的语言,难以上手和熟练掌握,因此也是一种比较容易出错、被误用的语言。C++ 一直与 C 基本保持了向后兼容性,这种兼容性,也一直是 C++ 的安全性和易用性方面的负担。

C++ 比起 C 来,要更安全,更不容易出现缓冲区溢出这类漏洞,但跟没有指针概念的语言比起来,它仍然是一种“不安全”的语言。我的个人经验,完成同样的功能,C++ 需要的代码行数一般是 Python 的三倍左右,而性能则可以达到 Python 的十倍以上。

那么问题来了:你在开发上额外付出的时间,能从性能上省回来吗?

显然,这取决于你开发软件的用途和开发时间。举个例子,如果你用 Python 开发需要一天,运行需要十秒,并且不需要反复运行;那么,转用 C++ 开发就意味着开发费用也许要增加两倍,开发加运行的总时间增加两天,大亏。

反之,如果用 Python 开发还是需要一天,单次运行需要十秒,但是软件会作为服务长时间运行、每天被调用十万次。在这种情况下,明显你就需要多台服务器来支撑其使用了。这时,如果用 C++ 开发会需要额外的两天,但跟 Python 相比,部署上有望节约十分之九的硬件和电费 —— 那就很值了。

简言之,当你的软件属于运算密集或者内存密集型,你需要性能、且愿意为性能付出额外代价的时候,应该考虑用 C++,特别在你的代码需要部署在多台服务器或者移动设备的场合。反之,如果性能不会成为你开发的软件的瓶颈,那 C++ 可能就不是一个最合适的工具。

此外,在嵌入式应用的场景,那就根本不是值不值、而是行不行的问题。如果程序完成一个功能不能在指定的若干毫秒、甚至微秒内完成,那产品根本是失败、不可用的。在这种场合,能和 C++ 竞争的只有 C,但 C 是一种开发效率更低、更需要堆人力的语言了。在嵌入式开发使用 C++ 的最大障碍可能不是技术,而是人力资源——搞嵌入式开发的程序员可能大多都习惯使用纯 C 了。

由于 C++ 是解决性能问题的利器,短时间里在市场上没有真正的竞争对手,对 C++ 的需求会在相当长的时间里一直存在,尤其在大公司和像金融机构一样对性能渴求的地方。

顺便提一句,C++ 之父 Bjarne Stroustrup 目前就职的地方便是摩根斯坦利。

4. 如何学习C++

作为很多聪明人使用过的语言,C++ 在某些场合也可能被用来炫技,写出除了本人之外谁都看不懂的高抽象代码。这恰恰是 Bjarne 想努力抵制的方向。他想让 C++ 对初学者变得更为友好,也明确提出过,他不希望 C++ 是一种让人们耍机灵的语言,而是一种让人们更易于使用的语言。

这同样也是本专栏 「C++深入浅出」 的一个目标:学习 C++ 语言就像学一门活跃使用中的外语,你不要期望能够掌握所有的单词和语法规则 —— 那对于世界上 99.999999% 的人来说是不可能的。但语言是服务于人的,语法规则也是服务于人的,是为了让人们能够更好地沟通和表达。虽然 C++ 的每一个新标准都是让语言从定义和规则的角度变得更复杂,但从用法上来说,新标准允许人们能够更简单地表达自己的计算意图。跟学外语一样,我们需要的是多看多写,掌握合适的 “语感”,而不是记住所有的规则。

Bjarne 有一个洋葱理论: 抽象层次就像一个洋葱,是层层嵌套的。如果想用较低的抽象层次表达较高的概念,就好比一次切过了很多层洋葱,你会把自己的眼泪熏出来的。与这个思路相反,教 C++ 往往有一种不好的倾向,从那些琐碎易错的底层教起,自底向上,使得很多人常常在尚未领悟到抽象的真谛之前就已经被 C++ 的复杂性吓翻,从入门到放弃;或者,在学了基本的 C 语法和 class 之后就满足了,错过了高级抽象带来的全新境界。他主张学习应当自顶向下,先学习高层的抽象,再层层剥茧、丝丝入扣地一步步进入下层。如果一次走太深的话,挫折可能就难免了。

5. 学前勉言

我想和大家分享几句编程格言。这三条格言已经陪伴了我很久,一直指导着我的编程实践。

  • 任何人都能写出机器能看懂的代码,但只有优秀的程序员才能写出人能看懂的代码。
  • 有两种写程序的方式:一种是把代码写得非常复杂,以至于 “看不出明显的错误”;另一种是把代码写得非常简单,以至于 “明显看不出错误”。
  • “把正确的代码改快速”,要比 “把快速的代码改正确”,容易得太多。

C++ 庞大、复杂是无法改变的事实,所以我们要把这三条格言铭记在心,对它保持一颗 “敬畏” 的心,在学习语言特性的同时,千万不要滥用特性,谦虚谨慎,戒骄戒躁。

我很喜欢 15 年前乔布斯在斯坦福大学演讲中的一句话,觉得非常适合 C++。所以,最后我想把它送给正在学习编程的你们,我们共勉,希望我们一起:

Stay Hungry,Stay Foolish.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++这么难,为什么我们还要学习C++? 的相关文章

  • 打击垃圾邮件机器人

    我的网站中有 C 表单 希望防止垃圾邮件机器人填写它 诀窍是 我想避免 CAPTHA 或任何其他用户输入 以避免丢失单个注册 以下是我心中的一些技巧 隐藏输入栏 问题 这还有效吗 跟踪时间 从第一个用户输入 关注名字 到发布表单 人类需要
  • C++ 返回值、引用、const 引用

    你能向我解释一下返回值 值引用和值常量引用之间的区别吗 Value Vector2D operator const Vector2D vector this gt x vector x this gt y vector y return t
  • MySql 最后插入 ID,连接器 .net

    我正在使用 MySql Connector net 我需要获取最后一个查询生成的插入 id 现在 我假设返回值是MySqlHelper ExecuteNonQuery应该是最后一个插入id 但它只返回1 我正在使用的代码是 int inse
  • 每次调用新方法时触发事件

    我正在做一个logger for a c 应用程序需要记录每个方法被调用的时间以及每个方法执行时间 我可以通过调用自己的方法来做到这一点EventLogger LogMethodCall方法在每个方法的开头 但我想知道是否有办法使CLR每次
  • 如何启动异步任务对象

    我想开始收集Task同时处理对象并等待所有对象完成 下面的代码显示了我想要的行为 public class Program class TaskTest private Task createPauseTask int ms works w
  • 使用预编译头减少 clang 编译时间

    我正在开发一个数据库项目 该项目将查询 以某种高级语言表示 编译为 C 代码 这段代码由数据库编译并执行 那部分工作得很好 现在 我正在尝试减少 C 查询代码的编译时间 我想知道是否可以使用预编译头来提高性能 该查询被转换为一个名为 Que
  • 在 DataGridView 中隐藏行非常慢

    我在 Winforms 应用程序中有一个 DataGridView 大约有 1000 行 未绑定 和 50 列 隐藏一列需要整整 2 秒 当我想隐藏大约一半的行时 这就成为一个问题 private void ShowRows string
  • Confuser .NET 混淆器。安全吗? [关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 我目前正在开发一个应用程序 其中阻止用户反编译代码非常重要 现在 我意识到 如果由经验丰富的程序员执行 大多数 exe 都是可反编译的
  • StreamReader,C#,peek

    我有一个 StreamReader 它偶尔会检查它是否有更多内容可以从简单的文本文件中读取 它使用 peek 属性 问题是 当我使用 peek 时 位置发生了变化 尽管不应该发生 FileStream m fsReader new File
  • 有没有办法将 boost::json::serializer 切换为美化输出?

    Using boost json serializer如中的示例所示文档 快速查看 http vinniefalco github io doc json json usage quick look html以紧凑格式保存 json tre
  • 如何使用boost库读取和写入.ini文件[重复]

    这个问题在这里已经有答案了 如何使用boost库读取和写入 或修改 ini文件 With Boost PropertyTree您可以读取并更新树 然后写入文件 请参阅load and save功能 看一下如何访问属性树中的数据 http w
  • C++:初始化静态字符串成员

    我在 C 中初始化静态字符串成员时遇到一些问题 我有几个类 每个类都包含几个表示 id 的静态字符串成员 当我通过调用静态函数初始化变量时 一切都很好 但是 当我想为一个变量分配另一个变量的值时 它仍然保留空字符串 这段代码有什么问题 st
  • 本地主机上的 .net HTTP_X_FORWARDED_FOR NULL

    抱歉 如果其他地方已经回答了这个问题 我找不到它 如果没有 我会尝试查找访问过该站点的机器的原始 IP 根据我的基本理解 变量HTTP X FORWARDED FOR无论代理和其他过滤器如何 都会显示用户的 IP 如果这是真的 我正在尝试对
  • 使用 QGraphicsScene 实现流畅的动画

    我希望我的问题并不总是同样的问题 我有一个 QGraphicsScene 它的项目是一些 QGraphicsPixmap 我用一个计时器来移动它们 每秒 SetX 10 我设置 10是因为窗口大100 使用这个解决方案我的动画不流畅 我想我
  • 该组件没有由 uri 标识的资源

    我想创建一个通用数据网格以在我的所有视图 用户控件上使用 这是我的结构 Class Library called Core Class called ViewBase public class ViewBase UserControl pu
  • 从 exit() 和 fork() 返回的结果奇怪地发生了位移

    我有一个 C 代码 有时会自行分叉 每个分叉都会执行一些操作 然后返回一个错误代码 目前 每个子进程返回其 ID 0 n void other int numero exit numero int main for int i 0 i lt
  • C# Julian 日期解析器

    我在电子表格中有一个单元格 它是 Excel 中的日期对象 但当它来自 C1 的 xls 类时 它会变成双精度型 类似于 2009 年 1 月 7 日的 39820 0 我读到这是儒略日期格式 有人可以告诉我如何在 C 中将其解析回 Dat
  • 在 C# WinForms 中预览文档(Word、Excel、PDF、文本文件等)?

    我正在开发一个 C WinForms 应用程序 我希望能够 预览 其中的各种文档类型 也就是说 当用户从列表中选择文件名时 它会在下面以相同的形式显示所选文件的预览 这很像 Outlook 允许您无需双击即可预览选定邮件的方式 有没有什么方
  • 清理堆分配对象的良好实践或约定?

    我正在学习C 我有 C C ObjC 背景 相当高级的语言 在 C 或 ObjC 上 作为函数或方法的结果返回堆分配的对象是很简单的 因为对象的清理是受管理的 按照惯例 会在适当的时候销毁 但我不知道在 C 中应该如何处理这个问题 例如 s
  • 如何从尖点库矩阵格式获取原始指针

    我需要从尖点库矩阵格式获取原始指针 例如 cusp coo matrix

随机推荐

  • 多服务环境下定时任务重复执行问题解决方案

    当一个服务部署在多台服务器上时 定时任务可能出现多次执行的情况 就是每个服务上执行一次 有以下两种思路 一是固定死只有某服务器执行定时任务 二是随机暂停几秒 某一服务执行了 其他就不再执行 1 固定某一个服务器作为执行定时任务的机器 通过配
  • 基于混沌映射与差分进化的自适应教与学优化算法

    文章目录 一 理论基础 1 标准TLBO算法 2 混沌映射与差分进化的自适应TLBO算法 1 改进的Logistic混沌映射 2 惯性权重自适应调节函数 3 教改阶段 二 仿真实验与分析 三 参考文献 一 理论基础 1 标准TLBO算法 请
  • 在Power BI中用DAX新建列的方式进行累计求和

    在Power BI中用DAX新建列的方式进行累计求和 DAX 新建列来累计求和 累计求和 DAX 新建列 DAX函数 Filter DAX函数 EARLIER DAX函数 SUMX DAX 新建列来累计求和 Power BI有两种方式进行累
  • 没有node-sass Windows 64-bit with Node.js 16.x

    throw new Error errors missingBinary OS X 64 bit with Node js 16 x Windows 64 bit with Node js 16 x 1 下载node sass 对应的版本
  • 数据分析理论【5】之 下采样策略和上采样策略

    合辑传送门 gt gt 数据分析 合辑 在分类问题的数据中 很容易出现正反数据集数量存在极大的差距 这类数据直接用于训练不利于模型的构架 所以我们需要对数据进行些许处理 很容易想到 合理的数据集应该是正反数据集数量应接近 那就存在两种策略
  • python的艰难学习之路-综合练习--名片管理系统

    名片管理系统需求 1 显示欢迎界面 提示操作码 按照操作码执行程序 2 操作码功能包括 查询 查找 新增 3 查询出来后 可继续操作 gt 修改 删除 4 名片需要记录的信息 gt 编号 姓名 电话 邮箱 QQ 5 编号自动生成 6 如果在
  • 通过高德地图API取得两点坐标间的距离

    高德地图在取两点间的距离比百度地图更详细 可以分为几种类型的API 提供的步行 公交 驾车查询 今天咱们使用驾车的API来计算两点的距离 其它的API大同小异 参考高德地图的API地址 路径规划 API文档 开发指南 Web服务 API 高
  • RunBlocking CoroutineScope SupervisorScope Launch Async CoroutineStart协程启动模式 Job对象和生命周期

    协程的作用域构建器 RunBlocking runBlocking是常规函数 会把当前主线程包装成一个主协程 其会阻塞当前线程 只有当等待其主协程体以及里面的所有子协程执行结束以后 才会让当前线程执行 CoroutineScope coro
  • QT foreach

    如果只想按顺序迭代容器中的所有项 可以使用Qt的foreach关键字 该关键字是对C 语言的Qt特定添加 并使用预处理器实现 与任何其他C 循环构造一样 您可以在foreach循环的主体周围使用大括号 并且可以使用Break来离开循环 其语
  • 华为OD机试 - 数组连续和(Java)

    题目描述 给定一个含有N个正整数的数组 求出有多少个连续区间 包括单个正整数 它们的和大于等于x 输入描述 第一行两个整数N x 0 lt N lt 100000 0 lt x lt 10000000 第二行有N个正整数 每个正整数小于等于
  • 锐捷交换机密码破解

    资料来源 https search ruijie com cn 8447 rqs preview html ie utf 8 wd eHAiOjE1NDU4NzUxNDcsIm5iZiI6MTU0NTYxNTk0N3020180920150
  • 虚拟机-扩充硬盘

    扩充硬盘 https www cnblogs com wy20110919 p 9150914 html https cloud tencent com developer article 1563508 from 14588
  • next_permutation(a,a+n)

    早就听说了了next permutation 产生全排列的强大 一直到昨晚遇到一个对字符串产生全排列的问题才知道这个函数的强大 我们队是按照dfs去搞全排列 然后在进行字符串的匹配 结果写的很长 过程中还各种debug 于是决定今天学一下
  • 认知-想象力:想象力

    ylbtech 认知 想象力 想象力 想象力 是人在已有形象的基础上 在头脑中创造出新形象的能力 比如当你说起汽车 我马上就想像出各种各样的汽车形象来就是这个 道理 因此 想象一般是在掌握一定的知识面的基础上完成的 想象力 是在你头脑中创造
  • Spring学习笔记(一)【BeanUtils.copyProperties方法】

    Spring下的BeanUtils copyProperties方法是深拷贝还是浅拷贝 一 浅拷贝深拷贝的理解 简单地说 拷贝就是将一个类中的属性拷贝到另一个中 对于BeanUtils copyProperties来说 必须保证属性名和类型
  • 【不忘初心】Win11_21H2_22000.100_X64_四合一[纯净精简版][2.9G](2021.8.5)

    此版更新补丁未知 WIN11全新的UI界面出炉 可以说这一次Windows 11全新升级 无论是从Logo上还是UI界面设计 都有很大的变化 不过WIN11目前还不够稳定 小问题比较多 母版来自MSDN WIN11 21H2 22000 1
  • 大学概率论与数理统计知识点详细整理

    目录 概率论学习自述 概率论的一些基本概念 随机变量的分布 一维随机变量的分布 二维随机变量 抽样分布 数学期望 矩 方差 协方差 常见分布的数学期望与方差 一些重要的定理公式 参数估计 1 点估计 2 区间估计 假设检验 独立性 概率论学
  • 蒙皮流程1

    选中要调整权重的点 打开这个窗口 可以调整他的权重值 蒙皮里面的导出导入权重贴图可以在要对模型做修改的情况下 对已弄好的权重进行保留 或者直接用下面的替换几何体用新的替换旧的 给人物下巴绘制权重时 下巴骨骼与躯干骨骼连接处插入一个小骨骼 给
  • Unity ScrollView左右拖拽翻页

    ScrollView来实现左右拖拽的翻页 类似于微信 左右拖拽时候上下无法拖拽 上下拖拽的时候左右无法拖拽 并且左右拖拽的是时候 会有弹力进行对对齐 using System Collections using System Collect
  • C++这么难,为什么我们还要学习C++?

    文章目录 前言 1 为什么难学 2 C 的意义 3 什么时候该用C 4 如何学习C 5 学前勉言 前言 C 可算是一种声名在外的编程语言了 这个名声有好有坏 从好的方面讲 C 性能非常好 哪个编程语言性能好的话 总忍不住要跟 C 来单挑一下