Create a gist now

Instantly share code, notes, and snippets.

Embed
What would you like to do?
贴吧相关 FAQ 、资源链接和其它附带清单。

贴吧历史资源整理

吧务操作

http://tieba.baidu.com/p/4171474077

日常(经)整理

http://tieba.baidu.com/p/2286985797

头衔

http://tieba.baidu.com/p/1293939748 C++吧的头衔勋章应该能改了吧

http://tieba.baidu.com/p/1294795004 【征集】C++吧头衔名征集帖。

http://tieba.baidu.com/p/1297445553 C++吧头衔碉堡了

http://tieba.baidu.com/p/2124684426 咱就不能有个正常点的会员名称吗?

http://tieba.baidu.com/p/2484488705 请问咱们吧的头衔亮出**cm是什么意思呢

http://tieba.baidu.com/p/2497542074 新人刚来 头衔好奇怪…………

http://tieba.baidu.com/p/2190079071 新人报道,话说这神奇的头衔是怎么回事→_→

http://tieba.baidu.com/p/2601257047 我非常迫切的想知道,咱吧里的会员头衔是什么个意思,亮出...C

0cm

<15cm

=15cm

似乎是 http://tieba.baidu.com/p/1820200287 ,已炸。

>15cm

关于头衔的外吧评论

会员名

个人 FAQ

头像

http://tieba.baidu.com/p/1291218344

学习历程

http://tieba.baidu.com/p/3388459316

基本法

操作示例

部分引用置顶索引贴 http://tieba.baidu.com/p/2678895259

例 1

[吧务操作]
解释备案。
依据现行吧规(git版本81e9aa0a0eccc127fa87ecb99b506b65c3c332ea,最后修订20140322),对本帖操作解释如下。
A.鉴于由LZ本人提交给吧务的证据,兹裁定LZ在本贴以前违反以下条款:
3.10。
B.兹裁定本贴LZ违反以下条款:
1.1、1.3、1.4、4.3、B.7、B.8。
C.违规认定和正文条款处理意见:
由条款2.5,判断LZ已经构成违规。依据2.1、2.3、2.5条款,对LZ违反3.10的处罚已经先期执行,对其它违规行为暂予保留2.4条款执行权力。
D.B条款处理意见:
操作者认为此贴意义不足以挂城门。
操作者认为此贴可以于不特定时间不解释删除,但暂缓执行。
[吧务操作]
补充30L说明。
E.25L建议按2.1条款仍然有效。不确定时间生效。
F.鉴于保留处理不属于不解释事项(2.9条款定义),兹解释保留暂缓处理的理由如下:
F.1 涉嫌多项违规,在30L明确行为前可认为不宜直接合并处理。
F.2 涉嫌傀儡账号(2.4条款定义)。对傀儡账号是否需要和LZ此帐号合并处理有待审议。
F.3 涉嫌的事由涉及操作者以外的吧务。尽管事实清楚,操作者仍然认为有必要转达处理现场。通知已经发放,待结案。
F.4 操作者认为此案例具有现实教育和阻止继续违规的意义,可以重申私信骚扰对于合理诉求无效这一常识性问题。
F.5 操作者认为此案处理过于仓促,不足以发挥惩戒的威慑作用;操作依赖F.1和F.2,仍不足以构成判例。

例 2

[吧务操作]再次提醒,暴力膜吱也须遵守基本法。
作为管理者,吧务希望吧友能够亲切友好的交谈,或者至少能坦率交谈。因为一般来说,会谈是有益的。
本吧对主题内容的要求和限制整体上并不严格。回复中个别人交换了意见,即便和主题内容无关,吧务原则上也没有必要干涉。当然,明显超出必要的限度,过于充分地交换了意见而造成不愉快,不管是否增进了双方的了解,都可能适用吧规处理而限制权利。
在合理合法的前提下,我们尊重吧友的言论自由权利,赞赏强调遵守规则的讨论者。对于非明令禁止以避免损害吧友利益的个人观点,我们持保留态度。在规则限定外,若产生误会,只能表示遗憾。
我们历来会对在规则之外没事找事、套用双重标准、道德绑架的讨论者表示极大的愤慨,并对可能发生的违规行为表示严重关切。特别地,单方面主张自身权利而无视他人权利者,不能置之不理。对所有这些不利于正常讨论氛围的情况,吧务保留做出进一步反应的权利。
考虑到出错可能在所难免,我们有限地允许关于言论中的不当内容,并留出适当时间做出修正。必要时,吧务可能限定时间限制。在进一步回复后,我们将重新考虑这一问题的立场。
无视客观事实、颠倒黑白和煽动群众分裂吧友阵营是我们万万不能容忍的;这是不友好的行动,是可忍孰不可忍。
我们敦促某些挑衅者悬崖勒马。执迷不悟者,不妨拭目以待,希望本吧的立场给你们能留下深刻印象。这对社会资源来说是也有益的,所以道德制高点基本上也自然失效了——换言之,请不要继续无谓地挣扎;否则,请自行负责由此引起的后果。勿谓言之不预也。

题外话,这样一说为什么到此为止这里瞎bb跳脚的不是“路人”就是有据可查作奸犯科嫌疑者?或者说“路人”就能和当权者划清界限于是乎了不起?也罢,看了这几个所谓的“路人”,要是真能代婊路人的平均水平,我以后都不好意思拿自己当路人逛别的吧示众了……
用祈使句前请搞清楚自己的能耐,不要别人科普权利和义务的有关常识。

借LZ贴,以下对所有自我意识过剩目无法纪好为人师没事瞎bb的逗比宣战。

(因为这个话题太low所以不好意思开新主题。看LZ先前积极参与的态度,放在这里大概也不算污染主题。)哪个想对号入座不满的,除了自挂到http://tieba.baidu.com/p/2390940218,到这里回复能澄清自己立场的不违反吧规的仍然保留。不过既然有够多敬酒不吃吃罚酒/给脸不要脸的例子,之后我也懒得警告,直接专政到能认识到错误、自觉闭嘴为止。到底战斗力过剩,拭目以待。

 [吧务操作]鉴于此类主题的内容和影响,拿基础学科话题解释而不是纯水的,注意尽量做到以下几点,否则同吧规4.6并从严处理:
1 明确讨论的具体对象的外延,以及和主题之间的关联。
1.1 一般应指出具体的学科分支以及直接影响,能让读者推断重要性之理由。
1.2 区分该学科的理论和课程。能让读者明确了解知识点依赖和学习的困难程度。
1.3 明确讨论的理论分支和整个学科的联系,严禁以偏概全。如有必要,明确该分支适用的范围。
2 确保可能发生争议的观点可以查证。
2.1 区分原创研究和公认事实。
2.2 没有把握或存疑的观点应明确指出。
2.3 必要时引用文献。

惩治戡乱纪要

例 1

http://tieba.baidu.com/p/3716248078

(因为内文含文本转义记号,分别加注。)

这个主题保留。

正好挂人,我懒得开新主题污染版面。

首先,关于LZ的一些“为什么学”的问题:有些人蠢惯了,也喜欢你们跟着一起蠢,这样谁都“方便”。然而只要制造了些玩具扩散了,擦屁股的人还是要有的,只不过怎么也轮不到没能耐的蠢人。

yucwei:        回复 幻の上帝  看来你是很懂了,那我问你一个问题,解决一个问题是用简单易懂的方法好,还是用只有你自己明白的不知道从哪里抠出来的歪门邪道的方法好?你所谓的懂就是一些不入流的,拿不到堂面,不值一提偏道技巧而已。

做个典例挂着。楼中楼回复不够显要,重复一遍:

幻の上帝:   回复 @yucwei   :你爱怎么解决就怎么解决。但是当你提出一个足够蠢的问题却还掩饰欺骗别人说这不蠢时,你浪费的别人的时间和精力就比你无论怎么解决的价值更大,那么就老 实挨打吧。好好反省一下懂一般需求的正常用户和只懂其中一小撮子集的逗比用户评论解决一般问题入不入流这种破事上,谁更有说服力呢。

然后要补充的是分析一下行为动机和额外结论。
先说结论:蠢货多数是牺牲品,然而另一方面又是祸害——只是因为收拾起来太麻烦了所以才能苟延残喘——这里应该也不例外。
分析理由:
首先这些人入门的时候就被某些现成的蠢货推销了挂羊头卖狗肉的黑材料,以为他们“学会”了某些真的“有用”的东西。
恰好C这样的语言因为设计和历史原因,特别适合造成这种错觉。实际上他们不是学会了真正被普遍承认的、在现实中发挥作用的C(羊头),而是一种按照个人智商局限下曲解以后的方言(狗肉)。
不巧,这些黑材料的作者自己并没有能耐实现语言(写个编译器啥的),自然更不可能改变工业界会用的解决方案了。
而某些黑材料又特别乐于或者只擅长于把C++阉成那种不伦不类的玩意儿。
于是这些黑材料的读者就被迫在微妙对不上号的现实实现和黑材料本身做出取舍。可能智商局限性会传染,缺乏其它补充材料对比时,就更容易盲信了。
比如说,当发现编译运行结果不符合预期,就自行脑补“原理”,以为靠自己眼前的个别现象就能归纳出普遍规律;甚至选择性无视,忽略和期望不符的事实自欺欺人。
可惜不管语言还是语言的实现,都是人为设计的结果。这种一厢情愿的学习方法一开始就是图样逗比的笑话。于是当没有正常靠谱点的材料撑腰又不知道对比和直面无知时,姿势当然正确不到哪里去。
对着C/C++这类敢于规定UB的语言这样YY,不仅是给自己挖坑,还给别人制造擦屁股的成本。(重复,智商局限性有传染的风险。)

无知是没问题,卖蠢还真是敬谢不敏。

