你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

C语言与C++

[复制链接]
gaosmile 发布时间:2020-12-25 22:27
70年代初,贝尔实验室创建了C语言,它是开发UNIX的副产品。很快C就成为了最受欢迎的编程语言之一。但是对于Bjarne Stroustrup来说,C的表达能力还不够。于是,他在1983年的博士论文中扩展了C语言。4 v! Z0 w" R3 f: _. e, m- X
于是,支持类的C语言诞生了。; E$ \9 r8 T' g
当时,Bjarne Stroustrup明白编程语言有许多组成部分,除了语言本身,还有编译器、链接器和各种库。提供熟悉的工具有助于语言被广泛接受。在这种历史背景下,在C语言的基础上开发C++也是有道理的。7 M- a1 \+ m  ?( ?1 H

0 O) W0 G3 F+ X* }
40年后,C和C++都在行业中得到了广泛使用。但是,互联网上的C开发人员认为C++是有史以来最糟糕的人类发明,而许多C++开发人员则希望有朝一日C语言灰飞烟灭。
, q" h  ?; p4 W; S' u
1、究竟发生了什么事?

# a: T. N. M0 s& \. P( ]3 \
从表面上看,C和C++都可以满足相同的用例:高性能、确定性、原生但可移植的代码,可用于最广泛的硬件和应用程序。
" g' j4 [* ~( X2 z1 r/ F( Q
但是,更让C自豪的是它是一门低级语言,更接近汇编。
7 ?; ^2 s  F3 g$ M. {
而C++,从诞生第一天开始就充斥了各种奇怪的东西。例如析构函数这个黑魔法。自作主张的编译器。尽管很早C++就有了类型推断功能,但是80年代中期的开发人员还无法接受这个概念,因此Bjarne Stroustrup不得不删除了auto,直到C++ 11又重新添加回来。
6 z6 \" L* C/ B/ [0 \( @+ V
从那以后,C++就不断加入各种工具来实现抽象。很难说C++是一种低级语言还是高级语言。从设计目的上来说,C++两者都是。但是在不牺牲性能的情况下,建立高级抽象是很困难的。于是C++引入了各种工具来实现constexpr、move语义、模板和不断增长的标准库。
% ^3 j- H- s; k  W6 @
从根本上讲,我认为C信任开发人员,而C++信任编译器。这是一个巨大的差异,单凭“两者的原生类型相同”、“while循环的语法相同”等简单一致是无法掩盖的。
' |- u; W1 H( @) ?3 T8 F) N
C++开发人员将有这些问题归咎于C,而C开发人员则认为C++过于疯狂。我觉得站在C的角度看C++,这种说法也很正确。作为C的超集,C++确实很疯狂。一个经验丰富的C开发人员面对C++可能没有熟悉的感觉。C++不是C,这就足以引发互联网上的激烈争论。
8 ~$ [0 f1 h% {& l4 x2 x' P
然而,虽然我不喜欢C,但也没有权利取笑C。尽管我有一定的C++经验,但用C编写过的代码少之又少,而且肯定是很糟糕的代码。好的编程语言包括良好的实践、模式、惯用写法,这些都需要多年的学习。如果你尝试用编写C++的方式写C的代码,或者用C的方式编写C++的代码,那感觉一定很糟糕。即便你懂C,也不一定会C++,反之亦然,懂C++也不一定会用C编程。
9 ?! s3 x0 b. C7 t$ ^: U
那么,我们是否应该停止说C/C++,为这两个不幸的命名而感到悲哀吗?也不至于。3 o; Q: L4 U4 `4 A: }% P4 s, E+ [
尽管C++的设计理念与C不一样,但是C++仍然是C的超集。也就是说,你可以在C++转换单元中包含C的头文件,这样依然可以通过编译。而这正是造成混乱的地方。% l0 A: B" v7 L+ Y
C++不是C的扩展,它是由不同的委员会、不同的人独立设计的标准。从逻辑上讲,喜欢C++理念的人会参与C++社区以及C++标准化的过程,而其他人可能会尝试参与C。无论是C的委员会还是C++委员会,他们表达意图和方向的方式只能通过各自的最终产品:标准;而标准是众多投票的成果。
9 G& o  p6 ?  R  P, R, J, T. M8 p
然而,编译器很难知道它正在处理的是C头文件还是C++头文件。8 d+ c6 e. F' N7 [
extern “C” 标记并没有得到广泛一致的使用,而且它只能影响修饰,而不会影响语法或语义。头文件仅对预处理器有影响,对于C++编译器而言,所有内容都是C++转换单元,因此也就是C++。然而,人们依然会在C++中包含C头文件,并期望它“正常工作”,而大多数时候也确实可以正常工作。% U/ \* ?* g3 i) I' U
那么,我们不禁想问:- ^8 q" ?1 O- |+ Y
2、由不同地方的、不同的人开发的C++代码如何保持C的兼容性?1 c: A7 c1 u: f/ C6 @- Z
恐怕很难。" E" b6 V- L$ l$ d8 ~% \
最近,一位同事让我想起了康威定律:5 B' @$ s9 _# K& `8 o8 `
"设计系统的架构受制于产生这些设计的组织的沟通结构。"& X$ y" e8 k+ p: Y$ E% T' h
根据这个逻辑,如果两个委员不互相合作,则他们创造的语言也不会互通。. Z  X& L+ {$ ?6 v* w
C++维护了一个与C及其标准库的不兼容列表。然而该列表似乎并未反映出许多C11和C18中添加、但在C++中不合法的功能。更清晰的介绍请参见这个维基本科页面(https://en.wikipedia.org/wiki/Compatibility_of_C_and_C%2B%2B)。
1 C; C' _" O  \' p9 `% m4 |
然而,仅仅列出两种语言之间的不兼容性,并不足以衡量二者的不兼容性。) J: n0 l5 Y5 H. K3 l( V
( `4 |9 J7 z2 x5 h+ U
那些存在于C++标准库中但主要声明来自C的函数,很难声明成constexpr,更难声明成noexcept。C的兼容性会导致性能成本,而C函数是优化的障碍。
0 d, }- M6 F& S# ~' ^; n
许多C的结构在C++中都是有效的,但无法通过代码审查(如NULL、longjmp、malloc、构造/析构函数、free、C风格的类型强制转换等)。
5 T, P% v0 z6 i; i; J
在C看来,这些惯用写法可能问题不大,但在C++中可不行。C++具有更强大的类型系统,不幸的是,C的惯用写法在这个类型系统中凿了一个洞,因此实现C的兼容性需要在安全性方面付出代价。/ v5 g/ h; b& D& |
别误会,C++仍然关心C的兼容性,某种程度上。然而,有趣的是C也很关心C++,某种程度上。实话实说,C对C++的关心程度可能高于C++对C的关心。看来,每个委员会还是在乎另一个委员会的工作。但我们很不情愿。
/ a4 D) H. _! M( O9 Y2 r
C++知道,许多基础库都是用C编写的,不仅包括libc,而且还有zip、png、curl、openssl(!)以及许多其他库,无数的C++项目都在使用这些库。C++不能破坏这些兼容性。! q, ~2 Q1 t0 S" _
但是最近,尤其是在过去的十年中,C++的规模已远远超过C。C++拥有更多的用户,并且社区更加活跃。也许这就是为什么如今C++委员会的规模是C委员会的10倍以上。
5 d) ^. _1 W  e. P" e3 b
C++是不可忽视的力量,因此C委员会必须考虑不破坏C++兼容性。如果非要说一个标准追随另一个标准对话,那么如今C++是领头者,而C是追随者。
. F7 N: L; R9 K% ]
% n; t8 M5 i; p. Y% _8 s* Q, R8 \
现在,C++处于稳定的三年周期中,无论是风雨还是烈日,抑或是致命的新疫情。而C每十年左右才发布一次主版本。不过这也很合理,因为作为一种较低级的语言,C不需要发展得那么快。* e% [' E/ p9 E$ o
C语言的环境也与C++完全不同。C多用于平台,更多地用于编译器。每个人(甚至他们的狗狗)都会编写C编译器,因为该语言的特性集很小,所以任何人都可以编写C编译器。而C++委员会真正考虑的实现只有四种,而且在每次会议上这四种实现都会出现。所以,C语言中的许多功能都是与实现有关的,或者是可选支持的,这样各种编译器不需要做太多努力就可以声称自己遵从了标准,据说这样委员会的人会比较高兴。4 m2 k5 q# {1 `8 i" W1 ^
如今,C++更加侧重于可移植性,而不是实现的自由。这又是一个理念的不同。
5 u3 G4 F+ h/ a) P: t" Y0 V
3、因此,你的提议破坏了C的兼容性

+ U. N4 U3 O# o- N- `7 Q# w# x
我提议的P2178的一部分理论上会影响与C的兼容性。这样的话所有方案都不会令人满意。
0 ]* J* c& X& E4 z" Q+ W
有人可能会说,你可以先向C委员会提议你的新特性。这意味着需要召开更多会议。C会议的严格出席规则可能导致你无法参加会议,这就将那些不愿意花上数千美元成为ISO会员的个人拒之门外。这是因为C委员会必须遵守ISO的规则。
9 w' Y6 N! S8 B
而且,如果新的标准刚刚发布,那么可能还需要等待十年时间,你的提案才会被考虑。最重要的是,如果C委员不理解或不在乎你正在努力解决的问题,那么你的提案就石沉大海了。或者他们可能没有精力来处理这个问题。而且,可能你也没有精力来处理C。毕竟,你的本意是要改进C++。实际上,哪怕会议上无人反对你的提议(尽管不太可能发生),如果有人让你先去跟C委员会的人讨论,就等于给你的提议判了死刑。9 G9 M5 E) t. w" E" E
另一种可能的情况是,C委员会接受与C++中存在的版本略有不同的版本。true只能做一个宏来实现。char16_t需要通过typedef。char32_t不一定是UTF-32。static_assert对应的是 _Static_assert。
( Q& l- P' ~( \! m0 U, q: p' ~
这类的情况还有很多,我们应该责备C吗?可能不应该。他们的委员会只是在尽力将C语言做好。反之亦然。在C++20中,指定的初始化器就受到了C的启发,但采取了略微不同的规则,因为如果完全一样的话就不符合C++的初始化规则。; I+ v- e- D; O& B9 G
对于这个问题,我也有责任。C有VLA。如果当时我在,我一定会反对在标准C++中采用它,因为它导致了太多安全性问题。我也会坚决反对将_Generic添加到C++中的提议。也许_Generic的目的是减少由于缺乏模板或缺乏重载而导致的问题,但是C++有这两个功能,从我的角度来看,_Generic并不适合我想象中的C++。) P" V: }$ |2 ?' \  l! E& X

2 J; b. C9 V% `! q) A
这两个委员会似乎对于对方语言的关心程度也不一样。有时我们会遇到兼容性非常好的情况(std::complex),有时完全不在乎兼容性(静态数组参数)。
3 b4 T) E( g7 |  A1 s$ Y
这没有办法。别忘了每个委员会都是一群人,他们在不同的时间、不同的地点投票,而试图控制结果会导致投票毫无意义。将这些人放在同一个房间也不现实。ISO可能会反对,参与者的不平衡会导致C的人处于极大的劣势。: ~. Z5 w% W7 A9 ~. t" O  |3 e
4、C的兼容性不重要

* c4 P% E2 V/ C" E5 |% v1 V
如果你是C开发人员,那么肯定会把C视为一种简洁的编程语言。但对于我们其他人而言,C的印象完全不同。
  e# ~4 v* t1 E0 [4 L/ t9 F
C是通用的、跨语言的胶水,可以将一切紧密地结合在一起。
# I3 p4 `7 v& A8 A% U0 @
对于C++用户而言,C就是他们的API。从这一点来看,C的价值在于其简单性。请记住,C++关心的那一部分C是出现在接口(头文件)中的C。我们关心的是声明,而不是定义。C++需要调用C库中的函数(Python、Fortran、Rust、D、Java等语言也一样,在所有情况下都可以在接口边界使用C)。8 N+ H9 }  E2 u, W: t1 b, X- ]
' a6 Z: U, G3 \1 K$ `& j
因此,C是一种接口定义语言。向C添加的内容越多,定义接口就越困难。这些接口随着时间的推移保持稳定的可能性较小。8 x: l; |* [) ^# Q" l8 N
那么,C++中缺少<threads.h>是否重要?可能并不重要,因为这不太可能出现在公共接口中。* Z, G( J9 h. P( w* O
5、如今大家都在谈论C
8 c  J/ S/ Q" e: i0 N2 L' |
过去,C的兼容性是C++的一大卖点。但如今,每个人(甚至他们的金鱼)都懂C。Rust可以调用C函数,Python、Java、一切语言都可以!甚至怪异的Javascript都可以在WebAssemby中调用C函数。+ L* _  d6 |) t+ I4 @
但是在这些语言中,接口是显式的。该语言提供的工具可以公开特定的C声明。当然,这比较麻烦。但这可以让接口非常非常清晰。而且还是有界的。例如,在rust中,调用C函数并不会迫使Rust牺牲某些设计来容纳C子集。实际上C是被包含进去的。

  1. & S5 ^9 ^8 N& c
  2. mod confinment {1 C/ A& w/ \8 R# n- F! f$ j+ y& \
  3.     use std::os::raw::{c_char};
    + {& N! z6 [1 V/ F2 [
  4.     extern "C" {( ]3 |  P/ M7 @0 n0 B+ h) R
  5.         pub fn puts(txt: *const c_char);4 e, p$ o$ }2 Z: R9 f5 h1 [# m
  6.     }# V2 d6 i1 r, |0 ?: V" ?
  7. }( i) ~! T- m5 a( i
  8. pub fn main() {9 U; K, Q# B8 U! E
  9.     unsafe {9 d6 B. R8 L) h3 j8 R
  10.         confinment::puts(
    0 Z6 g$ ~8 B. g; i
  11.             std::ffi::CString::new("Hello, world!").expect("failed!").as_ptr()6 f8 K' c7 h/ B5 C4 }. e
  12.         );
    + m1 T- h! D$ h( t) Y( C
  13.     }3 g' h  }. `$ ~9 }7 D% g0 s" E* O# D: N
  14. }
复制代码
: I3 q+ t+ \6 `) K' p* o8 s7 B; Y
6、编译器资源管理器

0 c7 M9 B0 r$ n. m/ \! D4 B& |
除非C的ABI发生变化,否则这段代码可以一直正常运行。而且Rust/C的边界非常清晰、不言自明。
; d6 W9 j; n6 s2 ~
6 i; m: B2 A0 ?7 \0 N, K5 w2 ~
因此,C++可能是为C兼容性付出最多的语言。1 d) h: z4 `# r1 S) }

2 s% K0 s4 k7 w+ @4 `: k
更糟糕的是,打开任何C的头文件,你很快就会发现一堆#ifdef __cplusplus。没错,C++的兼容性往往需要大量C开发人员的工作。兼容性一直是海市蜃楼。很多人都知道我的这条推文:
* ~+ L" S. g6 c; b: ^; i! F: |

# n5 Z5 e$ \0 ~' W3 z8 A
微信图片_20201225222545.png
! k* e. r6 d6 Y) k. v& n$ ~8 v- d; x& W

* O8 O* N# R5 f7、我们该何去何从?
3 d" V+ k. r; g6 P: z" W
我认为两个委员会都在尝试更多地沟通。他们计划明年在波特兰召开会议(尽管这个计划可能会变)。沟通是一件好事。* y% E  H, b# {4 G8 J* t, g/ d
但是鸡同鸭讲的沟通效果会非常有限。两种语言的设计支柱可能都不协调。我会努力建议提供一个模板。但是首先我得吐槽C语言没有模块、没有命名空间,以及整个宏是什么玩意儿。& v: f# q7 G; Y% c5 \2 A# P2 R
也许可以将C++能接受的C子集约束在C99上?也许两种语言都需要找到一个共同的子集并独立地发展?也许extern C需要影响解析。如果C++经历了多个时代,那么C可能是其中之一。

+ H9 y  L: Y9 ^0 {$ f# F% |
也许我们需要接受将C作为C++的子集,但唯一的方法是将WG14融入到WG21中。
; P- B* i" B3 i
现状可能不会改变。C++可能永远也无法从自己的起源中解脱,而C可能永远都要与那些顶着C语言之名的肮脏特性战斗。/ `( ~+ H0 L5 @# G8 m
- o- d" c( U+ {
赞 收藏 1 评论0 发布时间:2020-12-25 22:27

举报

0个回答

所属标签

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版