正确地黑C

Created @ 2014-07-29 09:01, rev v5 2015-04-12, markdown @ 2015-09-14.

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

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

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 这个定义,我邮件过去了,处理情况见这里 。)

  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 内的——有么?),欢迎对号入座战个痛。

增补 1 :

Appended @ 2014-07-29 15:58.

  既然提到了有符号数问题,这里加列个草稿。虽然算不上 C 的问题,但不少 C 和 C++ 用户都仍然对这个问题稀里糊涂以至于设计出渣接口。

  对于 C/C++ 以及其它大部分语言来说,不管是有符号数还是无符号数,首先指的都是刻意设计用硬件上具有特定表示的整数数据类型,即有存储空间大小和范围限制的、值的表示连续的定长整数。 从类型系统的角度上来说,对于表示算术操作的类型而言,类型限定它有意义的取值范围,同时可用于决定它具有的操作。

  这里的整数类型和数学中的整数显然不同——最显然地,它是有限集。但关于操作上的不同就经常被有意无意地忽略。大多数情况下这不是被期望的。

  实用中,有符号数和无符号数的不同在于,无符号数具有确定的表示方式,且尽管在不同的体系结构上大小不一致,但都遵循2^n的模算术。注意和数学中的整数的一般算术操作的差异,例如模算术减法在不够减时结果会回绕(wrapped) ——比被减数更大。

  而有符号数则没有这样普适的一致性:至少可以有原码、补码和反码表示。 ISO C 和 ISO C++ 都规定:允许有符号数使用三种表示方式(之一)。占用空间相同的有符号数的表示不保证可移植性。同时,有符号数的操作导致超过表示范围(溢出)则行为未定义。而对于有符号数的一些转换和位操作是实现定义行为。 而与此相反,无符号数的性质要确定得多。在确定模算术的意义上可以推导出一些方便的性质,例如无符号数一定不会溢出。无符号数也不会有上述有符号数操作的实现定义行为导致的实现差异。 从实现角度上看,通常无符号数更容易被硬件高效地实现。只支持有符号数而不支持无符号数操作的实用体系结构似乎从来就不存在。

  从抽象角度上看,无符号数的上限比同样大小的有符号数更大,所以在确定不需要符号时,更有利于正确的数值表示。

  因为上述原因, sizeof 的返回值 size_t 类型确定是无符号数类型。类似地,在 C 和 C++ 中,除非必要,应当避免使用有符号数。

  相对地, Java 使用(范围被硬编码的)有符号数而放弃使用无符号数。这在一般意义上是一种错误的(类型系统)设计:损失功能且带来了更多的麻烦。

  除了失去上述无符号数确定的性质( Java 倒是确定了有符号数位操作的语义),这导致一些只需要无符号数的场合,同样的大小范围损失了近一半,并且导致程序更加不清晰:用户没有办法表示“我这里就只需要一个非负数”“你应该能预期结果是个非负数”(天杀的 Java 还没 typedef 和宏……)。

  只有有符号数导致 Java 需要引入 >>> 操作符以便区别对待符号位,在另一方面导致了语言的复杂。缺少无符号数还导致一些应用上的麻烦——例如实现随机数生成器、通信协议或者图像处理算法之类。

  Java 抛弃有符号数的理由据信是“无符号数更复杂,因此不需要”——根据上面的分析,这是典型的扯蛋。一个比较可能的实际理由是对无符号数“莫名其妙”地回绕的无谓恐惧,特别是对结果比较操作上。但事实上,任何一个对这些语言中算术操作有清楚了解的用户,都不应该陷入这种陷阱,特别是编译器能轻易检查出对有符号数的不保证兼容性使用的情况下。

  大概也正是因为如此, C# 没有在这里“借鉴” Java ,还是补充了 uint 。不过, C# 的语言规则导致 uintint 诊断消息设置有些神经质,大量用户代码中还是充斥着本来不应该出现的 int 。设计者应该只是确信 uint 不可少,但只是有符号数的补充,而非尽量鼓励使用的对象。这和 C/C++ 的设计非常不同,也并没有完全发挥无符号数的好处。

  于是话说回来,为什么在 C/C++ 这样支持并且某种意义上更鼓励尽量使用有符号数的语言中,为什么还是有人非得喜欢 int 到底呢?

  恐怕原因和上面的一致。简而言之:这些人(包括某些抽风的语言设计者)自己就没把这种受限的定长整数算术操作搞明白,又欠缺谨慎、耐心和全盘考虑,可能还有些偏执。

