为什么隐秘的短标识符在低级编程中仍然如此普遍?


64

曾经有保持指令很好的理由/注册短名称。这些原因不再适用,但是简短的加密名称在低级编程中仍然很常见。

为什么是这样?仅仅是因为旧习惯难以改变,还是有更好的理由?

例如:

  • Atmel ATMEGA32U2(2010?):(TIFR1代替TimerCounter1InterruptFlag),ICR1H(代替InputCapture1High),DDRB(代替DataDirectionPortB)等。
  • .NET CLR指令集(2002):(bge.s而不是branch-if-greater-or-equal.short)等。

较长的非加密名称不是更容易使用吗?


在回答和投票时,请考虑以下内容。这里建议的许多可能的解释同样适用于高级编程,但是,普遍的共识是使用由一个或两个词组成的非加密名称(不包括通常理解的首字母缩写词)。

另外,如果您的主要论点是关于纸质图表的物理空间,请考虑这绝对不适用于汇编语言或CIL,此外,如果您向我展示一个简短名称适合但易读的名称的图表,我将不胜感激。 。从无晶圆厂半导体公司的个人经验来看,可读名称恰好适合,并可以使图更易读。

低级编程与高级语言相比有什么不同的核心之处,高级语言使简洁的加密名称成为低级编程而不是高级编程的理想选择?


82
答:感觉就像您是在使用低级语言进行编程一样。
Thomas Eding

5
隐秘是相对的。 JSR比它所代表的操作码($206502)长三倍,一目了然。
Blrfl 2013年

4
我有点失望,因为那里提供了正确的答案,但是绝对不是公认的答案。对于电路图,此类中断通常以与它们关联的线来命名,并且在电路图上您不想冗长,这不是好习惯或实用性。其次,因为您不喜欢答案并不意味着答案不正确。
Jeff Langemeier 2013年

4
@gnat:尝试set Accumulator32 to BaseIndex32吗?简单地扩展传统缩写并不是使某些内容更具可读性的唯一方法。
Timwi

1
“如果您的主要论点是关于纸质图表的物理空间”,那就不是因为好命名不仅仅考虑名称的清晰性,还要考虑其他因素(我在回答中给出了一些图表,包括那些绘制在图表上的图表)。黑板-只是其中的其他事情之一),而澄清是相对的事情(例如,无论选择什么,熟悉都有助于澄清)。
AProgrammer

Answers:


106

该软件使用这些名称的原因是因为数据表使用了这些名称。由于无论如何都没有数据表,很难理解该级别的代码,因此,使无法搜索的变量名称非常无助。

这就提出了为什么数据表使用短名称的问题。这可能是因为您经常需要在这样的表中显示名称,而这些表中没有足够的25个字符的标识符:

数据表中的TIFR1表

此外,原理图,引脚图和PCB丝网印刷等东西通常在空间上非常狭窄。


7
同样,这个答案并没有真正解决纯软件方面的问题,例如CLR,JVM,x86等:)
Timwi 2012年

12
@romkyns:当您实际阅读这些数据表时,为什么他们使用这些短名称就更明显了。我手头的微控制器的数据表大约有500页,即使在整个过程中都使用简称。如果我们使用更长的名称,表格的宽度将跨越数个页面/屏幕,这使得使用参考非常不便。
在计算机上2012年

27
@romkyns:更长的名字同样可以搜索,但是它们是“非本地的”。如果您听嵌入式工程师的话,他们实际上会说“ tiffer zero”,而不是“ timer zero's interrupt flag”。我怀疑Web开发人员是否会在其方法名称中扩展HTTP,HTML或JSON。
TMN 2012年

6
@KarlBielefeldt er,什么?:) 显然,我不会在当前数据表中找到它,因为它们改用了简称。这并不支持所谓的短名称更容易搜索的
说法

5
不仅数据表受空间限制,还包括原理图。所有这些逻辑组件的引线都需要连接到其他组件。“ TimerCounter1InteruptFlag.clear”也不太适合显示在细小的电线上“ TCIF.C”
AShelly 2013年

60

齐夫定律

您自己可以通过查看此文字来观察到,字长和使用频率通常是成反比的。所使用很频繁,像字itabutyou,和and很短,而使用较少的话想observecomprehensionverbosity较长。这种观察到的频率和长度之间的关系称为齐普夫定律