然后,关于陈良乔。
我为啥要说这个人呢——大概是因为某些方面也比较极端吧。
@人类的潜力 :        回复 用微笑释怀岁月  :如果觉得自己技术可以,推荐c++primer,或者2本都买,再给你推荐本国内牛人写的《我的第一本c++书》
正好有人提到了,看来也不算有意误导那就回一下。
首先,他那本《我的第一本C++书》的逗程度应该还是有点知名度的。
(其实以前也说过了:http://tieba.baidu.com/p/1519041467。)
要真以这本书的水平把所谓的“牛人”划分成一个等价类,我觉得这快到人身攻击了。所以就吐槽了。
客观地说,这人应该也不是完全有胆子像谭×自信爆棚乱跑火车,所以有时候翻译Bjarne Stroustrup的话啥的也没敢夹带私货。不过原创材料基本就只是笑话了。
这个人和我没啥私怨,不过给我和许多其他人的印象很糟糕。我能记得他的唯一一个突出优点就是说话能不带脏字,而骨子里十动然拒就是死皮赖脸,被指出硬伤以后就是当作没有造成伤害判定。换句话说,厚脸皮。
具体一点,要八卦的:
http://bbs.chinaunix.net/thread-3692320-1-1.html
http://bbs.chinaunix.net/thread-3772840-1-1.html
当然,图书市场编辑水准啥的这个也习惯了,另当别论,这里不多展开。
另外一个八卦是陈良乔和某个不入流的民科以下水准自称“人宇科学”的奇葩团体有一些看来说不大清楚的关联。
看来对一般的中老年人都没啥传教效果……这使我对其信众的素(智)养(商)判定降低了30点偏差值。

TODO

http://tieba.baidu.com/p/5521840151

贴吧官方作死

就这点斤两也想 70W 大戏,呵呵呵…… (结果是 4W 都没人要……)

“很遗憾地告诉您,您因不符合我们的要求申请未被通过。可能在以下方面需要加强哦~”
呵呵……
“1、提高您的活跃度,多登录,多发言”
请指教每月90%登录的Lv14的2.8w贴还差多少?
“2、提高您的发言质量,积极参与有意义的言论建设,不要为刷等级而灌水”
咦这里有谁敢刷等级灌水?
“3、使用文明的用户名”
不文明的用户名不直接干掉居然还能留着允许点申请吧主?

待定整理内容

待补充

const/常量表达式。

参考:

https://github.com/FrankHB/pl-docs/blob/master/zh-CN/variables.md

指引和科普

文献相关

C++入门书?Bjarne Stroustrup的The C++ Programming Language,虽然我也没耐心看几百页就去直接读ISO C++了。

嫌难就找Bjarne Stroustrup的Programming: Principles and Practice Using C++,对多数人来讲应该更容易点。The design & evolution of C++这本不是入门的,但研究语言设计理论的,可以当消遣性历史读物,用于通过自测不看书之前的理解是否对路帮助评判自己悟性如何。

以下只能给出常识性评价。

首先,权威文档是 ISO C 和 ISO C++ 。有错误可以翻 open-std.org 的 defect report 和 paper 考古。其它靠谱点的书基本都有 errata ,不过相对没那么容易及时修bug。

C的话就是 K&R 的 The C Programming Language ,不过也可能有一些错误,所以随时准备考虑 ISO C 修正。

C++入门的除了 BS 也就 Scott Meyers 的书稍微能看点。 Herb Sutter 和 Andrew Koenig 也算稍微高级那么点的话题的靠谱的作者(也包括一些关于 C 的说法)。这里提一下 Accerated C++ 风评还可以,但是应该比较旧了。相对来讲一些讲C++惯用法的书([More] Efficient/Exceptional C++)和以及讲C相关的书如 C Traps and Pitfall 这种相对不那么容易过时,特别是后者——因为语言在这方面变动比较少。

不推荐 C++ Primer 的原因是 Lippman 的文笔以及一些其它编排问题。以及读者认知偏差会比其它正经经典书更多。

Primer 后带 Plus 的不管是 C 还是 C++ 都是著名李鬼。作者应该不太懂关键知识点。

谭×之类的国产作者写这类书一般不作考虑——如果你不想返工重来一遍的话。尽管书本身的销量可能最多。

另外这里一个经验之谈是,如果一个作者既写 C++ 又写 Java 或者其它语言的基础教材,那么基本上至多只会有其中一门语言稍微靠谱点。比如谭×也就是写 BASIC 的书还行。当然,更可能的是没一本书靠谱。因为写靠谱的作者需要同时对不同语言的 spec 有深入准确的理解,这类作者一般不会去有空写这种书。(换做是我,要不是需要重复科普废话实在太多,带人灌水 paper 都来不及呢……)

如果有能力读原著,那就考虑直接读原著,因为译者的下限会把书的内容质量下限再过滤一遍,而译者普遍水平比坐着更差,虽然不排除个别还算靠谱的。尤其应慎重选择教科书的译著以免先入为主。裘宗燕的翻译质量整体还行,但有些术语明显偏离惯用法。其它自己搜风评。

TODO 可以加不少链接。

范型

计算理论

所谓语言入门基础说

所谓“语言就是工具”

什么东西适合用 C++ 写?

http://tieba.baidu.com/p/4054226083

@回复 MaDDove :

仍然需要 FAQ ,列个提纲。
什么东西适合用 C++ 写?
现有的参照:http://www.lextrait.com/vincent/implementations.html
为什么?
总体来说理由有两类:
1 不得不用。用其它语言,现实中不够具有可操作性,不是不可能,就是不方便到基本不可能。
有些东西,不管是在什么系统上几乎都只可能用确定的一种或几种主要实现语言。
通常贸然换用其它语言会遇到这种情况:有更大的风险遇到别人都没遇到过,又没法保证自己解决的问题。
如果加了虚拟机之类的间接运行时可能稍微好一点,基本上用其它语言直接扩展也比较麻烦。
这大类理由也适合其它语言,不限 C++ 。
一些大型的应用软件(典型地如浏览器)、应用虚拟机、可复用组件(如游戏引擎)和以及许多特定领域的应用,传统上主要的核心都是建筑在 C++ 上的且现在仍然占据绝对优势(历史上使用其它 C 、 Java 等其它语言实现的很多也迁移回 C++ ),极少有例外。
对于 C ,则集中在操作系统组件(及设备驱动程序)和版本管理控制等相对少数领域内。
还有一些领域,如数据库引擎、桌面环境等,主要实现语言不是 C++ 就是 C ,有更多机会混用。这也是很多情况下统称 C/C++ 的理由。
也有一些领域,曾经 C/C++ 也被相当广泛地使用,但之后使用更简便的其它替代,典型的就是 Web 后端(不含应用服务器自身的实现)。
1.1 历史包袱。很多项目历史代码就是主要用确定的语言写的。
1.2 习惯因素。这影响现有资源的可用性。
2.虽然也可以用其它的写,但因为需求不够固定之类的原因,考虑由此带来的(以及本来就有的)互操作性等方面的风险,综合起来找不到其它更合适的。
这点是 C++ 的专长:高效而同时具有抽象能力,支持多种范式,灵活迁移(当然前提还是用得对)。在面向应用领域的“通用”上可以说是现时(自从 Lisp 被放置 play 以后)最好的——只要你明确了设计,基本上什么都能实现得出来。同时,仍然能够保持一定可移植性。
而且 C++ 的一些设计能帮助避免做出来后发现不满足关键需求还要考虑换语言的情况(和具有 C++ 类似设计目标的,也就只有 Rust 等极少数语言,然而除了 C++ 都不成气候):例如,运行时性能坑爹——基本上用对 C++ 还不能满足性能需求的情况下靠换其它语言也不可能解决问题了。这也从一方面说明为什么很多严肃的应用会迁移回来重新使用 C++ 。
代价是语言和实现都比较复杂,没有足够的训练容易用错;用户素质普遍不够,沟通成本可能很大,导致损失开发效率。但是反过来也说明如果工程手段得当,就比较容易控制在预期范围之内。另外,复杂性同时限制了不遵从可移植要求的滥用(对比 C 很容易发现这点)。
对初学者来说,学习阶段不需要考虑上面的第 1 点原因。因为相当多基础是各个语言通用的,尽管大部分材料都不会清楚地告诉你多少内容,得自己课外补程序语言理论。而只是“会用”,或者会抄代码,难度不会差太多。自己玩玩就更不用考虑第 2 点原因了。所以 C++ 顶用和去学 C++ 没有直接的因果关系。
现实的 C++ 用户,几乎不可能只会用 C++ —— C++ 没有强到让一般用户容易代替其它所有语言的程度。尽管理论上来说可以做得到,但历史包袱和用户水平决定了不现实。于是许多时候都是 C++ + 其它某种“胶水”“脚本”语言。这算是一种妥协。
另一方面, C 的情况类似但更糟,只会用 C 基本上并没有什么卵用——就算是经典的场景你也得会 makefile 之类的至少一种 DSL ,退一步讲也至少别指望用 C 处理文本什么的了。除了这些历史包袱擅长的领域, C 的用途主要就是拿来写抽象解释器(解释器、编译器、汇编器等等)……不过现状是这些领域也正在逐步被 C++ 替代中(虽然比较慢)。
顺便, Scheme 主要就是拿来被实现的……
最后,非得学 C++ 入门的话,找尽量新的语言和实现版本(语用上表面看起来比较像其它“脚本语言”,虽然实现坑得可能会完全莫名其妙),自己反思能解决什么问题。
严格来说,类 C 语言的历史上一些基本的设计(像类型系统)都相当差,有太多随意的 artifacts ,导致很多东西要么没有,要么就是以后不很自然地打补丁糊上去的,背后的理由并不容易看出来也并不容易立刻就说清楚。(所以这里提到 constexpr 、 auto 和 decltype 什么的不理解,我也不方便一一解释。)对天赋没点到设计语言上的用户来说想像为什么要有这样设计的特性和如何去使用都是脱离真正要解决问题太远的负担。所以我强烈不推荐这些语言当第一入门语言——特别提醒, C 没像其它语言那样有那么多补丁(虽然方言特性倒是一堆),但一直只是表面上看起来简单,论坑的密度比 C++ 还大,学了半吊子基本白学还特别容易产生学会了的错觉,影响以后的正常路线。

IDE

http://tieba.baidu.com/p/4170297948

看来这里也要加个TODO。
先列提纲。
XP作死装不了高版本VS以及targetting XP的质量都烂什么就不管了,就说为什么VS不适合新手。
1.编译器是个残废:支持特性缺斤少两,bug一堆。
2.IntelliSense bug更感人,没经验的信了就等着被误导吧。
3.build system也废,没什么现成简单设置改用其它编译器。
4.编辑器还可以,但作为IDE除了上面的残废以外,大部分其它功能学C++压根用不到。
5.最小安装仍然太大,是不是装得成功可能比较看脸。

存储和存储资源管理

TODO conceptual:

  • memory 翻译成“内存”的问题
  • storage v. memory
  • RAM/ROM/...

默认 GC 的危害

GC = garbage collection/garbage collector ,垃圾回收/垃圾回收器。

一些技术性观点: http://tieba.baidu.com/p/3171730339

GC 特指非决定性(nondeterminstic) 的自动资源回收机制,主要管理在线存储资源(内存)。静态 GC 等本质上属于决定性资源回收的机制不属于此讨论范围。

GC 本质上是一种以闲置存储换取吞吐量或变通其它问题(如 ABA problem)的优化实现,不适合解决一般问题的默认手段,特别地,不适合被通用的语言作为公开接口被依赖。

GC 可省略显式释放,给一些人造成了“通用”的错觉。当这些人设计语言时,会诱导语言的用户(通常是比他们更不了解语言应该如何设计的开发者)使用“简单”的机制,反而把问题复杂化,同时让最终用户体验受损。

不够通用:因为延迟释放的特性, GC 也只能用于和内存足够近似的特性,不能像其它决定性自动资源管理机制(如 RAII )推广到其它资源(锁、数据库连接等),造成无谓的实现冗余,增加维护代码的工作量。

GC 造成的问题很多(也正因为如此在一般意义上只能作为谨慎的优化),典型情况下最突出的包括以下几类:

  • STW(stop-the-world) :
    • 回收基本上都会对响应(latency) 有负面作用,不适合实时任务。
    • 增加的时延不容易平摊(amortized) 且无法可靠地预测发生的时机,引起明显的不确定场景发生的卡顿,损害可用性和用户体验。因此 GC 也不适合对绝大多数和最终用户交互的程序,或至少容易增大实现和质量保证的难度。
    • 存在不受到 STW 的“无暂停”甚至可以适应实时任务的 GC 实现,但其它问题(实现难度、并发调度、内存占用)更严重。如 Azul Zing 使用 C4 GC ,设计为一个实例占用 “a few Gigabytes to 2 TB of memory”
  • 显著增加内存占用:
    • 在内存紧缺的环境下难以使用。
    • 可能导致更高的能耗。
    • 资源利用率低下:典型地,要实现相同的流畅响应,使用 GC 可能相对不使用 GC 需要占用 5 倍的内存,考虑能耗问题,使硬件的选择更加困难。在移动 Web 应用上这些问题尤其突出。
    • 在允许分页的系统配置中引起换页:可能增加 I/O 负担,进一步损害响应,极大降低运行时效率。
    • 更容易引起 OOM(out-of-memory) 问题,降低应用乃至整个多任务系统的可用性。
    • 某些系统如 Linux 默认使用 OOM killer 允许 over commitment 导致内存最终耗尽时行为难以控制引起比内存短缺时直接禁止分配更严重的系统可用性问题。这本质上是半吊子的系统内核级 GC 。
  • 释放资源的时机不确定导致程序行为难以控制,造成额外的风险和成本:
    • 释放内存的时机不可控,这点在 STW 体现得很明显。同时,考虑对宿主系统中其它不共享 GC 任务,也可能存在一些效果(对内存分配调度等的)不可预期的负面作用。
    • 增加回收频率会使 STW 更严重;反之,减少回收频率可能使资源非预期地占用,实际上也就是泄漏了。而启发式的回收策略本质上不靠谱,只能瞎蒙,所以生产环境经常需要人工“调优”试错。
    • 终结器(finalizer) 的副作用。终结器可能包括比内存资源回收更敏感的资源回收,实际上多数情况本应使用决定性回收策略。交给 GC 之后程序行为更不可控。
    • 附带的 object resurrection 问题引起潜在的 bug 、混乱的程序流程和逻辑并降低程序的可维护性。(另外一个类似但不同的失败例是传统 UNIX 允许僵尸进程 的设计。)
  • 使程序逻辑复杂化:
    • 为解决以上缺陷在程序中插入难以复用的冗余代码。
    • 非常重要但容易被不少开发者忽视的一点:名义上的省略释放使代码更“简单”,但实际上使代码自身的逻辑混乱,更难自注释(self-document) 。
      • 这集中体现在所有权(ownership) 边界的不明确:非原作者光读代码,很难看出哪里对象确实应该被释放了,而哪里是原实现者预期资源确实可用。
      • 要在这个方面读清楚这样的代码,读者需要自行脑补,实际上就是把自己变成人肉 GC(这个问题实际上不限 GC ,在不严格约定策略的手动释放的程序中也一样)。而在没有形式化工具的支持下,模拟非决定性的策略下的副作用执行本来就有困难,更别提人脑了,基本没法指望精确证明可靠。
      • 所以对代码以外的文档依赖更加严重——然而这类情况下,设计者未必就有意识会提供讲清楚问题的文档(运行时有点问题 GC 兜着,大不了“调优”)……也就是说,使用 GC 这种实现诱使程序从设计到实现整体上更差了。

可以看出 GC 不适合多数消费和桌面级产品;在某些特定类型(特别是怕内存多得没地方用)的服务器上, GC 才可能发挥积极作用。然而即便如此,这也只是通用资源管理中的区区一种实现而已——一个 GC 实例说到底就是一个把所有权松散提升到程序实例生存期允许延迟释放的资源池;不带默认 GC 的场合,用户仍然可以自行实现 GC ——并且因为可定制性充裕,容易实现得性能更高,更适合具体应用场景。

造成这种选型错误的主要原因:对资源管理需求的错误理解且缺乏足够系统学习相关设计的经验,他们通常不明白:

  • 特定场景下最终用户需要的是什么。
  • 什么是成本和效率之间的妥协,什么是一开始就应该考虑满足的需求。
  • 什么是开发者本应做到的,什么是适合让机器自动完成的。

反面教材例: http://yinwang.org/blog-cn/2016/09/18/rust

为什么正经的语言设计中(数组等的)索引应从 0 而不是 1 开始

http://tieba.baidu.com/f?ct=335675392&tn=baiduPostBrowser&z=4833521930&sc=99708310397#99708310397

关于索引从0还是从1开始的问题,我现在改变主意了,得加到Q&A套餐里。

会回答这个问题主要是当时正好空明流转的群有讨论过类似的问题,刚批判完某些不好好学数学的。也因此没兴趣重复废话。不过直接来看,确实不那么容易看得懂。——

不好好学数学暴露的并不是一个low问题,虽然数学教育总体上仍然相当low(EWD在那篇文章的最后似乎也暗示了对某些“搞数学的”的不满)。

拐到数学问题上的关键话题:最小的自然数到底是0还是1。

【勿】Pointer 22:12:07
反人类,boost format的编号居然是从1开始的
【不】Leaves 22:16:05
只能说反程序员,人类还是习惯从1开始编号的。
【不】Leaves 22:16:40
刚学编程那时候觉得从0开始编号真是发人类。
【不】Leaves 22:16:44
  
【谓】灰天飞雁 22:17:25
是反C系程序员(? 从Pascal转过来的时候也感觉不习惯
【不】Cheukyin 22:27:56
貌似lua也是1开始?
【管理员】幻の上帝 22:37:41
 
扯蛋,序数和基数本来都是0开始的。除非要扯无穷。
【管理员】幻の上帝 22:38:24
 
你一出生就会从1数数?
一坨停留在幼儿园概念的数学白痴而已。
【言】山旮旯巨侠 22:39:05
火力相当猛
【勿】Pointer 22:39:14
0到底算不算自然数
【管理员】幻の上帝 22:39:31
就是看不惯幼儿园和小学数学瞎教坏小碰友又不管擦屁股。
【言】山旮旯巨侠 22:39:36
我小学的时候算 现在应该被开除了
【管理员】幻の上帝 22:39:42
一直都算。
【管理员】幻の上帝 22:39:51
只有讲数论的时候方便起见才强行不算。
【言】山旮旯巨侠 22:40:03
额
【管理员】幻の上帝 22:40:16
然而一般定义凭什么跟除法开洞扯上关系?
【管理员】幻の上帝 22:40:27
不就是逃避描述不了无限集合?
【管理员】幻の上帝 22:41:00
反正现在国民教育体系下教出来的九成九数学残废,不计较了。
【管理员】幻の上帝 22:43:54
就算数学学傻了没法救了,但是看到怎么表示范围应该也能自己蒙出来普遍情况从1开始有多蠢。
这种常识本应每个人自己发现。即便要偷懒,也有现成的科普:
https://www.cs.utexas.edu/users/EWD/transcriptions/EWD08xx/EWD831.html
【言】学校的黄焖鸡加饮料只要9.9元 22:52:37
6…
【言】学校的黄焖鸡加饮料只要9.9元 22:52:59
帝球涉猎真广…
【言】易均 22:53:50
@Pointer 新的定义中,0是自然数。数学是定义,是工具,不是真理。
【言】江渚流光 22:54:36
帝球到底是如何快速定位到这些文档的...
【管理员】幻の上帝 22:55:01
坑多了看多了有索引。
【言】江渚流光 22:59:05
 
【管理员】幻の上帝 22:59:41
定义自然数的公认基础是皮亚诺公理。
确定自然数基本性质的皮亚诺公理本身确实不关心几是自然数的起始,它只明确有下限。
但是如果不是0为下限,其它用到自然数的地方基本就没卵用,自然数这个概念几乎(除了数学归纳法等极少数应用)就没存在的意义了。(而数学归纳法这类东西的基础也可以直接架空皮亚诺公理单独定义,当然这下基本上只能从0开始了。)
所以基于需求,把0不是自然数就是挺傻逼的。那些以1开始当自然数的定义还得加个whole number来描述包含0的情况,于是“正整数”冗余出来哭晕在厕所。
从0开始也是ISO 31-11钦定的标准做法。
【言】易均 22:59:51
从皮亚诺公理看
【管理员】幻の上帝 23:01:00
皮亚诺公理第一条就可以写死0是自然数或者1是自然数,所以实际上应该是个模板,正式的说法是公理模式。
【言】易均 23:01:08
 
【言】易均 23:01:47
陶喆轩实分析
【管理员】幻の上帝 23:03:04
定义0或者1为基准主要是因为0是加法的单位元,1是乘法的单位元。
然而讲道理皮亚诺公理可不鸟这些代数上的玩意儿。
【管理员】幻の上帝 23:03:12
皮亚诺公理给加法的定义提供基础。
【管理员】幻の上帝 23:03:15
而不是反过来。
【管理员】幻の上帝 23:03:43
光凭皮亚诺公理也没禁止你拿2开始当“自然数”。
只不过没卵用而不够“自然”罢了。
【管理员】幻の上帝 23:04:09
1只有在少数数学分支比较有卵用,而0在几乎所有分支中都有。这就是差距。
【言】Blizzard 23:04:10
 
【言】易均 23:05:54
@	幻の上帝 不同意,皮亚诺公理可没定义加法
【管理员】幻の上帝 23:06:08
当然没有。
【言】易均 23:06:09
只定义后继的意义
【管理员】幻の上帝 23:06:21
但是你定义整数上的加法会借助到皮亚诺公理。
【管理员】幻の上帝 23:06:35
定义实数上的加法还需要其它一些东西。
【言】易均 23:07:02
加法是借用了其中后继这条公理
【管理员】幻の上帝 23:07:14
其实上面说的就是这个意思。
【管理员】幻の上帝 23:07:21
皮亚诺公理才不需要鸟你什么加法。
【管理员】幻の上帝 23:07:46
所以0其实在这个层次上也没有什么特殊的。
【言】易均 23:08:13
陶喆轩这本书好处在于,从自然数到实数定义,环环相扣,国内教材找不到。国内分析教材一来就直接上实数
【管理员】幻の上帝 23:08:19
但是0在其它绝大多数场合下都足够特殊,至少比1要特殊。
所以把0排除出自然数是没事找事。
【管理员】幻の上帝 23:08:38
搞搞布尔巴基啥的吧。
【言】易均 23:08:46
大家可以参考下,蛮有意思,重新建立数学思维体系。
【管理员】幻の上帝 23:08:55
会给你{{}, {{}, {}}}.....}}}}}}啥的么。
【言】易均 23:09:16
布尔巴基?没看过,写什么教材的
【管理员】幻の上帝 23:09:24
也不算重新了。
基本上看这个的也没其它现成的基础。
【管理员】幻の上帝 23:09:51
https://zh.wikipedia.org/wiki/%E5%B0%BC%E5%8F%A4%E6%8B%89%C2%B7%E5%B8%83%E5%B0%94%E5%B7%B4%E5%9F%BA
【言】易均 23:11:06
自然数定义也可以从集合出发,只是这个定义暂时没看过。
【言】易均 23:13:00
这估计就是你说的布尔巴基学派了
【管理员】幻の上帝 23:13:26
自然数定义用λ演算就够了。
【管理员】幻の上帝 23:13:59
在操作的意义上集合论太弱。
【管理员】幻の上帝 23:14:12
而且ZFC还塞了一堆公理模式。
【言】易均 23:14:13
lambda演算只知道丘奇那本
【管理员】幻の上帝 23:14:17
NBG干净点。
【言】易均 23:14:33
他本就是打算重定义数学
【管理员】幻の上帝 23:15:26
https://zh.wikipedia.org/wiki/%E6%96%B0%E6%95%B8%E5%AD%B8
当然传统废柴数学还是挺顽固的。
【言】易均 23:15:34
1910左右吧,希尔伯特打算重定义数学,哥德尔不是证明不行嘛。
【言】山旮旯巨侠 23:15:53
long long直接转成uint64_t处理应该没问题吧
【管理员】幻の上帝 23:15:56
如果新数学能教干净的话,至少不会有什么人对为什么unsigned会wrap之类的问题稀里糊涂。
【管理员】幻の上帝 23:16:12
一般来说没问题。
【管理员】幻の上帝 23:16:26
当然不排除有大于64位的实现,只是我没见过。
【言】易均 23:17:07
看得越多,很多东西没有理所当然的正确性,都是定义。
【言】易均 23:17:25
定义又要符合逻辑
【管理员】幻の上帝 23:17:26
 
