[英]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”的前體復制而來的)。
這個特征[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.