虽然 c 语言并不是我所学的第一门语言,也不是我的最后一门语言,但是我仍然非常喜欢 c,当需要写程序时,我的第一选择还是 c。同时,我也会关注现代编程语言及其发展趋势,而且我还使用 rust 编写了自己的业务爱好项目。那么,为什么我没有抛弃 c 而选择其他语言呢?我对于 c++的看法又是如何的呢?
1 为什么说c不是最好的语言?
首先,这个世上没有最好的编程语言。每种语言都有独特的优势以及适用情况,所以尽管你可以在 excel 中编写光线追踪程序,但最好还是使用其他语言。因此,我们都需要了解编程语言的限制,不要抱怨 web 服务器不是用 fortran 编写的,也不要抱怨基本没有任何应用使用 perl 或 c++作为内部脚本语言。我认为 c 语言不太理想的方面包括以下几点。 除了 c 比较老,发展不快之外,当然还与个人的喜好有关。 其次,有些时候,c 的语言不够明确。比如,*可以是二进制乘法运算符、一元解引用运算符,也可用于声明指针。 再者,有些情况不够安全,例如越界访问数组这种极其常见的错误都没有运行时检查,这一点连 borland pascal 都比不了,更不用说更现代的编程语言了(尽管你会为了提高性能关闭这个编译选项)。此外,指针让我们很难保持一切井然有序。再加上一些其他情况,比如调用函数不需要事先声明原型,这样很容易将错误类型的参数传递给函数。 最后,c 的标准库非常有限。有些编程语言甚至拥有开箱即用的 web 服务器(或者至少有构建 web 服务器所需的所有模块),但 c 标准库甚至连 web 服务器的容器也没有。
2 为什么我还是喜欢c?
尽管如此,我还是十分喜欢 c,因为它是一种简单的语言。从某种意义上说很简单,很容易表达自己的想法以及期望。 举个例子,假设两个数组有两个偏移量,其中一个可以为负数,如果使用c语言编写,则可以写成: arr[off1 + off2] 如果是rust,则需要写成: arr[((off1 as isize) + off2) as usize] 通常,c 的循环也比 rust 的迭代器组合更为简洁(当然 rust 也允许使用前一种方式,但 linter 并不满意,它会建议你使用迭代器来代替)。类似地,memset()和 memmove()也是功能十分强大的工具。 在大多数情况下,你都可以预见到编译的结果,即对象在内存中的表示方式,以及如何通过不同的方式理解编译后的结果(新版 c 标准中这一点变得更困难,这都要怪 c++,我稍后再详细介绍)。另外,你也很清楚函数调用的结果等等。由于这个原因,c 被称为可移植的汇编语言,所以我非常喜欢 c。 我们拿汽车做个类比,c 语言就像一辆跑车,拥有手动变速箱,可以提供最佳性能,但是如果你不熟悉离合器和挂挡操作,那么变速箱很容易被损坏,甚至可能损坏发动机,当然,油门踩得过大也有可能冲出马路。然而,与自动变速箱相比,这种车辆的发动机能量更大,而且你可以预测性能,还可以炫车技,这些在其他车辆上都是不可能的。
3 这与c++有什么关系?
下面,我们来说一说 c++,其实我不讨厌 c++。我不能否认,与 c 相比, c++ 拥有两个优点:
更好的程序结构:c++ 拥有命名空间和类,而且在某些方面simula还是很出色的。
拥有 raii 概念:一个简单的例子就是 c++ 拥有构造函数,可在创建对象时初始化对象;还拥有析构函数,在销毁对象时,做一些清理的工作。这个概念进一步发展,就接近 rust 的生命周期了。
另一方面,c++ 有两个特征,我非常不喜欢。 首先是这门语言的整体性质。其他编程语言拥有的流行功能最终都会进入 c++。因此,每过几年,c++标准就会添加一些新功能。最终,这门语言就变得有点怪异,没人能够完全掌握,而且许多功能都是抄袭的其他语言。基本上每个人在编写代码的时候,都会选择一个 c++的子集,然后忽略其他功能的存在。另外,我们究竟应该使用哪个 c++版本的功能,并没有一套标准的方法。rust 在包的范围内提供了版本管理。据我所知,c++也曾尝试过引入“代际”的概念来实现同样的功能,但没有成功。我经常听到有人独自编写 c 编译器,却从来没听说过有人编写 c++编译器。 其次,实际上 c++不仅是多种语言,而且还是一种元语言(即模板)。我了解 c++的创建初衷,也同意它对于与类型无关的代码的处理,比 c 预处理器更好。但实际上,它产生的代码十分可怕,原本是“头文件仅包含声明,实现放在编译好的代码中”,变成了“头文件包含所有项目会用到的代码”。我不喜欢过于冗长的编译时间,但这种方式只能让情况更糟。 最后,我觉得 c++的出现反而给 c 带来了约束以及不良影响。我不是在讨论 c/c++,也不是指 c 与 c++的共通之处,我讨论的是耦合对标准和编译器都有不良影响。一方面,c++建立在 c 之上,从而得到了极大的发展;另一方面,如果 c++中没有 c 遗留下来的大多数功能的话,情况可能会更好(当然,c++曾设法通过淘汰的方式逐步放弃某些 c 功能,但对于旧功能的支持仍然存在)。但是,c++ 24 能够在 c++ 21 的基础之上,发展成为一门独立的编程语言吗?大多数过时的功能都可以抛弃吗?我对此表示怀疑。
4 c++编译器对c的影响
实际上,c 语言被当成了没有某些功能的 c++。比如微软的 c 编译器直到2015 版才开始支持 c99 功能(即便如此,它还是以 bug 修复 bug 的方式来支持兼容性,因为客户可能会震惊地发现可变参数宏居然可以运行)。但是,无论是标准的编译器还是其他编译器中都可以看到相同的方法,这些都是相关的问题。 主要问题在于,c 和 c++标准都是根据编译器开发人员的反馈而编写的,而且大多数都是 c++开发人员(有些人对现实世界编程一无所知,而且他们还认为现实世界的做法与自己的观点完全吻合,真是令人窒息的操作)。虽然我也没有遵循标准的开发程序,但是我很确定 c99 及其后版本中令人讨厌的诸多功能皆来自那些编译器开发人员。他们只从 c++的角度出发考虑,而且还将这些功能强加给了 c,还美其名曰简化编译器。 当然我指的是“未定义的行为”以及编译器的处理方式。这已成为一大毒瘤(只要你的代码依赖于二进制补码算术,就会被认定具有未定义的行为,编译器会抛弃整块代码)。 在我看来,以下四种行为尽管不值得提倡,但前两个也并非不可接受:
依赖于体系结构的行为(即依赖于 cpu 体系结构的行为)。包括绝大部分算术运算。例如,如果我知道目标及其使用了两个协处理器,为什么编译器会选择另一种方式,仅仅是为了获得理论上的优化?同样的问题也适用于移位运算。如果我知道 x86 会忽略移位偏移量的高比特,在 arm 上负的左移相当于右移,那么为什么不能专门针对该体系结构编写程序呢?毕竟,连整数的大小在不同平台上都不一样。这种不可移植性只需警告就好,让用户自行处理。
指针魔法和类型双关。这似乎又是编译器优化带来的限制。我同意,在重叠的内存区域上使用 memcpy(),不同的实现可能会给出不同的行为(现代的 x86 实现会从区域尾部开始复制),而且还依赖于地址的相对位置,但其他的规则就没什么道理了。例如,无法使用两个不同类型的指针同时操作同一块内存区域。我无法想象为什么这种行为被禁止,其原因只可能是编译器优化。这样就不可能利用联合体将整数转换成浮点数。linus 也曾吐槽过这一点,我就不用重复了。但在我看来,这样做的目的或者是更好的编译器优化,或者是出于 c++的要求(由于类型跟踪的要求)。
实现中定义的行为(即超出 c 标准规定的行为)。我常用的例子就是函数调用:根据调用的习惯约定和编译器的实现,函数的参数的求值顺序可能完全是随机的,因此 foo(*ptr++, *ptr++, *ptr++)的结果是未定义的,因此即使你知道目标体系结构,也不应该依赖于这种行为。
完全未定义的行为。最常见的例子就是在一条语句中改变变量状态,例如著名的 i++ + i++,或者更甚的 *ptr++ = *ptr++ +*ptr++。
由于 c++比 c 更高级(尽管它由许多来自 c 的特性,但都不建议使用,应该使用 reinterpret_cast代替类型转换,用引用代替指针,等等),所以不要期待 c++程序员能够像 c 程序员那样理解底层代码。当然,由于 c++程序员占绝大多数,c/c++的耦合也极其常见,所以 c 编译器通常会进行扩展以支持c++,并使用 c++重写,以适应其复杂度。所以很不幸,你不得不使用 c++编译器来编译 c 编译器(还好我们还有 lcc、pcc 和 tcc 等纯 c 编译器)。
5 总结
总的来说,我喜欢c所处的中层位置,它既可以完成一些底层的实现,例如轻松地操作内存,同时又可以享受高级语言的好处。另一方面,我对c++强烈的不满来自其在设计上的选择,而且这些设计影响了c标准和编译器。 至少我不可能用 c90 特别版取代 c90,并假装原来的版本不存在。
iPhone8全面升级?能否借此回到巅峰?
关于5G改变自动驾驶的相关介绍
6N5P单端胆机制作方法与心得
在什么条件下会发生锂沉积?锂是如何成核与生长的?
集成电路PPA和APP是个啥?集成电路创业机会在哪里?
C语言与C++的区别看法
串馈天线阵列智能设计方案
如何在智能工厂建设中少走弯路?
开门通知器的制作
基于GPRS模块与Zigbee网络实现照明终端系统的应用设计
使用高压输入稳压器简化工业控制系统的开发
高通骁龙895或将重回台积电制造
如何保护脆弱的供应链免受网络安全风险和攻击
韩国三大运营商是如何部署5G网络和发展5G业务的
是德科技推出下一代旁路交换机 iBypass DUO
4G多路开关量DI输入短信电话报警器
iPhone8什么时候上市?最新消息:iphone 8/iphone7s即将发布,真机谍照曝光!背面发光logo+一块屏幕
芯力特LIN收发器芯片SIT1021与国外同类产品的兼容性
选择2.4G无线模块的方法
EMC测试及整改建议