除了代数不等式以外,其它几乎全是对码农要紧的部分。
【管理员】幻の上帝 23:17:35
可见传统数学在教育码农上多傻逼。
【言】易均 23:18:21
逻辑又要从这本书入门
【管理员】幻の上帝 23:18:28
 
原因看来只是上梁不正下梁歪。
跟谭×为何如此普及是一个道理。
【言】易均 23:18:44
 
【言】易均 23:18:56
以及三本
【言】易均 23:19:14
 
【言】易均 23:19:36
@	幻の上帝 你是指的哪几门课程?
【管理员】幻の上帝 23:20:01
幼儿园算术、小学数学、初中数学、高中数学。
【管理员】幻の上帝 23:20:14
而且很多有误导性反作用。比如PEMADS。
【管理员】幻の上帝 23:20:32
废物小学数学教育出多少求值顺序都扯不清楚的傻逼。
【管理员】幻の上帝 23:21:06
 
其实讲道理,人教版还是有些新数学残余的。
然而教得太浅,本科还要重来一遍。
【言】易均 23:21:53
我也是最近看本就数学专业的课程,才发现,以前太多坑,也没说清楚。例如经典的0.99999=1的问题
【管理员】幻の上帝 23:22:21
纠结这种问题本应纯属傻逼,但居然有钦定出来也是逗比。
【勿】Pointer 23:22:24
微积分一脸蒙逼学完了,发现也就用泰勒展开写写三角函数实现
【管理员】幻の上帝 23:22:26
明显不知道本世纪数学的进展。
【言】易均 23:22:27
重新看戴德金和柯西定义实数问题
【管理员】幻の上帝 23:22:36
旧数学就是=无误。
【言】山旮旯巨侠 23:22:38
我这边手上一本数学分析~~
【管理员】幻の上帝 23:22:49
更旧的数学和50年代引入的非标准分析来看就不是了。
【管理员】幻の上帝 23:23:02
钦定=的鹦鹉学舌也是够了。
【言】易均 23:23:11
0.999这个其实就是个定义问题
【管理员】幻の上帝 23:23:12
本来这就是数学哲学内容而不是数学内容。
【言】易均 23:23:31
菲赫金哥尔茨的微积分学教程提到了
【管理员】幻の上帝 23:23:48
两种定义都通,只不过历史上钦定=的被先擦屁股干净了,所以就挤进教科书了。
【言】易均 23:24:26
陶喆轩也提到了,本就是有限小数的十进制表示法的一点瑕疵吧。
【管理员】幻の上帝 23:25:04
跟这个无关。
【言】易均 23:25:05
@山旮旯巨侠 你用那个作者的?
【管理员】幻の上帝 23:25:26
你要用其它方法,像p-adic数混搭什么的照样有这个问题。
【言】山旮旯巨侠 23:25:31
大学课本呀~~~ 被我们老师疯狂吐槽
【管理员】幻の上帝 23:25:36
这个问题不是数学该管的。
【言】易均 23:25:56
p-adic什么表示法?
【言】山旮旯巨侠 23:26:25
华东师范大学 数学系
【言】易均 23:27:19
@山旮旯巨侠 看菲赫金哥尔茨吧,尽量国外教材,不过少了测度和勒贝格积分。普林斯顿最近出了4本分析教材,也不错。
【言】山旮旯巨侠 23:28:14
记下了 不过需要我再次拿起课本吧~~
【言】山旮旯巨侠 23:29:21
大学的时候我还怼过裴哩文
【言】易均 23:29:21
陶喆轩看前几章自然数的定义到实数定义就好,其他的,菲赫金哥尔茨这个也是一步扣一步,例题也多。
【管理员】幻の上帝 23:29:26
https://zh.wikipedia.org/wiki/P%E9%80%B2%E6%95%B8
【言】山旮旯巨侠 23:29:26
裴礼文
【言】山旮旯巨侠 23:29:48
可惜没坚持 就跟了前面一部分习题 后面都放弃了
【言】Blizzard 23:30:23
 贵群还能学到数学 有空还是要看看