增补 2 :

Appended @ 2014-08-11 16:53.

  关于C/ C++ 和汇编语言的关系。

1. C 是 C , C++ 是 C++ ,汇编是汇编。

  ISO C规定了允许 asm 关键字嵌入汇编代码作为扩展,但 C 并不依赖这项特性。

  可能由于 Bjarne Stroustrup 等对阻止语言分裂的立场(如果需要另一个例子,可以参照对 Embedded C++ 的观点),ISO C++ 没有 ISO C那种“扩展”的概念, asm 关键字直接是正式的语言组成部分。

   C++ 的 asm-definition 的含义是实现定义的,但同样也并不见得里面就是汇编语言——例如, Cheerp 使用 asm 内联 JavaScript (虽然后来改为了 __asm__)……

  不管是 C还是 C++ ,作为实例的不支持内联汇编的实现也不难找,例如用于 ARM 和 x64 的 VC++

  所以即便是 C ,指望和汇编互操作在语言层面上缺乏一般的可移植性。

2.作为实现的汇编语言和对象语言(例如C或 C++ )的关系。

  一个常见的误解是“ C 和汇编对应”。事实上这种保证根本不存在。对于其它高级语言也大抵如此。 尽管现实大多数 C 或 C++ 的实现使用汇编作为中间表示,没有谁强制所有实现都遵循这一点。于是这里需要排除不使用汇编作为中间表示的语言。也就是说,要考虑这点,就不是讨论 C 或 C++ 语言本身而是具体实现了。

  需要肯定的是,对于某个实现来说,作为中间表示的汇编和 C 和 C++ 的某些操作的语义接近,有一些现实意义,主要在于互操作性的清晰简便。

3. C 和 C++ 在这里的差异。

  注意,对于使用 C 和 C++ 的用户,上述需求都存在并可以实现。 C和 C++ 实现在此的主要差异在于, C++ 提供更高级的抽象,这些操作在 C 中往往没有对应,所以这部分在这里没有比较的意义。 从实现的复杂性来看, C 和 C++ 公共具有的一些操作往往比较能直观地被理解。 C++ 的其余部分涉及到更多实现细节处理起来较为困难。如果使用这些操作,的确会导致一些现实问题,特别是 C++ 实现的 ABI 往往比 C 实现的更混乱。

  但是,在需要考虑这类需求的场合,使用什么操作是可控的。如果只使用 C++ 和C中类似的特性,那么问题实际上差不多复杂。

  差异只是用户对 C 和 C++ 的实现(即便排除 C++ 中实现起来看似不够直观的部分)理解有所不同而已。

  也就是说,到底还是用户自己的原因。(不作死就不会死……)

  至于运行时依赖问题导致 C++ 比 C 在这里欠缺可用性,这是另外的话题。

