C++ C语言有前增量和后增量的历史原因是什么?

C++ C语言有前增量和后增量的历史原因是什么?,c++,c,post-increment,decrement,pre-increment,C++,C,Post Increment,Decrement,Pre Increment,(注意:我不是问增量前和增量后的定义,也不是问它们在C/C++中是如何使用的。因此,我不认为这是一个重复的问题。) C语言的开发人员(Dennis Ritchie等人)创建增量和减量运算符有很好的理由。我不明白的是,为什么他们决定区分前后增量/减量 我的感觉是,在开发C时,这些操作符比现在有用得多。大多数C/C++程序员使用一种或另一种语言,而来自其他语言的程序员今天发现这种区别既奇怪又令人困惑(注意:这完全基于轶事证据) 为什么他们决定这样做,计算中有什么变化使得这种区别在今天不再那么有用 对

(注意:我不是问增量前和增量后的定义,也不是问它们在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;

按1递增和递减在当时的硬件中得到了广泛的支持:一个操作码,速度很快。这是因为“递增1”和“递减1”是代码中非常常见的操作(直到今天)

后抄表和预抄表只影响在生成的机器代码中插入此操作码的位置。从概念上讲,这模拟了“在使用结果之前或之后增加/减少”。一句话

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

是的。如今,这种区别与设计语言C时一样重要(这个特殊的习语是从它的前身“B”复制而来)

这一特性[PDP-7的“自动增量”存储单元]可能向汤普森[Ken Thompson,他设计了C的前身“B”);使它们同时成为前缀和后缀的概括是他自己的。事实上,自动增量单元并没有直接用于运算符的实现,而创新的更大动机可能是他观察到的++x的转换比x=x+1的转换小


感谢@dyp提到本文件。

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

要得到一个超越猜测的答案,你很可能必须亲自询问丹尼斯·里奇等人

除了已经给出的答案,我想补充两个可能的原因:

  • 懒散/节省空间:

    您可能能够在诸如
    while(--i)
    while(i-)
    之类的结构中使用适当的版本在输入文件中保存一些击键/字节。(看看pmg的答案,如果你在第一次运行中没有看到,为什么两者都会产生影响)

  • 美学

    由于对称性的原因,只有一个版本,无论是增加前版本还是增加后版本/减少后版本,都可能会感觉遗漏了一些东西

编辑:在提供推测部分的输入文件中增加了几个字节,现在也提供了一个很好的“历史”原因

无论如何,列出清单的要点是给出可能的解释的例子,这些解释不太具有历史意义,但在今天仍然适用


当然,我不确定,但我认为,除了个人品味之外,询问一个“历史”原因是从一个不一定正确的假设开始的。

当你从
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;
}
#包括
void foopre(int n){
printf(“pre”);
而(--n)printf(“%d”,n);
认沽权(“”);
}
无效邮政编码(int n){
printf(“post”);
而(n--)printf(“%d”,n);
认沽权(“”);
}
内部主(空){
foopre(5);
邮政总局(5),;
返回0;
}
有关C,请参见。

让我们看看Kernighan和Ritchie的原始理由(原始K&R第42页和第43页):

不寻常的是,++和-,可以用作前缀或 作为后缀。(…)在不需要任何值的上下文中(…)选择 前缀或后缀根据口味而定。但在某些情况下 其中一个是特别需要的

本文继续介绍一些在索引中使用增量的示例,其明确目标是编写“更紧凑的”代码。因此,这些运算符背后的原因是更紧凑的代码的方便性

给出的三个示例(
squere()
getline()
strcat()
)在使用索引的表达式中仅使用后缀。作者将代码与不使用嵌入增量的较长版本进行比较。这证实了重点在于紧凑性

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

用于C++

<> Bjarne Stroustrup希望具有C兼容性,所以C++继承前缀和后缀增量和减量。 但还有更多的内容:在他的书《C++的设计和发展》中,斯特劳斯特鲁普解释说,最初,他计划在用户定义的类中只为后缀和前缀设置一个重载:

一些人,特别是布莱恩·克尼汉,指出 从C的角度来看,限制是不自然的,并且阻止了用户 从定义一个可用于替换 普通指针

这使他找到了当前签名的差异,以区分前缀和后缀


顺便说一下,没有这些运算符C++将不是C++,而是cPulsUL1;-)p> 考虑以下循环:

for(uint i=5; i-- > 0;)
{
    //do something with i,
    // e.g. call a function that _requires_ an unsigned parameter.
}
如果不将递减操作移出,则无法使用预递减操作复制此循环
#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;
}
for(uint i=5; i-- > 0;)
{
    //do something with i,
    // e.g. call a function that _requires_ an unsigned parameter.
}