【言】山旮旯巨侠 23:30:28
吉米诺维奇
【言】山旮旯巨侠 23:30:50
都还给老师啦
【言】易均 23:30:54
国内教材真的懒得说,都是到处抄。表达不清晰,数学本就是逻辑性强的东西,最需要一步一步推导。
【言】易均 23:31:14
习题集北大的也不错

为什么黑苹果

https://tieba.baidu.com/p/5151307647?pid=108091070649&cid=0#108091070649

为什么黑苹果?指鹿为马、不公平竞争、混淆视听、误导消费者……这个自己看着办,总之不只是莫须有。

这里就澄清对动态加载相关实现(包括这里的热更新)在技术方面的一些实质。

就@lcc771209 的理解的所谓的(指令)代码和数据的问题来讲,至少在软件实现的角度上,这两者没有严格的界限,不需要一定被区分。本来代码说白了就是编码了的数据的一种。

业务上需要怎么区分是另一回事,取决于具体业务的需要。不同软件对待这个问题的答案不需要一致。回答什么东西算所谓的“纯数据”,还轮不到应用商店以审核政策一刀切。

一般地,这里可以有几个不同的角度的理解。

1.软件层面上,被误认为有必要区分指令和数据才能实现一些功能或保证某些性质,不过是事先站在体系结构和宿主环境(操作系统)下所谓本机(native)语言的实现这种“系统软件”的角度上看问题不腰疼罢了。这类语言一般不是所谓的动态语言,正因为它们预先假设了可执行的部分和不可执行的部分。多数开发者在很长一段时间之后才会意识到,这种预先假设只不过一种语言设计上的偷懒和甩锅而已,对开发应用这个目的来讲并不是必须的。反过来,强制要求遵守这个假定可能会显著影响开发效率(因为这些语言多数设计得抽象水平非常拙计不好用),于是业界才会出现性能拙计的动态语言实现更流行的情况。(性能太烂坑到最终用户是个问题,但不是在此讨论的主题;合理的使用反而有利于性能;后面还会看到强制策略对性能的负面影响。)

2.抛开直接使用的语言实现,理论上,现在仍然没有一个靠谱的形式系统能通过区分代码和数据来显得比不区分代码和数据的系统更“安全”,虽然使用前者的系统的确有用于安全目的(主要是在一些验证工具的设计中作为发现而不是避免安全漏洞的理论基础)。

3.硬件角度来看,现今主流体系结构仍然使用不显式区分地址和数据线普林斯顿结构(原始版本的冯·诺依曼结构),而处理器内部的微架构会通过区分数据和指令缓存的形式过渡到哈佛结构,整体上算是混合架构。(历史原因有很多,没修过计算机体系结构的或者白学了的自己搜。)

划重点:包括苹果设备使用的CPU在内,主流设备在ISA层次都使用和主存布局一致前者。(非主流嘛……51单片机什么的……)

这一方面是软件(特别是操作系统)历史上的兼容性包袱,但根本的还是来自满足需求的可行性——你使用的操作系统依赖的、提供给app用的一致的虚拟存储抽象就是这样设计的,并且找不到什么伸缩性足够好、成本可以接受的靠谱的替代。(几个现代用户能受得了加一根内存条然后在运行每个app前把内存管理例程的参数挨个儿biu一遍嘛……)像单片机那样敲捣现在你用系统乃至app并非技术上不可实现,但现时不存在任何“生态系统”可以承受这种开发和部署成本。

回到之前的问题。苹果在这里做了什么?强制开发者在高层区分指令和数据。

这会造成一系列严重问题,比如说要实现一般意义上的热更新这样的特性,排除一直以来(严格程度上)或多或少被苹果禁止的方式,得需要额外的动态语言运行时;再 如不能使用JIT编译等手段发挥硬件机能(差别参照硬件加速的作用)。后者在浏览器这种应用上明显成了瓶颈,于是苹果给自家的Safiri开了洞,而别家浏览器就没这种差别待遇。这种利用作为OEM和系统供应商的优势垄断地位对其它厂商的不正当竞争,靠不对等信息操纵市场缩减用户选择权,摆明是非技术上都该恶心到最终用户的例子了。

另一方面,硬件实现混合体系结构,包括普遍支持的SMC(self-modification code)特性,是有显著的代价的。芯片授权费用的很重要一部分来自于设计成本,而设计复杂的存储子系统之后再绕过去显然是没事找事,效果就是用户花了的这部分钱打水漂,这是恶心最终用户的破烂之二。

