正确地黑C

时间:2022-05-22 17:56:23

转载:http://tieba.baidu.com/p/3190068223?pn=1

本版暂时当作提纲,不做详细展开讨论,以后可能更新。

注意:本文主旨不是政治正确。

1.设计
C的设计相对于同期来说是局促的。C语言具有明显的历史局限性是不争的事实。
类型系统是一个显著的例子。理论上,typed lambda calculus在当时(70年代)即便没有流行也已经有了数十年的发展,但是C的设计并没有有效利用当时的理论成果,还在前人的经验上开洞(比如奇葩的函数指针转换)。
类似地,数组类型也不是第一类实体,也会有类型上的修正。
这些无聊的设计除了让抽象变复杂,限制用户的*外,唯一的好处也许就是顺应之前的具有局限性的语言实现的习惯了。不过,今非昔比,这样的设计并没有简化现在的语言实现——不管是C还是其它语言。
这种仓促的设计影响深远。C++至今仍然把一些肇始于C的奇葩转换保留在“标准转换”中,以至于重载的规则加倍复杂——考虑到兼容问题以及维护规则本身的困难,这些无谓的复杂性今后可能永远也无法移除。
左值(lvalue)是一个不幸的设计。一开始作为文法性质,并没有考虑到潜在的复杂,以至于后来引入const后,功能和区分左值在相当大范围内重复了(若完全一致反倒还好点)。这点另外有说过,按下不表。而这里重要的是,也无法指望丢弃冗余性(C++甚至变本加厉)。
当然,考虑历史的主干,C来自于B,来自于BCPL,来自于CPL,来自于ALGOL 60,来自于ALGOL 58,来自于FORTRAN。这些语言都没有这方面的合理经验积累。所以C的设计的局限,并不难想象。
也有一些其它的不足之处——丢弃有用的特性导致的倒退。不过,有些被纠正过来了,例如BCPL后扔掉的单行注释(//),被某些C方言、C++和C99以及其它一些C-like派生重新吸收。
还有一些奇葩的莫名其妙的地方。B里面叫vector的东西说的好好的,怎么到C里面就变成array了呢?只是因为加了个残疾的“数组类型”?(B是没好意思说成有类型的……)为什么array而不是vector就非得能对元素“O(1)访问”——这种偏见是谁发明的?(另一个不良后果是A.Stepanov搞STL的时候没词了就顺手捡了个……)
2.标准化的有效性
不能否认,在被规范化的语言(而不是实现)针对平台的可移植性来讲,C差不多应该是现在的语言中最强的了。ISO C++在某些少数极端情况下的可移植性的确不如ISO C(见WG21/N4049)。而其它语言,假定1字节不等于8比特的大小就足够打退堂鼓,若不够可以再加上允许原码/反码作为有符号数表示——这两者是ISO C和ISO C++都明确支持而其它大部分语言规范都与之矛盾的东西。
标准化的一个重要作用是取得共识,避免一些重复工作,提升可移植性,减轻用户(包括实现者)的负担。若标准被架空而没有被实际使用,标准本身就失去了绝大部分意义。
这里的一个反面素材是C#,.NET的实现都出到5.0了,ECMA-334到现在还是当年2的版本……(还有C#里把finalizer叫成destructor的奇葩导致混乱的说法在ECMA里被纠正了,MSDN上仍然将错就错。)
啥,ECMA是区域性组织,不够权威?——人家现在叫ECMA International好不好。不过不够权威好像是能坐实点,C++/CLI在ISO标准化被英国的意见驳回,ECMA就通过成ECMA-372了……
那么这里就拿C++比较好了。同样是ISO/IEC JTC1/SC22下的工作组,两者的产出看似类似,效果大相径庭。
而敢无视WG21的实现——据我所知,一个都没有。即便GNU早年隐晦地表达过和标准划清界限,现在来看在C++前端是口嫌体正直了。反观GNU C,这个效应就弱得多。
简而言之,ISO C虽然整体上是有效的,但是对于语言实现者来说,效力略为不足。
概括起来,这有两方面原因。
其一是WG14本身的活动没有WG21强调“open”。WG21的文档早就被公开多年,而WG14的没记错的话到2012年才被公开。
可能是由于C++本身的复杂性以及历史教训(轻率引入export和dynamic exception specification)需要避免消极的design by committee的影响,参与C++工作讨论的非委员会用户活动非常显著,Google Groups里讨论标准提议的论坛已经开设了好几年。isocpp.org也是一例。WG21甚至在github上拥有公开仓库允许用户pull request修改标准草案的内容。
反观C方面呢?一个对应的东西都没有。
作为工作产出来看,WG14公开的paper也要比WG21少得多(得多……)
C++比起C真有复杂到这种程度么?还是说C的“社区”(暂且这么说)在传统上就“不够进取”?或者根本不愿意达成共识呢?
第二点恐怕未必。POSIX就比较活跃了。
可笑的是,维护POSIX的Austin Group和WG14之间也能出现琐碎的意见分歧。参见WG14/N1174。
原因是其二——还是C的设计。
C欠缺了太多东西。
上面的隐晦分歧就是指,是不是在标准化的接口中,允许函数返回动态分配的对象然后让用户自行释放?ISO C的意见是“不”——于是asprintf乃至strdup都不可能出现在ISO C标准库,但这个原则之前看来从来没有被正式提起过。而POSIX方面以及其它传统C用户大概不会这么认为(特别是GNU)。
其它主流语言里还有这种奇葩问题么?
而ISO C新引入的东西,很多也不是自身的设计。
ISO C11引入的sequenced before的wording,是WG21/N2239提出来的。注意,是C++的paper,C后来照搬过去了。
(嘛,C++比较激进删除了之前ISO C引进的sequence point,不过这里有个bug,漏了sequenced after这个定义,我邮件过去了,处理情况见github.com/cplusplus/draft/issues/61。)
C11同时照搬过去的还有多线程和atomic的基本概念。由于语言特性上的先天不足,C没法做到C++的优雅(虽然这词普遍恶心,但用在这里的确不错)。(看看那个奇葩的_Atomic……)
C11还不得不引入了某种意义上的“临时对象”,这种手段又是埋坑。
C11绝无仅有的东西呢?哦,比如generic-selection?我问一下,这里谁对_Generic有印象的?知道它是怎么回事的?能说清解决了什么问题,并且是怎么起作用的?有多少人在实际代码中用过?在其它语言中类似的东西是什么?
ISO C比ISO C++多出来的,特别是近来新添加的,差不多尽是广大C用户都难以认同的琐碎玩意儿……
WG14,请不要玩脱。
3.用户素质
本来我在这里不想攻击任何人。但是C的用户在辩护语言和实现表达自己的观点时,表现出来的槽点明显比其它语言的用户多得多,并且不少自以为得计,优越感爆棚。忍无可忍。
在这里起到负面影响的用户都可以概括成不同程度的“不懂装懂”“在不熟悉的领域里瞎BB”“误导”,不过可以分成那么几类(也有不少复合的):
a)原教旨主义
认为C是程序设计语言发展历史上的“正统”——却连老祖宗ALGOL的地位和影响都不知道,甚至和亲爹B语言之间的差别都一问三不知。
b)盲信
不管可以引证的依据和理性思辨而盲目认同一些经不起推敲的观点(比如C比起其它高级语言总是“性能高”“开发效率低”)。
c)无知
缺少其它语言的使用经验却臆测行为和实现。甚至对C自身的基本概念(比如“对象”“左值”“未定义行为”)都说不清楚。
d)不独立思考
缺乏怀疑精神,对符合固有印象的说法来者不拒,却不考虑理由。“我听说”……“人家×××就是用C写的!”
e)缺少专业基础
没有PLT常识,对其它语言品头论足,对其中和C设计不同之处——而不是不足之处做出非理性的批评。
从经验上来看,这些用户中最核心的部分同时是UNIX的脑残粉——包括一些只用过Linux然后把自己包装成UNIX粉的。
这些用户,绝大多数都说不清楚C(也许还有UNIX)历史和发展方向之类的详细问题,要提一些现有实现的缺陷和可改进之处都支支吾吾,甚至说不清楚为什么好用,满足了什么需求(某些果粉都比他们更强一点)。
脑残粉本身不足一提,不过当一些“知名人物”也具有这些特质之后,他们就好像找到了什么被撑腰的了——却不知道很多“大师”在评论这里的问题上很多也是半外行,跟普通用户无异。
4.专治各种不服:有谁自认为对C本身了解更深刻而全面反对以上观点的(特别是挂名在WG14内的——有么?),欢迎对号入座战个痛。

EOF