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

时间:2015-05-25 00:23:00

标签: c++ c post-increment decrement pre-increment

(注意:我不是在询问预增量与后增量的定义,或者它们在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

6 个答案:

答案 0 :(得分:10)

当时硬件广泛支持递增和递减1:单个操作码和快速。这是因为&#34;递增1&#34;并且&#34;递减1&#34;在代码中是一个非常常见的操作(直到今天)。

post-andcrecrement表单只影响在生成的机器代码中插入此操作码的位置。从概念上讲,这模仿&#34;在使用结果&#34;之前或 之前增加/减少。在一个声明中

i++;
&#39>之前/之后&#39;概念未被使用(因此它与++i;相同),但在

printf ("%d", ++i);
是的。现在这种区别与设计语言C时的区别同样重要(这个特殊的习语是从它的前身“#34; B&#34;”中复制而来。)

来自The Development of the C Language

  

此功能[PDP-7&#34;`自动增量&#39;记忆细胞&#34;]可能会向Thompson [Ken Thompson,他设计&#34; B&#34;,C的前身]建议这样的操作员;使它们成为前缀和后缀的概括是他自己的。实际上,自动增量单元并没有直接用于运算符的实现,而且创新的更强烈动机可能是他观察到++ x的翻译小于x = x + 1的翻译。

感谢@dyp提及此文档。

答案 1 :(得分: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;
}

请参阅code running at ideone

答案 2 :(得分:2)

为了得到超出猜测的答案,很可能你必须亲自询问Dennis Ritchie等人。

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

  • 懒惰/节约空间:

    您可以使用while(--i)while(i--)等结构中的相应版本在输入文件中保存几个击键/字节。 (看看pmg的答案,看看为什么两者都有所不同,如果你在第一次运行中没有看到它的话)

  • 美学

    出于对称的原因,只有一个版本的前后增量/减量可能会让人觉得缺少某些东西。

编辑:在推测部分的输入文件中添加了少量字节,现在提供了一个非常好的&#34;历史性的&#34;原因也是。

无论如何,整理清单的主要目的是提供一些可能的解释,这些解释不是太历史性,但仍然在今天举行。

当然我不确定,但我认为要求一个历史性的&#34;个人品味之外的其他原因是从一种不必要的假设出发。

答案 3 :(得分:2)

对于C

让我们来看看Kernighan&amp; Ritchie原始辩护(原始K&amp; R第42和43页):

  

不寻常的方面是++和 - 可以用作前缀或   作为后缀。 (...)在没有值的情况下(..)选择   根据品味的前缀或后缀。但是htere就是这样的情况   一个或另一个是专门要求的。

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

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

K&amp; R高亮显示第102页,将这些运算符与指针解除引用结合使用(例如*--p*p--)。没有给出进一步的例子,但同样,他们明确表示这种好处是紧凑的。

对于C ++

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

但还有更多内容:在他的书中&#34; C ++的设计与发展&#34;,Stroustrup解释说,最初,他计划只有一个重载用户定义的类中的后缀和前缀:

  

有几个人,特别是Brian Kernighan,指出了这一点   从C角度看,限制是不自然的,并且阻止了用户   定义一个可以用作替代的类   普通的指针。

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

顺便说一下,没有这些操作符,C ++就不会是C ++而是C_plus_1; - )

答案 4 :(得分:1)

考虑以下循环:

select er_mental,
    case when ISNUMERIC(er_mental) = 1 then
        case when er_mental > 1 then 'Yes'
             when er_mental < 1 then 'No'
        end
        when er_mental = 'Not obtainable' then 'No'
    end as er_mental_resp
from #hlq;

您无法通过预递减操​​作复制此循环,而无需在for(...)构造之外移动递减操作,并且最好进行初始化,交互和检查所有在一个地方。

一个更大的问题是:可以为一个类重载增量运算符(全部4)。但是后来运算符严重不同:后期运算符通常会导致正在创建的类实例的临时副本,而前运算符则不会。这在语义上是一个巨大的差异。

答案 5 :(得分:0)

PDP-11只有一条指令对应*--p,另一条指令对应from collections import Counter frequency_per_character = Counter(open(filename).read().lower()) (或可能反过来)。