当然,这可以归咎于开发者的一般意义的蠢;把软件撸得无谓地卡翔了,大抵也属于此类,只不过可以是无心之过而且没有那么突出的反智效果而已。

还有一点,苹果虽然能在应用商店的地盘以下架威胁变相强制策略,但没法顾及不以应用商店渠道发行的软件。注意这些软件可能是完全合法的(比如操作系统本身),照样可能会受到因为允许不区分指令和数据的形式的潜在的安全问题(如果有)的影响,也没有免责的锅能甩。

(……至少苹果的系统内部是干不掉实现类似dlfcn功能的这坨API的,所以技术上就别指望一定能有多安全了。)

于是所谓的安全仍然回到了对渠道的信任上,没有新意。(应用商店没有满足需求的软件你又不会自己撸?活该咯?高兴么?)限制使用唯一封闭的渠道这点对外行来讲或许不算什么大不了的,但对懂行的来说就是少了一个公开审计的渠道,在同等条件下即增加不安全性(即便任何此类机制最终都面临工作量过大根本审计不完的问题)。

为什么黑三星(的某些产品)

https://tieba.baidu.com/p/5555131286?pn=3 (一直被吃贴……)

不好意思,我不是针对谁暴躁。我是觉得被智熄设计玩过的都该有这样的觉悟的。

我不清楚诸位是不是足够享受过近几年如 note8 这种丧尸式内存管理下对普通应用的困扰——自动熄屏后打开 100 次有 95 次以上前台应用直接就给干掉了,有几次发现终于长记性了,然而一划屏就一夜回到解放前了……以下脏话省略。

更严重的问题是这类机器上一旦 root , KNOX 机制生效,用户就迫真活该损失行货保修了?本来用的好好的 Samsung Pay 突然就不能用了?活该我傻我买机器的损失咯?

特别注意,市场上卖的这种应用安全机制,就三星那么有自信直接封装 Snapdragon MCU 的 efuse 做成硬件上能修改但不可逆的玩意儿。这锅甩得 666 啊。

拜托,买的机器又不是租的,拿到的是整机的所有权,在消费者根本没有违反任何强制性安全标准更改物理实现的情况下,是谁给厂家强制用户选择不改动系统和不保修只能选择其一的脸的?是谁给厂家强制用户物理上不可逆地损失正常功能和正常运行体验之一的脸的?

几十年前搞密码学的就知道安全不能依赖算法保密,你三爽还敢冒天下之大不韪让用户指望名存实亡的物理隔离咯?说极端点,鬼知道授权经销商暗地里能交易到什么程度?别说你企业产品卖给个人想概不负责,开历史倒车挖公共安全墙脚懂吗?

话说回来,如果 build.prop 能自己编辑也就罢了。但是偏偏就是要 root 权限,而且这里要 root 一定程度上说得过去。那么换个角度讲,在 Android 之前,本来各个 Linux 发行版的系统管理员拿 root 权限就是很正常的事情(如果你是租的光猫之类的另论),印象中就从 Android 开始,突然就搞得 root 反而还不安全了?

这里从某种意义上更欠黑的还是苹果, jailbreak 本来是个中性的技术手段,莫名其妙就被 FUD 妖魔化成非法了,谁带的节奏来着。

为什么黑华为(的某些终端设备产品)

https://tieba.baidu.com/p/5717092249

按已有风评,华为在音游漏键(屏幕实时响应)是普遍特别辣鸡。

只确认过个别机型,不知道现在是不是有改善,不过从尿性来看不要指望太多。

实际原因是:

  • 任务调度有问题,实测造成问题主要是华为应用商店不管是否强制关闭应用/停止服务/关闭自动更新都会不定时瞬间吃大量的CPU,并且强制关闭后不断自启;因为是系统应用,无法卸载禁用。
  • 稍微一热处理器性能打折严重。

理论上讲 root 能治,这点比三星厚道点,因为 root 了还能刷回去,不是不可逆的。

语言特性的构造

http://tieba.baidu.com/p/5476456843

语言设计问题相关实例

基础知识

文本编码

一般语言话题

标准库的称呼

求值策略相关

数组和指针

数组和指针(误导实例)

指针和引用

安全性

C 范围检查

http://tieba.baidu.com/p/4389930794

线程安全

http://tieba.baidu.com/p/5683849936

一看就属于没经验的…… 靠谱点讲好了,这种允许副作用又允许共享可变状态的语言,给你都保证线程安全才是真的烂……跟UI没什么关系,只不过搞UI的特别习惯在这里被坑而已。 异常?给你异常就不错了。数据竞争就是UB,鬼知道会有个毛线。

一样的道理,想想为什么野指针跑飞了会挂?给你保证不会跑飞有什么代价?能自觉保证正常的代码的码农凭什么要陪写烂代码付出这种代价?要可移植还没法没有代价咯? 唯一要求确定行为合理的,是对付不可信代码,不能放任不可预知的行为。而划分什么代码可信本应由用户决定,用户决定不了就代理给码农决定,而不是语言的设计者自己替用户脑补,否则就是一种无能——不允许可移植地避免显然的代价。Java在userland的实现其实已经就混淆了这种问题,要求NullPointerException至少是两种蠢(一种是不懂利用静态类型系统避免大部分可能引起这种问题的情形,另一种是不懂Hoare logic这类简单的工具,没脑子区分出narrow contract);当然,某些人就是喜欢把兼容蠢代码当作“安全”,只能呵呵。

delete 后清空指针值

http://tieba.baidu.com/p/4325323468

看来还是有必要上FAQ。提纲。
0.确定在这之后必须使用这个空值是另一回事。然而具体到指针上如果不是内部实现,一般也是蠢用法。(智能指针/optional死哪去了?)
1.不管是不是空,反正访问了都UB,语言层面上一样没保证(扯什么异常的妥妥重修)。
2.考虑具体实现,基本上也就是调试,所谓的防御性编程除了糊弄(隐藏错误行为,而不是错误的来源)外没卵用。往往因为你不知道的——并且这种做法下通常你更难以搞清楚的——原因,运气不好一点QA都糊弄不过去(特别是你只能测试你自己撸的有限的部分,而UB炸了原则上是没有范围限制的)。
3.给自己没事找事:破坏第一现场给调试造成麻烦。
4.仍然是没事找事:废话+语义噪音。
上面是鸡汤式偷懒思路。如要考虑原则正确性问题,以上几点从下往上看。

分配函数

http://tieba.baidu.com/p/4303278739

算了,这个也是经了,加个work item列入FAQ。这里列提纲。
首先,如果是你自己想出来的问题也是罢了,如果是别人教的……拖过来我保证不打死他。
什么new/delete和malloc/free?抱歉,没个卵关系。
非得沾亲带故,也就是new和delete不和operator连用能有new-expreesion和delete-expression的语法,然后new-expression和operator new有一腿,delete-expression和operator delete有一腿,然后调用默认nothrow版本的::operator new的行为可以类似调用malloc,调用默认::operator delete的行为可以类似调用free而已。原来的问题仍然是蠢问题。
不算placement new以外的new-expression和operator new调用的求值主要差别是前者可以多出来构造函数调用;delete-expression和operator delete调用的求值主要差别是前者可以多出来析构函数调用。而placement new某种意义上就是手动调用构造函数……

operator new/operator delete和malloc/free差别一箩筐,即便行为能够接近:
前者是钦定的allocation/deallocation function,后者充其量只能是可能的实现;
前者能在类作用域重载后被自动用上,后者没这待遇;
前者是区分数组和非数组版本的;

除非nothrow版本,operator new失败抛异常,而malloc失败返回空指针值;
前者的全局版本可以由用户替换标准库实现提供的定义,没有任何其它函数有这种待遇;

异常了滚回去的时候operator delete会被自动调用。

说白了差得太多太明显了,malloc/free充其量只是和nothrow版本的::operator new/::operator delete在行为上能有那么点可比性而已。混一起炒冷饭的是什么居心,嫌不够乱?

在理解上面的常识之前,记住不管是哪坨玩意都不是给你用的,随便乱用八成效果是通不过code review找抽或者蠢代码之一。

再补三点不限LZ估计也不太容易注意而不是那么像常识的,想无聊撵人的可以塞面试题或者业务考试里:
1.数组版本的operator new在ISO C中并没有对应。注意calloc是带初始化填0的,目的不一样;
2.malloc对0参数的行为impl-def:
WG14/N1570
7.22.3/1 ... If the size of the space requested is zero, the behavior is implementation-defined: either a null pointer is returned, or the behavior is as if the size were some nonzero value, except that the returned pointer shall not be used to access an object.
而allocation function在此情况下可能失败但不是impl-def,没有要求实现文档明确,而返回的结果上附加约束:
WG21/N4567
3.7.4.1/2 ... The effect of indirecting through a pointer returned as a request for zero size is undefined.36
36) The intent is to have operator new() implementable by calling std::malloc() or std::calloc(), so the rules are substantially the same. C++ differs from C in requiring a zero request to return a non-null pointer.
3.C++14开始实现有权合并满足特定条件的::operator new调用而不需根据as-if rule证明不存在可观察行为改变后才能优化。C没有类似的东西。
WG21/N4567

5.3.4
10 An implementation is allowed to omit a call to a replaceable global allocation function (18.6.1.1, 18.6.1.2).
When it does so, the storage is instead provided by the implementation or provided by extending the
allocation of another new-expression. The implementation may extend the allocation of a new-expression e1
to provide storage for a new-expression e2 if the following would be true were the allocation not extended:
(10.1) — the evaluation of e1 is sequenced before the evaluation of e2, and
(10.2) — e2 is evaluated whenever e1 obtains storage, and
(10.3) — both e1 and e2 invoke the same replaceable global allocation function, and
(10.4) — if the allocation function invoked by e1 and e2 is throwing, any exceptions thrown in the evaluation
of either e1 or e2 would be first caught in the same handler, and
(10.5) — the pointer values produced by e1 and e2 are operands to evaluated delete-expressions, and
(10.6) — the evaluation of e2 is sequenced before the evaluation of the delete-expression whose operand is the
pointer value produced by e1.
[ Example:
void mergeable(int x) {
// These allocations are safe for merging:
std::unique_ptr<char[]> a{new (std::nothrow) char[8]};
std::unique_ptr<char[]> b{new (std::nothrow) char[8]};
std::unique_ptr<char[]> c{new (std::nothrow) char[x]};
g(a.get(), b.get(), c.get());
}
void unmergeable(int x) {
std::unique_ptr<char[]> a{new char[8]};
try {
// Merging this allocation would change its catch handler.
std::unique_ptr<char[]> b{new char[x]};
} catch (const std::bad_alloc& e) {
std::cerr << "Allocation failed: " << e.what() << std::endl;
throw;
}
}
—end example ]
(嘛,这里的例子被我在working draft里改过。。。)