4.还要注意的是,上面这些到底只是“接近”,并不是所谓的普遍存在的“对应”。

  这个问题细说起来可以展开很多……

  有一个目的上出发的根本观点:设计可维护的程序,依赖的应该是接口而不是接口的实现。依赖实现的 hack 只是妥协而不应作为可靠的常规手段。

  对于能够使用这些接口解决的问题,不需要也不应当依赖它们的实现。

  一般地,高级语言的规范自身通过描述程序的语义和/或行为,事实上给出了一套更高级普遍的接口。 ABI 规范作为适用于二进制互操作的低层次的接口,相对来说提供的是语言规范的实现,以缩小适用范围为代价,补充语言规范中没有限定的一些细节。

  C / C++ 这类本机实现的 ABI 一般可以分为体系结构和编译器相关的两部分。可惜某些实现不给出清晰的、友好的、公开的规范,拿黑箱让用户自己猜……活该谁倒霉呢。

  没有足够能参照的 ABI ,所以需要互操作时,不得不通过观察生成的中间代码来预测实际可能的程序行为,这也不是不能理解。这种不保证可移植性的程序行为,语言规范更加管不着。

  (这里中间代码说的就是汇编,其它类似的还有 JVM bytecode 、 CLR IL 之类的中间表示,不过传统上汇编占这种情况的绝大多数。)

  但在二进制互操作以上的层次,仍然依赖上面的手段——无理由、无益地依赖实现——就有些匪夷所思了。 或许实际情况是,这些开发者根本就对这些原则性策略缺乏清醒的认识。

  造成这种情况的理由可能就是在于方便依赖“和汇编对应”之类实现细节在作怪——至少表面上来看,只要“会”汇编,对生成代码有个大体的经验,很多时候就能弥补对不清楚接口导致无法预料程序行为的不足了。 这显然是类似“撞大运编程”的错误姿势。只是大部分这类用户甚至不会考虑体系结构之间的可移植性,所以这种错误认知也不容易起到负面影响。一旦有机会,坑的会是谁呢。

  由于种种历史原因(比如说早期编译技术比较初级导致编译器一般不会进行复杂的变换优化), C 的实现往往给人一种在接口规范之外也容易找到“对应”的印象。不过,现代的实现对此已经渐行渐远了。 就这点这当然不能怪 C ,那么。这种破事说到底也只能当作某些误导作祟导致某些用户技能树点错学岔了产生误会了。

  此外,对于学习语言来说,这种错觉的危害一般更大。考虑到这里的问题,先学汇编再学C这种路线应当被慎重考虑,不推荐一般用户以免形成先入为主的观点。当然,这是另外一个问题了。

5.附带的其它结论。

  除了上面的需求导致的现象外,一个副产物是, C++ 的抽象让不善于使用正确姿势思考问题的新手望而却步了,其中不少可能就学看起来“底层”的 C 去了……

  对于 C++ 来说,的确可以算 teachability 的耳光响亮。不过,在“底层”方面的逗比用户也因此更少了,也许也是好事。

  (另一方面,“纯 OOP ”逗比用户被 Java 之流给架走了不少……不过剩下来也许还有两方面都特别逗的用户,那就不怎么样了 wwwwwwwww ……)

增补3:

Appended @ 2015-08-09 10:00.

  突然一时不爽,就拿 C 和 C艹一起黑。当然,还是 C 的锅。

  总体属于设计问题:没理由的不一致。

  具体例子有很多,不过 C 就有并且 C艹照搬的垃圾设计就稍微少一点了。这里值得一提的是逗号表达式——体现无能然而搞得莫名其妙。

  逗号表达式有鸟用?其实说白了就是在本来该写表达式的地方想硬塞下语句而已。严格地说,意味不明地区分表达式和语句这种烂设计并不限于C而是某些Fortran/ALGOL系的硬伤,但是在C里还有特别恶劣的其它影响:搞得规则不伦不类。逗号前后的表达式是不是能确定求值顺序?因为存在逗号表达式,答案是不一定。可能有人说把其它用逗号的地方(像是函数参数列表)都改成从左到右就一致了,然而这样就损失了“放弃明确顺序而让实现自由优化顺序”的表达能力,和Java之流一样蠢了。所以锅在逗号表达式顺序自己。一旦允许表达式套语句,逗号表达式就是鸡肋。事实上也有这样的扩展——GCC的语句表达式。

  在 C++ 中,由于逗号可以重载,却不提供操作数求值顺序保证,这种混乱更加明显。所以考虑到误会的风险以及简化问题,一般回避重载逗号,甚至于泛型算法实现中假定不存在逗号重载(然而大多数用户是直接无视了)。但这也就是无法直面从C继承的锅的鸵鸟政策。另外,有 lambda 表达式后,非重载的逗号就更加鸡肋了——即便是不重载的 C++ 逗号表达式,因为更加不像C那么“有用”,所以更罕用,在更复杂的语言规则中导致误会的风险也更大。

  上面说了C( C++ 也一样)允许刻意不限定求值顺序。然而这并不是直接好用的语言特性。 YBase中 的 ydef.h 提供了宏 yunseq 用函数模板包了一层才稍微清楚,然而对于 void 表达式无能为力。而如果逗号表达式一开始就一如函数参数列表一致地提供非限定顺序求值的语义(至于需要限定字面顺序的地方,就用“语句”——比如说,使用分号替代逗号),那么本来就不需要这样费事了。可惜事实不是这样。