给定微处理器的指令集中的指令数量通常为数十或数百。例如,Atmel AVR指令集似乎包含约一百种不同的指令(我没有计算),但是其中许多指令是通用主题的变体,并且具有非常相似的助记符。例如,乘法指令包括MUL,MULS,MULSU,FMUL,FMULS和FMULSU。您不需要很长时间就可以查看指令列表,您可以大致理解以“ BR”开头的指令是分支,以“ LD”开头的指令是加载等。对变量也是如此:即使是复杂的处理器也只能提供有限数量的位置来存储值:条件寄存器,通用寄存器等。

由于指令太少,并且长名称需要较长的阅读时间,因此给它们起短名称是有意义的。相比之下,高级语言允许程序员创建大量的函数,方法,类,变量等。与大多数汇编指令相比,每种方式的使用频率要低得多,并且更长,更具描述性的名称对于为读者(和作者)提供足够的信息以了解它们的用途和作用越来越重要。

另外,用于不同处理器的指令集通常对相似的操作使用相似的名称。大多数指令集都包含针对ADD,MUL,SUB,LD,ST,BR,NOP的运算,如果不使用这些确切名称,则通常使用非常接近的名称。一旦了解了一个指令集的助记符,就可以很快适应其他设备的指令集。这样看起来“神秘”你的名字是对像的话一样熟悉andornot程序员谁是本领域的技术水平低编程英寸 我认为大多数在汇编级别工作的人都会告诉您,学习阅读代码并不是低级编程中的更大挑战之一。


2
谢谢迦勒!对我而言,这个出色的答案挽救了一个问题,该问题设法以一种标题收集了四个价值判断:“神秘”,“短”,“仍然”,“如此普遍”
gnat

1
谢谢@gnat,感谢您的评论和慷慨的奖励。
加勒布

37

一般来说

命名的质量不仅要具有描述性名称,还必须考虑其他方面,从而得出如下建议:

  • 范围越全球化,名称应越具有描述性
  • 使用的次数越多,名称应越短
  • 在所有情况下,同一事物都应使用相同的名称
  • 即使上下文不同,不同的事物也应使用不同的名称
  • 差异应易于检测
  • ...

请注意,这些建议存在冲突。

指令助记符

作为汇编语言程序员,使用short-branch-if-greater-or-equalfor bge.s给我带来的印象与我看到的印象相同,就像Algol程序员从事计算几何学SUBSTRACT THE-HORIZONTAL-COORDINATE-OF-THE-FIRST-POINT TO THE-HORIZONTAL-COORDINATE-OF-THE-SECOND-POINT GIVING THE-DIFFERENCES-OF-THE-COORDINATE-OF-THE-TWO-POINTS而不是dx := p2.x - p1.x。我只是不同意在我关心的上下文中第一个更具可读性。

注册名称

您从文档中选择正式名称。文档从设计中选择名称。设计使用了很多图形格式,其中长名称不够用,设计团队将使用这些名称使用数月甚至数年。出于两个原因,他们不会使用“第一个计时器计数器的中断标志”,而是会在其架构中以及讲话时将其缩写。他们知道这一点,并使用系统的缩写,例如TIFR1这样,以减少混乱的机会。这里有一点是,这TIFR1不是随机缩写,而是命名方案的结果。


4
TIFR1真的更好的命名方案比InterruptFlag1虽然,或者IptFlag1如果你真的要短?
Timwi

4
@Timwi,InterruptFlagIptFlag均优于IF以同样的方式,EnumerableInterfaceItfcEnumerable均优于IEnumerable
AProgrammer 2012年

@AProgrammer:我认为您的回答和您的评论是最好的,如果可以的话,我会将其标记为接受。那些只认为身体上的限制决定简称的人是错误的。这次讨论会为你有意思:37signals.com/svn/posts/...
alpav

5
@alpav您是否知道您的链接与该答案相反?如果有的话,InterruptFlag1出于更加清晰的原因,它完全支持。
罗曼·斯塔科夫

24

除了“旧习惯”的原因外,30年前编写并仍在使用的旧版代码非常普遍。尽管有些经验不足的人认为,对这些系统进行重构以使其看起来很漂亮,但付出的代价很小,却要付出很高的代价,并且在商业上不可行。