<iostream><cstdio> 有什么区别?

http://tieba.baidu.com/p/3971013816

1.cstdio是面向“文件”的,或者不强调文件和非文件流的区别,默认流就是可以关联外部文件,至于文件的外延是啥就不管,扔给宿主环境了。从std::FILE这个名字以及printf/scanf接口描述基于fprintf/fscanf上就可以看出来。
iostream头只是包含了一坨东西,封装标准输入输出流,和文件流(在<fstream>)不通用。
2.cstdio不知道iostream,而iostream知道cstdio并且默认同步,此外提供有限的接口摆脱关系(sync_with_stdio)。因为这个接口约束,iostream要脱离cstdio(的实现)是不太可能的。
3.cstdio有orientation的概念;iostream是否wide是直接写死在静态类型的模板参数里的,并且底层的流不只支持char和wchar_t字符类型。
4.iostream底层公开了相对完整的缓冲接口(如std::basic_filebuf),可以自行扩展;cstdio只能笼统地设置缓冲模式和提供提供区的接口,但多了行缓冲的概念(_IOLBF)。
5.iostream和std::locale交互。C标准库的<locale.h>(对应C++的<clocale>)提供locale-specific behavior,和cstdio不直接关联。两者不是一回事。
因为这个原因,iostream能直接编解码字符串,而cstdio不能处理和wide-oriented无关的编码,需要<wchar.h>(对应C++的<cwchar>)或C11的<uchar.h>的例程显式调用。
6.iostream体系提供了基于字符串提供了“内存流”(主要是std::stringstream),而cstdio这部分一般只是内部实现,如果需要得自己造。
7.iostream底层通过继承和重写protected虚函数提供实现。cstdio这部分是内部实现,不提供与之对应的扩展功能。
8.两者都实现了流的状态,但不尽相同。iostream显式区分bad和fail,但cstdio没有。
9.iostream提供特定的打开模式的组合,而cstdio使用字符串参数。前者无法直接扩展;后者解析较低效,虽然不需要修改类型就可扩展但也存在实现的运行时兼容性问题。
10.除了检查流的状态,iostream处理错误可选使用异常。cstdio处理错误依赖返回值和流状态。
11.iostream的格式输入输出基于重载,静态分派且类型安全,可以实现得更高效;cstdio的格式输入输出使用的是领域特定语言,需要运行时解析,通常比较低效,且实现的运行时兼容问题修复较困难。
但后者同时提供运行时配置可修改的格式的功能,而前者没有。

另外, iostream 在主流实现默认比 cstdio 慢的主要原因,基本上就是因为兼容后者的锅。具体来说,后者为了兼容某些扩展行为,通常会实现为具有互斥锁定语义的底层调用。默认前者允许和后者混用,结果保持同步,实际实现一般也直接复用后者的部分接口和实现。这要求每次 I/O (极端情况下,每个字符的 I/O )进行一次锁定,在典型操作系统下很可能涉及陷入内核的系统调用,上下文切换的单次开销即可能无法忽视,频繁调用导致性能极端低下。

解决方案:一个 sync_with_stdio(false) 天下太平。注意要在开始使用前确定调用,确保之后不使用 cstdio ,否则无法确保 I/O 行为可预测。关闭同步后,实现可使用对应非同步的接口(如 getc_unlocked 代替 getc ),不存在此类性能瓶颈。

关于 scanf_s 等接口

由微软在 Visual C++ 中提供,后来提交标准化为 ISO/IEC TR 24731-1

这些接口的实用性存在争议反对者包括 Austin Group ( POSIX.1 起草者)以及 Ulrich Drepper ( GLibC 维护者)等

虽然后来这些内容作为 Annex K(normative) 进入了 ISO C11 , 但并不要求所有实现支持(明确只有定义了宏 __STDC_LIB_EXT1__ 的实现需要支持)。计划引用 ISO C11 的新版 ISO C++ 要求实现定义。而当前仍引用 ISO C99 的 POSIX.1 并中不包含对此的实现要求。

正因为当前由于绝大多数主流实现者拒绝实现而导致有 WG14 提案提议把它从未来的语言标准中移除或先 deprecated 后移除,使用它一直会导致可移植性问题。

<cstring><string>

http://tieba.baidu.com/p/4641914364

完全不同的标准库头。

按 ISO C++ 的惯例,前者对应 ISO C 的 <string.h> ,区别 <cstring> 要求库函数在 namespace std 声明并且还多出来一些 C++ 支持的重载(对 <cstring> 而言主要和函数参数和返回类型中的 const 有关),以及 ISO C++ 要求的库函数保证不以宏实现。注意 <cstring> 不要求包含 <string.h> ,所以全局命名空间可能有或者没有对应的 ISO C 函数的声明,而 <string.h> 可能支持的非 ISO C 扩展(如 POSIX 的 strdup )也不能保证在全局命名空间中声明。

<string> 主要提供 std::char_traitsstd::basic_string ,和 <string.h><cstring> 提供的接口无关。

另见下一节注意事项。

关于 C 和 C++ 标准库头

注意 ISO C++ 包含了 ISO C 的标准库头,但在 ISO C++ 中被 deprecated ,如无必要,使用 C++ 标准库时, <xxx.h> 应尽量用被 <cxxx> 代替。必要情况如前言使用 POSIX 扩展。尽管甚至 Stephan T. Lavavej 都曾表示不知道这为何被 deprecated ,这里的主要原因应是鼓励这种做法。

顺带,注意到某些人鼓吹只使用 <xxx.h>,如:

另外 ctime 头文件没定义 std::gmtime_r,而 time.h 定义了 ::gmtime_r。我可不想去背哪些函数是 C 语言的哪些是 Posix 的,哪些头文件是 C 语言的哪些是 Posix 的(在Linux下,二者基本不分家)。为了用几个系统函数(例如 fcntl() ),我该 include cfcntl 还是 fcntl.h?用线程是 cpthread 还是 pthread.h?我总是记不住 memset() 的参数顺序,因此一般用 bzero() 代替,但是 manpage 说 bzero() 声明于 strings.h,那我要不要考虑试试 cstrings 呢?何必给自己找麻烦,C++ 标准库之外的内容干脆统一用 .h 头文件好了。

硬伤一堆,列举如下:

  • “ctime 头文件”——暴露出不知道ISO C++ 标准库头不要求实现为“文件”
  • “C++ 标准库之外的内容干脆统一用 .h 头文件好了”——没有搞清 C++ 标准库的外延。不管 <xxx.h> 还是对应的 <cxxx> ,当前都是 C++ 标准库要求实现的一部分。并且如上面 STL 所说,看样子前者并不打算短期内移除。
  • “哪些头文件是 C 语言的哪些是 Posix 的”—— POSIX 头文件要求支持 C (因为 POSIX.1 直接就是 ISO C 的扩展),可不保证支持其它语言;然而实现中,很多都有 #ifdef __cplusplus 这样的考虑,所以区分什么头文件是什么“语言的”本来就是莫名其妙的做法。应要知道的,是指定的接口来自于哪个规范,以便减少必要时查证的开销
  • “系统函数(例如 fcntl() )”——概念混乱。系统函数是指什么?系统调用?用户空间提供的 POSIX 的接口的实现?
  • “我总是记不住 memset() 的参数顺序,因此一般用 bzero() 代替,但是 manpage 说 bzero() 声明于 strings.h”——愚蠢。 BSD 发明 bzero 并不是为了纵容记性差,记不住为什么不查?(哦,好吧,会 man bzero 是吧……怎么不会 man memset 了?)连这点都做不到,写的代码谁敢用?更别提 bzero 本身可移植性明确更差的事实了。
  • “那我要不要考虑试试 cstrings 呢?”——文档明确告诉干嘛了还多此一举,吃饱了撑的?只有找文档的漏洞时这样做可以理解,不过看上面连搞清楚引用的哪个文档都懒得做,实在不像是有做这个的资质。

这样做纯粹损害可移植性而没有获得任何好处(允许以混淆读者视线而适合让自己变蠢这点在此论外),应予视为误导。

标准库头包含

经常有见到什么某个标准库头(如 <iostream> )包含另一个(如 <cstdlib> )所以不用包含后一个就能用,不信看源码云云——明摆着犯了特定实现代替接口的以偏概全的逻辑谬误。

实际上, ISO C++ 还真规定了一些必须(等价于)包含的情况,但也就只有那么些情况

32 位 v. 64 位

http://tieba.baidu.com/p/4816012657

什么是64位CPU 要了解什么是64位的CPU,先要了解以下几个概念: CPU的位和字长 位 在数字电路和电脑技术中采用二进制,代码只有“0”和“1”,其中无论是“0”或是“1”在CPU中都是一“位”。

数字电路不一定需要使用二进制、 ENIAC 之类的古董没用二进制以及没有CPU之类的低级常识硬伤不提,首先你缺乏指令集架构(Instruction Set Architecture, ISA) 的概念。

CPU 设计从来不直接和什么位和字长关联。“字”在一定意义上纯粹只是历史遗留物。“字长”这种概念也是冗余的。

能套用这种概念的是操作的局部特征。特别是 IA-32 这样的历史包袱严重的一大坨玩意儿,即便是寄存器,位宽不都一定。

所谓64位CPU,严格意义上只是说能实现 64 位 ISA 的一类设备。而64位的ISA也仅仅只是指其中的一些典型操作使用64位,并不是全部。一概而论是没有意义的。

对IA32兼容架构来说,64位体现在特定系统模式下(通用目的)指令使用的地址的默认位宽。具体点说,首先需要进入IA-32e模式(从保护模式开始,关闭分页、启用物理地址扩展(PAE) 、加载 4 级页映射表(PML4) 、设置 IA32_EFER.LME = 1 、启用分页),然后在 IA-32e (IA32_EFER.LMA = 1) 下:

1.64位模式:代码段描述符满足 CS.L = 1 和 CS.D = 0 。此时默认操作数32位,默认地址64位。使用指令前缀可以改变操作数大小为64位和16位,地址大小为32位。

2.兼容模式:代码段描述符满足 CS.L = 0 。此时可以运行16位和32位的程序,绝大多数架构行为和保护模式一样,但线性地址到物理地址翻译、中断和异常处理以及系统调用(调用门和SYSENTER/SYSEXIT)使用IA-32e机制。

其它64位ISA当然不需要有一致的设计。

字节和字长的区别 由于常用的英文字符用8位二进制数就可以表示,所以通常就将8位称为一个字节。字节的长度是固定的,而字长的长度是不固定的,对于不同的CPU,字长的长度也不一样。

这个是典型扯蛋。