3.1

Appended @ 2015-08-09 10:04.

看起来逗号表达式如何“有用”,也算一般理解了。

3.2

Appended @ 2015-08-09 10:07.

yunseq的实现( C 就别想了)。

增补4:

Appended @ 2015-08-09 10:54.

  还是关于烂用户的事——但也不全是用户的问题。

  用户理解得是否正确清晰而不会造成破事,除了语言设计,语言规范的质量自身也是重要参考因素。 首先需要澄清,我并非觉得 ISO/IEC 14882 作为技术文档的质量很好——相反,它相当罗嗦,有不少 bug (虽然被我和其他少数人不断修正但也不断会窜出新的),并且总体来说并不怎么容易看。

  而这些不足也同样适用于 ISO/IEC 9899 ,除了不断窜出新 bug 这点(嘛……理由是 ISO C 更新实在太少)。

  当然,这两个都够吊打体例不够清楚到技术标准程度的 spec 了,比如 JLS现在的版本 甚至比ISO C++ Clause 17 之前的部分还长,以后我倒是看看谁说 Java 语言规则简单然后就能啪啪打脸了)和 M$ 的 C# specECMA 那个还好不过显然过时放置 play 中)。

  但是,虽说这两个很多地方都不怎么样,一些关键的地方还是有高下的。

  比如说,关于重要的程序执行模型,也就是 ISO C 放在了 Clause 5 ,而ISO C++ 放在了 Clause 1.9 的内容。(且不说C11在此照搬了大段 C++ 11的基本概念)尽管前者定义清楚了一个实用的 observable behavior 的概念,后者的内容比前者还详尽得多。这导致了关于 conforming 问题上后者更清晰。

  一个比较直接的实例是,关于未定义行为“在什么时候发生”的破事。

  实际上这里即便是 ISO C ,本义也是清楚的。但是就会出来这种逗比问题

这是一道精妙的题目还是一道傻逼题目?

问题出自:大型互联网公司为什么会出一些质量低下的校招题目?

http://pic4.zhimg.com/9dc0fadbb39515914415411f958a8cb7_b.jpg

有“我们微软”的RSDE vczh 称:

“但是就截图第七题来讲,我发现只有A选项有可能得到4,其余三个选项无论选取哪种顺序都得不到4,很屌。这是一道精妙的题目,不叫傻逼题目。你们不要看见

考++就说这道题不行,++虽然有undefined behavior,但是这道题目的考点是你知不知道undefined

behavior下面能允许的东西其实也是有限的,懂不懂得穷举他们。”

  这几乎是常识性错误。vczh的脸自然(?)已经被回复打肿了(只是看了zhihu的尿性所以全文按合理使用的形式引用,粗体略):

张景旺,图灵的机器上,能否承载邱奇的梦想

呵呵一笑百媚生、舒夜、知乎用户 等人赞同

不同意你们的说法,题目改成“可能为4”的话,那么4个答案都是正确答案。undefined behavior出现什么结果都是可能的。

题目改成:在我们微软的编译器编译之下,下列哪个表达式的结果为4?就是精妙的题目了……

或者更直接一点:

你是信C++标准?还是信VC++?

A:信VC++

B:信标准