出于非常充分的原因,与硬件接近且要访问寄存器的嵌入式系统倾向于使用与硬件数据表中相同或相似的标签。如果在硬件数据手册中将该寄存器称为XYZZY1,则表示该变量的变量很可能为XYZZY1,或者如果程序员过得很愉快,则为RegXYZZY1。

就而言bge.s,它类似于汇编程序-对于少数需要知道它的人来说,较长的名称不太容易理解。如果您不能走开bge.s,认为branch-if-greater-or-equal.short会有所作为-您只是在使用CLR而并不知道。

您将看到简短的变量名的另一个原因是由于在该软件所针对的领域内缩写的广泛传播。

总结-反映行业影响和行业数据表等外部影响的简短缩写变量名。通常不希望使用软件内部简短的缩写变量名。


如果我理解您用来捍卫“ bge.s”的论点,那么TIFR1对那些想知道它的人来说,可读性比TimerCounter1InterruptFlag正确吗?
罗曼·斯塔科夫

2
@romkyns:绝对-在这种情况下,更少的就是更多。...与CNTR不同,T1FR1的定义可能是“ Counter”,“ Control”,“ Can Not Trace Route”等。
mattnz

“如果您无法绕过bge.s并认为branch-if-greater-or-equal.short会有所作为-您只是在使用CLR而并不知道。” 我不知道 我非常了解x86汇编,但是每次编写循环时,我都必须查找所有j?指令的含义。进行更明显地命名的说明肯定会对我有所帮助。但是也许我是例外而不是规则。我很难记住琐碎的细节。
科迪·格雷

11

这里有很多不同的想法。我不能接受任何现有的答案作为答案:首先,有利于这种可能的因素有很多,其次,我不可能知道哪一个是最显著之一。

因此,这是其他人在此处发布的答案摘要。我将其发布为CW,目的是最终将其标记为已接受。如果我错过了一些内容,请编辑。我试图重新表达每个想法,以简洁明了地表达它。

那么,为什么隐式短标识符在低级编程中如此常见?

  • 因为它们中的许多在各自的领域中都很常见,因此需要一个非常短的名称。这会使学习曲线恶化,但考虑到使用频率,这是一个值得权衡的问题。
  • 因为通常只有少数可能性是固定的(程序员无法添加到这套可能性中)。
  • 因为可读性是习惯和实践的问题。branch-if-greater-than-or-equal.short最初不是更具有可读性bge.s,但也有一些实践情况被逆转。
  • 因为通常必须手工完全键入它们,因为低级语言通常不带有功能强大的IDE,这些IDE具有很好的自动完成功能,或者a / c不可靠。
  • 因为有时需要将大量信息打包到标识符中,而且即使按高级标准,可读名称也不会令人接受。
  • 因为这就是历史上低级环境的样子。改掉习惯需要有意识的努力,冒着使那些喜欢旧方法的人烦恼的风险,并且必须被证明是值得的。坚持既定的方式是“默认”。
  • 因为它们中的许多来自其他地方,例如原理图和数据表。这些又受空间限制的影响。
  • 因为负责事物命名的人员甚至从未考虑过可读性,或者没有意识到他们正在制造问题,或者是懒惰。
  • 因为在某些情况下,名称已成为数据交换协议的一部分,例如某些编译器使用汇编语言作为中间表示。
  • 因为这种风格可以立即识别为低级,因此对极客来说很酷。

我个人认为,其中一些实际上并没有导致新开发的系统选择这种命名方式的原因,但是我觉得在这种答案中过滤掉一些想法是错误的。


10

我要把帽子扔进这个烂摊子。

高级编码约定和标准与低级编码标准和实践不同。不幸的是,大多数都是遗留代码和旧思想流程的遗留物。

但是,有些确实可以达到目的。当然,BranchGreaterThanBGT更具可读性,但是现在有一个约定,它是一条指令,因此在过去30年来用作标准已获得了一定的吸引力。他们为什么从它开始,可能是指令,变量等的任意字符宽度限制;他们为什么要保留它,这是一个标准。该标准与使用int作为标识符相同,在所有情况下使用Integer都更容易理解,但是对于编程时间超过几周的任何人来说,这都是必须的.​​..不。为什么?因为这是标准做法。