在一定意义上字节(byte) 也是历史包袱。历史上 1 字节一开始就不确定几位。像 TAOCP 设计的 MIX 就是 1 字节 = 6 位。而 ISO C 和 ISO C++ 明确支持 CHAR_BIT >= 8

要求表示8个二进制位数据的场合(如某些 RFC)使用的术语是八元组(octet) 以刻意规避字节概念的不精确。

只有微处理器流行后实施的设计如 POSIX 和 Java 之流才会假定 1 字节正好 8 位,但这也削弱了 ISA 层次上的可移植性。一些 DSP 使用 16 位为 1 个字节。也有干脆任何数据都是 64 位的 ISA ,对这种 ISA 来说区分字和字节都没什么实际意义了。

字长 电脑技术中对CPU在单位时间内(同一时间)能一次处理的二进制数的位数叫字长。所以能处理字长为8位数据的CPU通常就叫8位的CPU。同理32位的CPU就能在单位时间内处理字长为32位的二进制数据。

什么叫“一次”?一个时钟周期?不巧,现在的主流 CPU 都是流水线化 + 多发射的,你根本说不清楚什么叫“一次”。

8位的CPU一次只能处理一个字节,而32位的CPU一次就能处理4个字节,同理字长为64位的CPU一次可以处理8个字节。 这样AMD的64位的CPU在处理速度上较32位的CPU理论上快了一倍。 8位处理器、16位处理器、32位处理器和64位处理器,其计数都是8的倍数。

扯蛋不重复纠正,不过还要指出存在 36 位等非 8 为倍数位数字长的ISA,即便现在都是古董。

剩下的我就粘了,很麻烦,另外我已经强调特指处理器性能,而且是理论值,我不知道你是什么意思?x86架构方面的专家?还是什么?为什么你就这么牛逼?即使我说的不对,用不着这样吧?

理论值?什么理论?连 Amdahl 定律这种层次都不到的YY?

没分辨能力粘个蛋蛋,全是胡扯。虽然手打科普是我的事,但是就因为有这样胡扯乱粘的才增加我科普和挂人工作量。要点脸行不?

为何我需要纠正你的笑话?不巧,我现在就拿这个吃饭。我这里造 CPU 特么的一坨恶心的破事就被你两句话轻描淡写打发了?

另外我在申明一点,我所谓说的如今不存在谁快的问题,是因为如今的硬件水平,起步已经在4g内存之上了,所以不存在楼主所说因为内存不足而造成的性能下降,我不知道你怎么理解的,啰嗦半天,装的跟大神一样。

这贴里没说就是什么硬件吧?这个吧的主题能适用的硬件也不管你这个假定吧?比如说,这里谁的手机都是 4G 以上了?我的特么就不是。

另外,我最后再说一点,如果内存过下,确实会出现楼主所说的因为64位相交32内存占用略多而造成性能下降问题,我之所以说实际使用不存在这种问题是是因为除了上述原因,还有最重要的,64bit最低内存要求为2GB,2g并不会出现性能降低的问题,即使有,那也是windows

神特么甩锅给 Windows 。除了地址空间划分的问题,对应用来说,是不是 Windows 有什么区别?哦,是有一些 ABI 问题,但是你会管什么 FS 什么 GS 不?

GUI 入门

图形 v. 图形用户界面

TBD

常见误区

http://tieba.baidu.com/p/4381930595

1.目的。

如果是上班,要你撸什么用什么,当然基本上靠视频都是低效做法。如果是瞎忽悠要你调研自己不清楚的东西还负责选型,出问题谁背?这样的单位趁早跑路。

如果是自己撸……没事撸毛 GUI ?还用 C艹 ?

技术选型能力比起语言使用能力在这里很可能更重要。

2.所谓GUI。

三流科班和野路子出身的往往喜欢望文生义 GUI = G + UI = G + U + I,结果把经典 GUI 成功的部分如 WIMP metaphor 全给扔了。不是说不能创新(比如说从桌面迁移当然应该有变化),但是一问三不知,是罪。

3.抽象层次。

这里着重需要婊的是所谓“立即模式”GUI(IMGUI)。说白了就是早年性能等限制导致的一个实现泄漏出来的抽象,近些年居然好意思开宗立派了……

对付这类玩意儿,一概同没有 AST 的辣鸡编译器一样论处。

(另一个要婊的是 FRP 和 UI 相关的倾向,但这个也坑着。)

这类玩意儿,倒不是说学了完全没用,一些时候也就是需要这样解决实际问题。但完全没基础的学这个就是毁三观的歪门邪道。

4.依赖。

首先,没法划清边界,无原则依赖具体实现的所有软件都是半吊子。 GUI 也不例外。需要拎出来说是因为历史原因造成了GUI不方便跨平台的错觉,而实际上只是因为有兴趣和耐心搞 GUI 框架的,大多对正常姿势的软件工程方法和码农手段一窍不通而已,自然上梁不正下梁歪。

这里需要着重婊的是凡学 GUI 先讲 Win32 的 Win32 中心主义(即便没有 Win32 外的跨平台需求)。实际上Win32的实现从ReactOS等途径扒出来就可以看到极其狼狈,而接口抽象设计上也是非常拙计。应用层实现不算 C 的辣鸡风格 API 的问题还有一大坨破事,比如 WndProc 加个 void* 会死?——这类烧饼问题基本上现代的其它解决方案都不会有。强行 Win32 能和其它方案同等质量地(几乎不可能,至少考虑成本是这样)完成项目的确可以算是炫技,但另一方面在实现出来前,光是选型就是炫蠢了。

(比 Win32 更烂的其实不少,主要是非 Windows 平台上流行,略过。)

MFC 为什么 zz ?主要原因也是类似,真正干活的几乎全是外包给Win32的。所以就学习而言MFC还不如Win32,即便项目做起来代码看起来没那么屎(但这也仅相对 Win32 而言)。架构嘛, Document-View 的过气笑话也不值得浪费时间。至于CArray被吊打几条街之类的玩意儿就不提了,反正设计 MFC 的在非 GUI 的问题上往往更加外行。

所谓的 DUI(Direct UI) 是另一个典型反面教材的例子。 DUI 原本只是微软的某个实现中发明的架空 HWND(Win32窗口句柄)的技巧,说白了是一种 hack ,结果莫名其妙被没见过世面的井底之蛙搬过来变成了架构创新层面的大煋闻。实际上,如果真按所谓 DUI 的精神,世界上的 GUI 从来都是 DUI 的,只不过实现了 Win32 (其实是 Win16 )的 GUI 之后 HWND 成了 DUI 的一个实现极烂的代理。会有这种错觉,主要原因恐怕就是上面所说的 Win32 中心主义,以及本身学习能力、眼界、想象力和品味太差,空有生产烂轮子的山寨水平这个原因了。

至于BCB这样(某种意义上包括整个所谓的RAD行业)的过气玩意儿,不予多评论(现在叫BCB的和VC6一样古董我就不多戳穿了)。就说一点:鼓吹BCB好转进C#?那为什么不直接学C#?

其它待挂列表

Google C++ Coding Style 槽点

有人已经挂过了。考虑到材料不断更新,可能需要重新估算工作量。

实例:所谓的static initialization order fiasco

Style, also known as readability

乱弹琴。

The term Style is a bit of a misnomer

不过之后似乎有自知之明……

Forward Declarations

A "forward declaration" is a declaration of a class, function, or template without an associated definition.

冗余定义。

C system files.

C++ system files.

__USE_MINGW_ANSI_STDIO 呵呵呵……

Do not use a using-directive. Do not use inline namespaces.

无脑逗比,显然不知道这些特性干什么用的。放着 inline namespace 不用难道上 ABI tag ?

FRP

发明者对外延失去控制?

http://weibo.com/1684815495/CsFjTbcOj?refer_flag=1001030103_

被人嫌弃命名烂

http://stackoverflow.com/questions/5385377/the-difference-between-reactive-and-functional-reactive-programming

http://tieba.baidu.com/p/2286985797

找FRP的denotational semantics,结果随便搜就是挂婊的2333……

http://conal.net/blog/posts/garbage-collecting-the-semantics-of-frp

UTF-8 Everywhere Manifesto

以下评论基于中译版本

一些科普常识和结论(例如 UTF-16 因为变长特性导致的无能,以及代码页是不合理的烂设计)是没有问题的。但是,从动机、论证的目的以及参照的依据看,存在相当多的低级错误,应予视为误导。

1-3

第一个错误是混淆了“文本”的用途。文本首先是人类书写系统的直观具象。文本编码的最初设计也遵循这个基本要点,虽然之后作为妥协混入了“控制字符”也已有相当长的历史了,但那只应该是妥协。

文中提到文本作为通信协议表现形式的常用现状。这仍然只是妥协。注意通常称为二进制编码的非文本是无法被文本取代的,因为严格地来说文本充其量只是二进制编码的一种。原用于反映书写系统、之后作为妥协折衷产物的这种形式具有非常多不适合编码的普遍糟糕特性(例如浪费资源),只是体现不合理的 worse is better 渣设计的历史包袱的实例而已,其被普遍接受并非来自于自身的技术优势——换句话说,也是妥协。

造成用户不得不“享受”这种现状的,早年罪魁祸首之一是 UNIX 系为首的业界通行设计,近年来则是 Web 。 Unicode 承前启后不假,但在“流行”充其量是拾人牙慧—— UTF-8 几乎全是借 Web 的东风而大势所趋,尽管技术上来讲对 Linux userland 来说它更能发挥作用。

这些规范在标准化的意义上来讲相当不得体。作为其中影响最巨大的几个余孽(和妥协失败品),基于 UNIX 早年对文本处理的过于简化的错误假设, ISO C 长期以来没能说清楚什么叫“字符”(character) ; ISO C++ 也有类似的问题。(并且相关术语定义的混乱和微妙的不一致仍然存在于正式(normative) 文本中。)

4

不透明“参数”是此文较有建设性的论点,可惜并没有给出实用的做法。因为语言设计中普遍缺乏约束,这基本只能靠用户(程序员)自觉,效果没法指望。更正确的姿势是,对“字符集”和编码方案透明,减少字符集特性的冗余假设。这样,简单起见只有 ASCII 自然扩展才是 UTF-8 的核心优势。“完整” Unicode 支持从头到尾都是没有说服力的。

5

之所以 Unicode 有必要单独抽出来评价,是因为在概念混淆和把妥协强行扶正为正式惯用技俩推广的不可替代的消极作用。

技术上 Unicode 有显著的问题。作为妥协的以“字符集”为中心特性之一的规范,一个明显的笑话是“字符集”无法精确定义——因为它根本就没有(也没法)规定,什么“感官”“字符”应该是字符集的收录范围。

因为不是随便谁都能往 Unicode 里添加公认接受的字符,这导致了 Unicode 扩展不同于寻常的字符集定义的流程,缺乏文化和厂商中立性,并且可能会对实现添加麻烦。

近期较为显著的例子是 emoji 。一个笑话:为什么 FreeType 突然需要依赖 libpng 才能看起来使 Unicode 渲染的支持“完整”一些呢?

6

把传输编码和文本表示编码比较是一种常识性错误。应该逆转过来,从原始需求出发:如果一开始就做正确的事——就让文本编码尽量只表示人能读写的真正意义上的纯文本,那么这样的比较还有何意义?

反过来,正是因为这种不切实际(需求)的浮夸比较,进一步助长了对不适合作为通信协议的“文本”这种形式的滥用,这是一种恶性循环。

至于只用于真正“文本”的情形,作为东亚语言母语用户,我很清楚几乎从不需要英文字母以外的那些在 UTF-8 中占用两个八元组的西欧文字,作者对此非常地想当然。另外,真要论东亚语言占用空间,而不担心随机定位性质的话, GB18030 跳出来就能把 UTF-8 打得满地找牙(嘛,本是同根生……)。

7

真正优雅的操作是把编解码和对码位的操作分离——虽然会让人怀疑为什么究竟还需要编码这回破事了。

也就是说,这方面 UTF-8 永远被 UCS-4 吊打。

8

这里没有否认对感官字符的计数不重要,却有意无意地忽略了一点,在计数能可靠到显得重要的时候,码位和感官字符是 1:1 的。主要显著例子是没有复杂拼接规则的字母文字(如英文)和东亚语言文字。

绝大多数人口——不管是不是 Unicode 的用户——只关心使用这类文字。

还有一个自以为是的错觉是了解 Unicode Normalization Forms 对所有用户都很重要。文本的(最终)用户通常不应该需要理会编码细节——这点跟“程序员不需要成为一个语言文字专家就能够正确地写一个文件复制程序了”对比,莫名喜感。

9

UTF-16 当然很烂。

性能几乎不是问题

偷换概念。空间占用难道不是性能问题?别忘了为什么 UTF-32 没有把 UTF-8 干趴。

很多人认为不应该用文本通信协议,但实际上通信协议几乎都用英语和 ASCII 字符组成,UTF-8 更有优势。对不同类的字符串用不统一的编码使复杂度大大上升,更容易引发问题。

定义不清的“统一”的编码只会掩盖问题。“几乎都用英语和 ASCII 字符组成”也仍然打不死 GB18030 ,只能说是历史包袱原因。

我们尤其认为给 C++ 标准增加 wchar_t 是一个错误,给 C++11 增加 Unicode 亦然。我们更想要一个能存储任意 Unicode 数据的基本执行字符集。

这是技术错误。让 ISO C++ 引用 ISO 10646 并不十分困难,但要求基本执行字符集的想法不切实际——即便排除 Unicode 可变版本兼容的困扰,要求运行时分辨被编码的字符需要额外的开销并不能被任意实现接受——特别是处理“文本”毫无用处的应用场景上。而只是要求容纳码位,并不需要更改执行字符集。

实际上 ISO C++ 连 ASCII 都仍然懒得完整要求。说到这里是不是又得想想 UTF-EBCDIC 哭晕在厕所呢?

标准库的 facet 有一堆设计缺陷。

依赖区域的特性本来就是一坨笑话。

10

仍然通篇逃避区分内部和外部编码这类关于选型的文本程序开发人员的基本义务,反而把问题搞得更复杂,同时强迫最终用户承担本可能不必要的额外开销。

11

但既然存在,那么说明文本不一定是给人类看的。

更不说明应该存在。

操作子字符串的方法会兴高采烈地把非 BMP 字符一刀两断,返回一个无效的字符串。

为什么判断“字符串”“有效”必须以 Unicode 为准则?

底层框架选错了内部字符串表示的方法,影响了 ASP.NET 这样的 Web 框架:网络应用几乎全要用 UTF-8 来输出(和输入)字符,在大流量的网络应用和服务中,字符串转换导致了显著开销。底层框架选错了内部字符串表示的方法,影响了 ASP.NET 这样的 Web 框架:网络应用几乎全要用 UTF-8 来输出(和输入)字符,在大流量的网络应用和服务中,字符串转换导致了显著开销。

偷换概念。某些特定的应用层不等于“网络”。

更应该说的是选错了服务的领域。要求统一编码,只剩“外部”的场景,就不应该做适配的无用功。所谓物以类聚和 garbage in garbage out 对这里的场景更合适。

不论原来 UTF-8 是否在创造时是作为一个兼容性措施,现在它比任何其它 Unicode 编码更好,也更流行。

“更好”再次被 GB18030 打脸。顺便,到现在 UTF-8 也不是任何一个官方使用东亚语言的政府强制要求支持的标准。

你不打算让你的软件设计完整支持 Unicode,是在开玩笑吗?

指望多数用户的大脑能“支持”非 BMP 字符才更加可笑——事实上是 BMP 字符也太多了。

另外同上,因为外延的不确定性, Unicode “字符集”论及“完整支持”,也是彻头彻尾的笑话。敢明确版本嘛?

那么,既然你打算支持,而非 BMP 字符罕见就不支持,除了给软件测试增加难度,实在没什么好处。

支持才是明显增加测试工作量。

然而,真正值得在意的是,真实的应用程序不怎么操作字符串——只是原封不动地传递字符串。这意味着“几乎定长”几乎没有性能优势(参见“性能”),让字符串短一点倒有可能挺重要。

莫名其妙的逻辑。码位计数可用且码位和感官字符是 1:1 时,分隔完整字符边界的操作是 O(1) 还是 O(n) 在长字符串上性能差异可能相当明显。当然这看来的确不很常见,但这恐怕也是 UTF-8 在编码东亚文字文本上唯一打得过 GB18030 的一个技术理由。

我们坚信,只要让软件 API 默认使用统一的主流编码,程序员不需要成为一个语言文字专家就能够正确地写一个文件复制程序了。

回顾上面的 normalization 打脸。

但是如果你将来打算加个配置文件或日志文件,请考虑把字符串都转换成窄字符串。这样可以免除后患。

这是常见的鸵鸟方法论。正确的做法是之前隐晦提到过的,区分“内部”和“外部”编码方案——如果不能自己发明设计,至少也需要了解应用。这才是处理文本和其它数据的程序员需要做且必须擅长做的基本功课。

我们无法接受,即使是在像文件连接这样简单的情形下,所有现有的代码都得注意到 BOM 的问题。我们无法接受,即使是在像文件连接这样简单的情形下,所有现有的代码都得注意到 BOM 的问题。

这则是彻头彻尾的愚蠢

注意 UTF-8 的 T 的含义。所谓字节序标记本质上是传输时必要的元数据。只有附加假定以字节流传输,才可以省略字节序时指望正确的数据流——但是不一定包括边界。正确的传输总是需要这种附加的 cookie ,区别无非是哪一层做罢了。提供字节序是对边界的一种附加的(不完全)强制验证。在必须考虑边界时,丢掉附加验证并不会更“简单”。

另外,当处理“文件”这种作为外部传输的单独映像时,二进制文件的所谓文件头的元数据基本是不会被任意丢弃,处理“文本”附加额外的不一致的抽象,对“简单”而言,得不偿失。

只有假定没有边界(如 UNIX 的 cat 传输的数据)时才需要显式丢弃元数据的干扰。但这种缺乏类型标记的非结构化数据传输即便是对 shell 编程这种用途来讲,都是不怎么靠谱的,基本上只适合用完即弃。

另外, Unicode 标准实际上并没有显式鼓励传输时丢弃字节序的做法——在数据是已知 UTF-8 有效载荷的情况下,加入冗余 BOM 当然是添乱,所以应予避免。

永远使用 \n (0x0a) 作为行尾标记,即使是在 Windows 上。文件应以二进制模式读写,这保证了互操作性——一个程序在任何系统上都会给出相同结果。

依赖本不必要涉及的二进制表示通常是灾难的开始。

既然 C 和 C++ 标准采用了 \n 作为内部行尾表示,这就导致了所有文件会以 POSIX 惯例输出。

纯属谣言。

文件在 Windows 上用“记事本”打开可能会出问题;然而任何像样的文本编辑器都能理解这样的行尾。

而且,“实际”并不能保证行尾可信。记事本就是一个假定行尾二进制表示的反面例子。

至于为什么这么麻烦嘛……怪一开始的“妥协”好了。不过,考虑控制字符的设计, CR+LF 在含义上是比 LF 要合理的。

另外,实际上很多(应用层网络)协议就是规定用 CR+LF 而不是 LF ,为何在此反复?

我们也偏好 SI 单位、ISO-8601 日期格式,用句点而不是逗号作为小数点。

SI 最近正在改基准单位定义, ISO-8601 一坨几种来着……

一个更稠密的编码就更有利于性能。

对东亚文字一如既往被 GB18030 吊打。

没有纯文本这种概念。没理由在一个名叫“string”的类里存储仅 ASCII 或 ANSI 代码页编码的文本。

然而 std::string 其实彻头彻尾存的是二进制数据,还能在中间夹杂 \0 ……

况且,MultiByteToWideChar 不是最快的 UTF-8↔UTF-16 转换函数。

作为撸过目前为止还没有发现更快的 UTF-8 → UTF-16 解码实现的,我可以负责任地讲,实际上相当慢……虽然通常并不介意这点差距。

将文件存为无 BOM的 UTF-8 可以解决。MSVC 会认为文件已经在正确的代码页,就不会碰你的字符串。

然后 MSVC 很高兴地会瞎猜编码了……嗯,如果不介意打开 IDE 就弹窗警告的话。

如果你打算设计一个接受字符串的程序库,只要用简单、标准且轻量的 std::string 就好。

“接受”和“储存”不是一个概念。

这种观念显然过时了,被 string_view 吊打。

如果你是 C 或 C++ 程序库作者,用 char* 和 std::string,默认 UTF-8 编码,并且拒绝支持 ANSI 代码页——因为这东西本来就不支持 Unicode。

负责的接口设计者应只在外部编码约定使用一致的普适编码,如 UTF-8 ,而避免作为实现细节的内部编码污染接口。至于 ANSI 代码页这玩意儿烂和 Unicode 无关。

12

这是我们基于经验和现实中程序员对于 Unicode 犯下真正的错误

然而对历史上导致这些设计的真正的错误却睁一只眼闭一只眼。

13

支持 Unicode 方可大幅提升用户体验。我们建议选择 UTF-8 而不是 UTF-16、GBK 或 GB18030 作为应用程序的默认编码。

这两句话连在一起,说得 GB18030 不是基于 Unicode 一样……(虽然 UTF-16 的部分应该不会被理解错。)原文并没有讲清的东西一笔带过,含义有些不清楚。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment