簡體   English   中英

C語言有預增量和后增量的歷史原因是什么?

[英]What are the historical reasons C languages have pre-increments and post-increments?

(注意:我不是在詢問預增量與后增量的定義,或者它們在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:單個操作碼,並且速度快 這是因為“遞增1”和“遞減1”是代碼中非常常見的操作(直到今天)。

post和precrement表單僅影響在生成的機器代碼中插入此操作碼的位置。 從概念上講,這模仿了“ 使用結果之前之后增加/減少”。 在一個聲明中

i++;

沒有使用'之前/之后'概念(因此它與++i;相同++i; ),但是

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

它是。 現在,這種區別與設計語言C時的區別同樣重要(這個特定的習語是從其名為“B”的前體復制而來的)。

C語言的發展談起

這個特征[PDP-7的“自動增量”存儲單元“]可能會向Thompson [Ken Thompson,他設計的”B“,C的前身)建議這樣的操作員。 使它們成為前綴和后綴的概括是他自己的。 實際上,自動增量單元並沒有直接用於運算符的實現,並且創新的更強烈動機可能是他觀察到++ x的翻譯小於x = x + 1的翻譯。

感謝@dyp提及此文檔。

當你從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上運行代碼

為了得到超出猜測的答案,很可能你不得不親自詢問Dennis Ritchie等人。

添加到已經給出的答案,我想補充兩個可能的原因:

  • 懶惰/節約空間:

    您可以在輸入文件中使用適當的版本保存一些擊鍵/字節,例如while(--i) vs while(i--) (看看pmg的答案,看看,為什么兩者都有所不同,如果你在第一次運行中沒有看到它)

  • 美學

    出於對稱性的原因,只有一個版本的前后增量/減量可能會讓人覺得缺少某些東西。

編輯:在推測部分提供的輸入文件中添加了少量字節,現在提供了一個非常好的“歷史”原因。

無論如何,整理清單的主要觀點是提供可能的解釋的例子,這些解釋不是太歷史,但仍然在今天舉行。

當然我不確定,但我認為除了個人品味之外,要求一個“歷史性”的理由是從一個不必要的假設開始。

對於C.

讓我們來看看Kernighan&Ritchie的原始理由(原始K&R第42頁和第43頁):

不尋常的方面是++和 - 可以用作前綴或后綴。 (...)在沒有值的情況下(..)根據品味選擇前綴或后綴。 但是htere是特別要求其中一個或另一個的情況。

本文繼續介紹一些在索引中使用增量的示例,其明確目標是編寫“ 更緊湊 ”的代碼。 因此,這些運算符背后的原因是更緊湊的代碼的便利性。

給出的三個示例( squeeze()getline()strcat() )僅使用索引在表達式中使用postfix。 作者將代碼與不使用嵌入式增量的較長版本進行比較。 這證實了重點是緊湊性。

第10頁的K&R重點介紹了這些運算符與指針解除引用(例如*--p*p-- )的結合使用。 沒有給出進一步的例子,但同樣,他們明確表示這種好處是緊湊的。

對於C ++

Bjarne Stroustrup希望具有C兼容性,因此C ++繼承了前綴和后綴增量和減量。

但是還有更多內容:在他的“ C ++的設計和演變 ”一書中,Stroustrup解釋說,最初,他計划在用戶定義的類中只有一個重載,后綴和前綴:

有幾個人,特別是Brian Kernighan,指出這種限制從C角度看是不自然的,並阻止用戶定義一個可以用作普通指針替換的類。

這導致他找到當前的簽名差異來區分前綴和后綴。

順便說一下,沒有這些運算符,C ++就不會是C ++而是C_plus_1 ;-)

考慮以下循環:

for(uint i=5; i-- > 0;)
{
    //do something with i,
    // e.g. call a function that _requires_ an unsigned parameter.
}

您不能使用預遞減操作復制此循環,而無需在for(...)構造之外移動遞減操作,並且最好在一個位置進行初始化,交互和檢查。

一個更大的問題是:一個可以為一個類重載增量運算符(全部4個)。 但是后來運算符嚴重不同:后期運算符通常會導致正在創建的類實例的臨時副本,而前運算符則不會。 這在語義上是一個巨大的差異。

PDP-11有一條對應*p++指令,另一條對應*--p (或者可能反過來)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM