C语言为什么要使用前置递增和后置递增?这与历史有关。

10

(注意:我并不是在询问C/C++中前置/后置递增的定义,或它们在代码中如何使用。因此,我不认为这是重复的问题。)

C语言的开发者(Dennis Ritchie等人)之所以创建了递增和递减运算符,是有非常好的原因的。但我不明白的是,为什么他们决定创建前缀和后缀递增/递减的区别?

我的感觉是,当C语言正在开发时,这些运算符要比现在更有用。大多数C/C++程序员使用其中一种,而来自其他语言的程序员今天会觉得这种区别很奇怪和令人困惑(注:这仅基于个人经验的陈述)。

他们为什么要这样做,计算机的什么改变使得这种区别现在不那么有用了呢?

值得记录的是,两者之间的区别可以在C++代码中看到:

int x = 3;

cout << "x = 3; x++ == " << x++ << endl;
cout << "++x == " << ++x << endl;
cout << "x-- == " << x-- << endl;
cout << "--x == " << --x << endl;

将作为输出结果呈现

x++ == 3
++x == 5
x-- == 5
--x == 3

1
《C语言的发展(由Ritchie)》(http://cm.bell-labs.co/who/dmr/chist.html)中包含了一段关于递增运算符以及后缀/前缀的内容,但并没有深入讲解。 - dyp
2
副作用才是有用的。 - Fiddling Bits
1
【投机警告】在使用C或C++进行系统编程时,你会写很多代码。任何有助于保持简洁的东西都是好的。前缀和后缀运算符允许程序员在变量操作中浪费更少的空间。我认为这就是它们被添加的原因。不过,我很惊讶C语言没有一个变量值交换运算符。 - Dai
2
@Dai 当我开始学习C语言时,我会使用任何看起来酷炫的技巧,但今天我尊重POLA和其他原则。任何傻瓜都可以编写计算机能理解的代码。好的程序员编写人类能理解的代码。~Martin Fowler - v.oddou
1
您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - Jongware
显示剩余6条评论
6个回答

10

在当时,增加和减少1的操作在硬件上得到了广泛支持:一个单一的操作码,并且速度很快。这是因为“增加1”和“减少1”在代码中是非常常见的操作(至今如此)。

后缀和前缀递减形式只影响了在生成的机器代码中插入该操作码的位置。从概念上讲,这模仿了“在使用结果之前或之后增加/减少”的操作。在单个语句中完成。

i++;

“before/after” 概念未被使用(因此与 ++i; 相同),但在

printf ("%d", ++i);

它就是。当C语言被设计时,这种区别与现在同样重要(这种习惯用法是从其前身“B”中复制的)。

摘自C语言的发展

PDP-7的“自动增量”内存单元可能向汤普森[设计了“B”的Ken Thompson]建议了这样的运算符;使它们成为前缀和后缀的一般化则是他自己的创意。事实上,自动增量单元并没有直接用于运算符的实现,而创新的更强动机可能是他观察到++x的翻译比x=x+1的翻译小。

感谢@dyp提供此文档。


6
Ritchie, C语言的发展:“人们经常猜测C语言和Unix首次流行的DEC PDP-11提供了自动递增和自动递减地址模式,因此它们被创建出来。然而这在历史上是不可能的,因为B语言开发时还没有PDP-11。”看起来PDP-7具有一些可能发挥作用的特征,尽管我认为从文档中并不完全清楚它们是前缀和后缀“++”存在的主要原因。 - dyp
2
翻译变小了。所以,这就是你的搞笑葡萄干:早期编译器缺乏优化器。 - v.oddou
1
B是在Honeywell 6070上“积极”运行的,该机器于至少1967年左右拥有“AOS”(将存储器加一)指令。它没有类似的减法操作,PDP-7也没有(B接下来就在那里)。当C语言进入PDP-11时,DEC团队已经引入了自动寻址模式。 - bishop
1
考虑到在1967年,将一个存储单元加一已经是一件“事情”,并且在大约1970年后完整的“寻址模式”也可用,我怀疑在低级别(指令集)和高级别(B、C)上都有普遍的社区认识,认为这些操作具有价值。 - bishop
1
这个相关答案链接(http://programmers.stackexchange.com/a/331887/33478) 中有《C语言的发展》的更长节选。 - Keith Thompson
显示剩余3条评论

5

当你从n倒数时,非常重要的是使用前缀递减还是后缀递减。

#include <stdio.h>
void foopre(int n) {
    printf("pre");
    while (--n) printf(" %d", n);
    puts("");
}
void foopost(int n) {
    printf("post");
    while (n--) printf(" %d", n);
    puts("");
}
int main(void) {
    foopre(5);
    foopost(5);
    return 0;
}

查看在 ideone 运行的代码


感谢分享这个智慧。我在我的帖子中添加了一个引用。很可能我的假设,即两个版本之间的差异是显而易见的,是错误的,并且在我的回答中被忽略了。 - mikyra

2

对于C语言

让我们看一下Kernighan & Ritchie最初的正当理由(原始K&R第42和43页):

不同寻常之处在于++和--可以作为前缀或后缀使用。在没有需要值的情况下的上下文中,根据口味选择前缀或后缀。但是有些情况下需要特定的一种。

接下来的文字给出了一些使用增量的示例,其明确目标是编写“更紧凑”的代码。因此,这些运算符背后的原因是方便编写更紧凑的代码。

给出了三个示例(squeeze(), getline()strcat()),只在使用索引时在表达式中使用后缀。作者将代码与不使用嵌入式增量的较长版本进行了比较。这证实了他们关注的是紧凑性。

K&R在第102页强调了这些运算符与指针解引用的组合使用(例如*--p*p--)。虽然没有给出进一步的例子,但他们再次明确表示好处是紧凑性。

对于C++语言

Bjarne Stroustrup希望C++具有C兼容性,因此C++继承了前缀和后缀增量和减量。

但是还有更多内容:在他的书《C++设计与演化》中,Stroustrup解释说,最初他计划在用户定义的类中只有一个重载来处理前缀和后缀:

一些人,特别是Brian Kernighan,指出这种限制从C的角度来看是不自然的,并阻止用户定义可用作普通指针替代品的类。

这导致他找到了当前签名差异以区分前缀和后缀。

顺便说一句,如果没有这些运算符,C++就不会是C++,而是C_plus_1 ;-)


2
要得到超出猜测的答案,最好亲自向Dennis Ritchie等人询问。除了已经给出的答案外,我想补充两个可能的原因:
1、懒惰/节省空间: 你可以在输入文件中使用适当的版本来节省一些按键/字节,例如在结构中使用while(--i)while(i--)。如果您第一次运行未看到它,请参阅pmg s的答案,了解两者之间的区别。
2、美学上的考虑: 出于对称性的原因,只有一个前缀或后缀增量/减量版本可能会感觉像错过了什么。
编辑:在揣测部分中添加了节省一些字节的内容,现在提供了一个非常好的"历史"原因。
总之,这个列表的主要目的是给出可能的解释示例,这些解释不太具有历史意义,但仍然保持着今天的重要性。
当然,我不确定,但我认为寻求除个人品味以外的"历史"原因是从不必要的假设开始的。

3
遗憾的是,如果你想问丹尼斯,你需要用通灵板——他在4年前去世了。 - Charlie Martin
2
Mikyra,交出你的宅男徽章吧。接下来你会告诉我们你不知道Leonard Nimoy的事情了吗 :-) - paxdiablo
1
而约翰·纳什...(但在创建C语言时(实际上是B),"懒惰"可能不是一个要点,但在输入文件中节省一些宝贵的字节可能是一个要点。) - Jongware
那么Elvis怎么样?不要告诉我... 无论如何这是个好观点,我会将其加入到猜测中。 - mikyra
无论如何,主要的观点是展示原因可能不太历史悠久,因为对于给出的两个原因,我今天仍然会错过这个功能,并且更愿意认为不支持它们的语言很奇怪。 - mikyra

1
考虑以下循环:
for(uint i=5; i-- > 0;)
{
    //do something with i,
    // e.g. call a function that _requires_ an unsigned parameter.
}

你无法使用预减操作来复制此循环,除非将减量操作移至for(...)结构之外,而且最好将初始化、迭代和检查都放在一个地方。
一个更大的问题是:可以为类重载增量运算符(所有4个)。但是,这些运算符具有关键性差异:后置运算符通常会导致类实例的临时副本被创建,而前置运算符则不会。这在语义上有很大的区别。

0

PDP-11有一条指令对应于*p++,另一条指令对应于*--p(或者可能是相反的顺序)。


请参阅 C语言的发展(Ritchie撰写)。这些运算符在B语言中出现,而B语言是PDP-11之前的语言。 - dyp

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接