其次,正如我在评论中所说,许多中断都被命名为INTG1和其他隐式名称,它们也有一定的用途。在电路图是命名您的线路好习惯和这种冗长其杂波图,伤害可读性。所有详细信息均在文档中处理。并且由于所有接线图均具有这些中断线的简称,因此中断本身也具有相同的名称,以保持嵌入式设计人员从电路图一直到对代码进行编程的一致性。

设计师对此有一定的控制权,但是像其他任何领域/新语言一样,硬件之间也遵循惯例,因此每种汇编语言都应保持相似。我可以看一下汇编的摘要,而无需使用该指令集就可以得到代码的要点,因为它们遵循约定,LDA或与其相关的某些关系可能正在加载寄存器MV可能正在将某物从某处移动到另一处在其他地方,这与您认为好的或高级的实践无关,它本身就是一种语言,因此具有其自己的标准,意味着您作为设计师应该遵循的标准,而这些标准通常并没有那么随意。他们看起来。

我将为您提供这一点:让嵌入式社区使用冗长的高级实践就像要求化学家总是写出化学化合物一样。化学家将它们写为自己的简称,该领域的其他任何人都可以理解,但是新来者可能需要一点时间来进行调整。


1
我确实觉得“我们将使用隐秘名称,因为这使低级编程具有这种感觉”“我们将使用隐秘名称,因为这是低级编程的约定”,因此几乎相同,因此+1从我的角度出发,我会考虑将其作为我最初接受的一种不太发炎的变种。
罗曼·斯塔科夫

6
+1供化学家参考,因为它为不同编程领域提供了很好的类比。

4
+1我也从未理解过,如果人们能读懂更多的“ DiHydrogenOxyde”,为什么人们会使用诸如“水”之类的简短而隐秘的名称
Ingo,

6

他们使用隐秘的短标识符的原因之一是因为它们对开发人员而言并非隐秘。您必须意识到他们每天都在使用它,而这些名称实际上是域名。因此,他们内心知道TIFR1的确切含义。

如果有新开发人员加入团队,他将必须阅读数据表(如@KarlBielefeldt所述),以便他们对此感到满意。

我相信您的问题使用了一个错误的示例,因为确实在这类源代码上,您通常会看到很多非域内容不必要的crypt标识符。

我之所以这么说,是因为当编译器没有自动完成您键入的所有内容时,就会存在不良习惯。


5

摘要

初始主义是许多技术和非技术领域中普遍存在的现象。因此,它不限于低级编程。有关一般讨论,请参见Wikipedia上有关首字母缩写词的文章。我的答案特定于低级编程。

神秘名称的原因:

  1. 低级指令是强类型的
  2. 需要将很多类型信息打包到一个低级指令的名称中
  3. 从历史上看,单字符代码通常用于包装类型信息。

解决方案及其缺点:

  1. 有一些现代的低层命名方案比历史上的更一致。
    • 虚拟机
  2. 但是,仍然需要打包很多类型信息。
    • 因此,仍然可以在任何地方找到隐秘的缩写。
  3. 改进的线对线可读性将帮助新手低级程序员更快地使用该语言,但不会帮助理解大量低级代码。

完整答案

(A)可以使用更长的名称。例如,与汇编助记符中的7个字符相比,C ++ SSE2内部函数的名称平均为12个字符。 http://msdn.microsoft.com/zh-CN/library/c8c5hx3b(v=vs.80).aspx

(B)然后问题转到:低级指令需要多长时间/非加密?

(C)现在,我们分析这种命名方案的组成。以下是同一低级指令的两种命名方案:

  • 命名方案1: CVTSI2SD
  • 命名方案2: __m128d _mm_cvtsi32_sd (__m128d a, int b);

(C.1)低级指令始终是强类型的。不能有歧义,类型推断,自动类型转换或重载(重用指令名称表示相似但不等效的操作)。

(C.2)每个低级指令必须在其名称中编码很多类型信息。信息示例:

  • 建筑家族
  • 运作方式
  • 参数(输入)和输出
  • 类型(有符号整数,无符号整数,浮点数)
  • 精度(位宽)

(C.3)如果说出每条信息,该程序将更加冗长。

(C.4)各种供应商使用的类型编码方案具有悠久的历史根源。例如,在x86指令集中:

  • B表示字节(8位)
  • W表示字(16位)
  • D表示双字“双字”(32位)
  • Q表示qword“四字”(64位)
  • DQ表示dqword“双四字”(128位)

这些历史参考文献没有任何现代意义,但仍然存在。更一致的方案是将位宽值(8、16、32、64、128)放入名称中。

相反,LLVM是低级指令中朝着一致性方向迈出的正确一步:http : //llvm.org/docs/LangRef.html#functions

(D)不管指令命名方案如何,低级程序已经很冗长且难以理解,因为它们专注于执行的细节。更改指令命名方案将提高线对线的可读性,但不会消除理解大型代码操作的困难。


1
改进的线对线可读性必然会对理解整个内容产生一定的影响,但是,仅命名本身并不能使它变得平凡。
罗曼·斯塔科夫

3
另外,有点离题,但CVTSI2SD携带的信息不多于ConvertDword2DoubleConvInt32ToFloat64,但后者虽然更长,但可以立即被识别,而前者必须被解密...
Roman Starkov

2

人类仅偶尔读取和写入汇编,大多数情况下,它只是一种通信协议。即,它最常用作编译器和汇编器之间的中间序列化基于文本的表示形式。此表示越详细,该协议中的不必要开销就越大。

对于操作码和寄存器名称,长名称实际上会损害可读性。短助记符更适合于通信协议(在编译器和assember之间),并且汇编语言通常是一种通信协议。短助记符对于程序员来说更好,因为编译器代码更易于阅读。


如果需要节省空间,只需gzip即可!...如果不需要开销,请改用二进制格式!如果您使用的是文字,那么您的目的就是提高可读性-那么为什么不一直使用并使其正确可读?
罗曼·斯塔科夫

2
@romkyns,压缩两个本地进程之间的基于文本的通信协议?那是新东西。二进制协议的健壮性要差得多。这是一种Unix方式-基于文本的协议可偶尔读取。它们足够可读。
SK-logic

对。您的前提是,我读和写这些寄存器或CIL指令的名称几乎不会引起开销。但是考虑一下;在编程,它们与其他任何编程语言中的奇数方法或变量名一样被频繁使用。难道如此之少以至于多余的几个字节都很重要吗?
罗曼·斯塔科夫

1
我尊重您有权对名称的长短有所不同的权利,但是您是否真的在编译器中命名方法和本地名称,例如TIFR,还是它们倾向于包含完整的单词呢?
Roman Starkov

1
我认为与可读性和短期权衡无关的差异。我当然认为它们是不同的,就像变量与函数不同,函数与类型不同一样。我只是不明白为什么操作码和寄存器名称会从极其简短的优点中受益,以至于在您不了解它的作用之前必须先为每个新遇到的文档查阅文档。到目前为止,您唯一的争论是高效存储,如果我没有记错的话。您是真的吗?...还是其他原因?
罗曼·斯塔科夫

1

通常这是惯用的。就像@TMN在其他地方所说的那样,就像您不编写import JavaScriptObjectNotationimport HypertextTransferProtocolLibrary使用Python一样,您也不Timer1LowerHalf = 0xFFFF使用C 编写。在上下文中,它看起来同样荒谬。每个需要了解的人都已经知道。

某些方面可能会因为某些嵌入式系统的C编译器供应商为了实现对嵌入式编程更有用的功能而偏离语言标准和语法的事实而产生了抵制变化的能力。这意味着在编写低级代码时,您不能总是使用您喜欢的IDE或文本编辑器的自动完成功能,因为这些自定义会破坏其分析代码的能力。因此,短寄存器名,宏和常量的实用程序。

例如,HiTech的C编译器为需要在用户内存中指定位置的变量提供了特殊的语法。您可以声明:

volatile char MAGIC_REGISTER @ 0x7FFFABCD;

现在,存在的唯一可以对此进行解析的IDE是HiTech自己的IDE(HiTide)。在任何其他编辑器中,您每次都必须从内存中手动将其键入。这很快就变老了。

还有一个事实,当您使用开发工具检查寄存器时,通常会在一个表中显示几列(寄存器名称,十六进制值,二进制值,最后值十六进制等)。长名称意味着您必须将名称列扩展为13个字符才能看到两个寄存器之间的差异,并在数十行重复的单词中播放“发现差异”。

这些听起来像是愚蠢的小问题,但是不是每个编码约定都旨在减轻眼睛疲劳,减少不必要的打字或解决其他百万个小小的投诉吗?