C:我要进C++委员会,将来我说了算

D:老子编程从不想标准还是编译器啥的,生成可执行文件就是成功……

这样就是一个绝对精妙的问题了!这样正确答案就很明显了,而且还能筛选出“我们微软”想要的人才,毕竟这三十年来我们微软也以无视各种标准而出名……好传统,要保持!

  引用这里的一些评论。首先是垂死挣扎:

vczh

显然undefined behavior是有范围的。多个++出现的时候,标准说得很清楚,尽管++发生在什么时候不知道,但是有多少次++就加多少次1是肯定的。因此就算你写a+++a++,a也不可能从0变成3。

2014-10-13

嗯,我就先不管“标准说得很清楚”在哪了——虽然其实这就是就这个例子我之后要讨论的内容。

紧接着被直接打脸:

薛非 回复 vczh

显然undefined behavior是有范围的。//无知

2014-10-13

薛非 回复 vczh

多个++出现的时候,标准说得很清楚,尽管++发生在什么时候不知道,但是有多少次++就加多少次1是肯定的。//臆测

2014-10-13

薛非

因此就算你写a+++a++,a也不可能从0变成3。//妄断

  下面没存在感的附议略。

  看客也有明白人,虽然引用的论据强度不够:

2014-10-13

Jimmy shen

In computer programming, undefined behavior refers to computer code whose behavior is specified to be arbitrary. It is a feature of some programming languages—most famously C and C++ .

这次轮子哥估计要跪了。坐等二位大战。

2014-10-13

  不过稍微有一个需要注意一点的(连续三个评论,“知乎用户”应该是两个人,下面标记为A和B):

知乎用户 回复 薛非

我觉得您的IQ可能需要充值了,我特意去翻了下手头的2003版ISO,如果是C或 C++ 的话,这个所谓的undefined behavior要保证不与前面的运算符章节冲突,只存在两种可能……

2014-10-22

知乎用户 回复 知乎用户

我觉得您的IQ可能需要充值了,你根本还不知道什么是undefined behavior

2014-10-25

知乎用户 回复 知乎用户

undefined behavior再undefined也要符合标准在其他地方对这个运算的限制。只强调一个undefined却不看其他限制条件,这叫玩儿文字游戏。

2014-10-26

  vczh的脸我不感兴趣(看了这个问题我正再次考虑他是不是有资格和方是民和王垠等人相提并论),然而最后一点需要澄清。

ISO/IEC 14882:2003

1.9/5 A conforming implementation executing a well-formed program shall produce the same observable behavior as one of the possible execution sequences of the corresponding instance of the abstract machine with the same program and the same input. However, if any such execution sequence contains an undefined operation, this International Standard places no requirement on the implementation executing that program with that input (not even with regard to operations preceding the first undefined operation).

  看括号里的。好了,“特意去翻了下手头的2003版ISO”那位的脸已经肿了。

  虽然上面没有先吐槽翻了一下2003版的ISO如何得出C的结论,不过翻了下ISO C的确是没有那么方便打脸的条款的(当然,这个问题的结论不变)。

  不管怎么说,翻差不多同样晦涩的ISO C打脸却不方便,收益不够,相对于ISO C++ 来说也算是个黑点了。

增补5:

Appended @ 2017-02-04 01:53.

  还有不容忽视的一点在历史上的影响——这是极坏的榜样。鉴于这方面罄竹难书,所以之前有意忽略了。不过还是有不少历史材料可以补,例如这里的评论

  还有一个特别值得一提的破事,也算是跟前辈 BCPL/B 不同的槽点——“数组”——设计问题因为上面有提到所以论外,光说这个名字好了。本来好好的关联数组(associative array)的坑被占了不说,还搞得之后叫“向量”(仔细考虑下数学上什么叫vector)的东西浑身不自在(Java的array倒是叫Iliffe vector)。A.Stepanov为什么要用std::vector来命名这种“动态数组”,似乎也不是愿打愿挨而是想不出什么好主意了。

results matching ""

    No results matching ""