2
您所有的论点都是有道理的。我完全理解所有这些要点。但是,您不认为完全一样的事情适用于高级代码吗?您还需要在C#函数中查看本地列表。语境是主观的,File.ReadAllBytes对于以前习惯的人来说看起来可能也很长fread。那么...为什么要区别高级代码和低级代码?
罗曼·斯塔科夫

@romkyns -我同意你的观点,但我不认为我们其实并不处理高层次的代码非常不同。缩写词在许多高级上下文中都很好,我们只是没有意识到,因为我们更习惯于该缩写词或与此相关的任何方案。当我实际上用低级代码编写函数或创建变量时,我会使用漂亮的描述性名称。但是当我提到一个寄存器时,我很高兴我可以看一眼字母和数字,并很快想到“ T =计时器,IF =中断标志,1 =第一个寄存器”。在这方面,几乎就像有机化学一样:P
有意思,

@romkyns -此外,在纯粹的实际意义,我想在C#中的一些微处理器的IDE和应用开发的登记表之间的区别是:高达寄存器表可能看起来像:Timer1InterruptFlagTimer2InterruptFlag,..., ,Timer9InterruptFlag,,IOPortAToggleMask IOPortBToggleMask等x100。在更高级的语言中,您将使用相差更多的变量...或使用更多的结构。Timer1InterruptFlag相较于,是75%的不相关噪声T1IF。我认为您不会在C#中创建庞大的变量列表,而这些变量几乎没有什么不同。
2013年

1
@romkyns-您可能不知道的是,您所描述的已经发生了转变。Microchip最新的编译器随附的库比仅寄存器等更为冗长和更具描述性的库。UARTEnable(UART1, BITS_8, PARITY_N, STOP_1, BAUD_115200)。但是它们仍然令人难以置信的笨拙,并且涉及很多间接性和低效率。我尝试尽可能使用它们,但是大多数时候,我将寄存器操作包装在自己的函数中,并从更高层的逻辑中调用它。
2013年

@detly:CCS编译器有这种方法,其他一些处理器也有。我通常不喜欢他们。寄存器规范足以编写使用寄存器的代码,并且足以使某人阅读使用寄存器的代码来查看这些寄存器的功能。如果将N值写入硬件预分频器的操作将周期设置为N + 1(非常常见),set_prescalar(TMR4,13);则IMHO 的正确含义要比清楚得多TMR4->PSREG=12;。即使有人查看编译器手册以找出第一个代码的功能,也可能仍然需要...
supercat

1

令我惊讶的是,没有人提到懒惰,而且没有讨论其他科学。我作为程序员的日常工作向我表明,程序中任何类型的变量的命名约定都受三个不同方面的影响:

  1. 程序员的科学背景。
  2. 程序员的编程技巧。
  3. 程序员的环境。

我认为讨论低级或高级编程是没有用的。最后,它总是可以固定在前三个方面。


第一个方面的解释:首先, 许多“程序员”不是程序员。他们是数学家,物理学家,生物学家,甚至是心理学家或经济学家,但其中许多不是计算机科学家。它们中的大多数都有自己特定于域的关键字和缩写,您可以在它们的“约定”命名中看到它们。它们通常被困在其域中,并使用那些已知的缩写而不考虑可读性或编码指南。

第二个方面的解释: 由于大多数程序员都不是计算机科学家,因此他们的编程技能受到限制。这就是为什么他们通常不关心编码约定,而是更多地关注领域特定约定的原因,如第一个方面所述。另外,如果您没有程序员的技能,那么您也不会了解编码约定。我认为大多数人并不认为迫切需要编写可理解的代码。就像失火了一样。

第三个方面的解释: 不太可能破坏您的环境惯例,这些惯例可能是您必须支持的旧代码,公司的编码标准(由不关心编码的经济学家经营)或您所属的领域。如果某人开始使用隐秘名称,并且您必须支持他或他的代码,则您不太可能更改隐秘名称。如果贵公司没有编码标准,我敢打赌几乎每个程序员都会编写自己的标准。最后,如果您被域用户所包围,您将不会再写他们使用的其他语言。


没有人提到懒惰 -也许是因为这与这里无关。而且不讨论其他科学,这很容易哦:这个站点不用于讨论。这是对问题和答案
蚊蚋

懒惰是合法的原因。几乎所有程序员都是懒惰的人(否则,我们会手动完成所有操作!)。
Thomas Eding
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.