C++/Object Pascal中迴圈結構的一點差異及討論 (轉)

worldblog發表於2007-12-11
C++/Object Pascal中迴圈結構的一點差異及討論 (轉)[@more@] 

C++/ Pascal中迴圈結構的一點差異及討論

 :namespace prefix = o ns = "urn:schemas--com::office" />

雖然在所有的語言之中,的都無非是順序、分支和迴圈等幾種結構,比如說,C中的if…else,switch…case,while和do…while, 以及Pascal中的if…then…else,case…of,while…do,repeat…until,幾乎就是一一對應的關係,只不過語法上有小小的差異而已。這樣的相似性使得我們在掌握一門語言之後,再學習其他語言就容易得多了。然而,語言之間的不同是細微而瑣碎的,如果不注意這些差別的話,它們遲早會從不知哪個角落跳出來,讓程式設計師陷入de的噩夢。

 

讓我們來看一個例子。我們知道在一般的迴圈結構中,fo迴圈和while迴圈是可以轉換的,例如,在Pascal中

for I:=StartValue to EndValue do …;

可以轉換成相應的while 迴圈:

I:=StartValue;

while I<=EndValue do begin

  …;

  Inc(I);

end;

看起來,這兩種迴圈是完全一樣的。別忙,真的完全一樣嗎?下面是一段程式碼,你可以先心算一下,看得出來的結果應該是多少:

procedure TForm1.FormCreate(Sender: TObject);

var

  i, j : integer;

  index : integer;

  str : string;

begin

  i := 0;

  j := 3;

  for index:=0 to j do begin

  Inc(i);

  if j<5 then Inc(j);

  end;

  str := IntToStr(i);

 

  i := 0;

  j := 3;

  index := 0;

  while index<=j do begin

  Inc(i);

  if (j<5) then Inc(j);

  Inc(index);

  end;

  Caption := str + ',' + IntToStr(i);

end;

雖然兩個迴圈看起來完全沒有差別,執行後結果卻截然不同:一個是4,一個是6。

 

我們先不忙討論其中的原因,再來看看在C語言中情況如何。

void CTestDlg::OnLButtonDown(UINT nFlags, CPoint point)

{

  int i, j;

  int index;

  i = 0;

  j = 3;

  for (index=0;index<=j;index++)

  {

  i++;

  if (j<5) j++;

  }

  CString str;

  str.Format( "%d", i );

  SetWindowText(str);

 

  CDialog::OnLButtonDown(nFlags, point);

}

 

void CTestDlg::OnRButtonDown(UINT nFlags, CPoint point)

{

  int i, j;

  int index;

  i = 0;

  j = 3;

  index = 0;

  while (index<=j)

  {

  i++;

  if (j<5) j++;

  index++;

  }

  CString str;

  str.Format( "%d", i );

  SetWindowText( str );

  CDialog::OnRButtonDown(nFlags, point);

}

答案如何呢?在Visual C++中,兩個結果都是6。

 

為了排除對程式執行結果的影響,我在Delphi和Visual C++中分別執行不最佳化的程式和最佳化後的程式,結果也是一樣的:Delphi中兩段迴圈的結果分別是4和6,而在Visual C++中,兩個迴圈結果都是6。

 

以上的試驗我是在 SE,Delphi 6.0和Visual C++6.0的環境下測試的。如果你感興趣的話,也可以換VB,或者C++Builder或者其他環境來測試一下,看得出的結果如何。

 

看來現在可以得出答案了。之所以在Delphi中兩個結果會有所不同,是因為我們在迴圈中改變了變數j的值,而j又用來作為測試迴圈終止的條件。我們可以得出這樣的結論:

1.在Object Pascal中,for迴圈和while迴圈對於迴圈如何終止的條件是有差別的。for迴圈在每次迴圈後不會對終止條件再次求值,也就是說,任何一個for迴圈已開始,總共的迴圈次數就已經確定了,即使你想改變也辦不到。而while迴圈的執行次數是不定的,你可以在迴圈體中隨時改變迴圈次數。

2.由於處理方法不同,因此在執行上也相應的有所不同。在Object Pascal Language Gu中已經指明瞭這一點。下面是該書第四章《語法元素》中的一段話:

The difference between this construction and the for...to statement is that the while l re-evaluates finalValue before each iteration. This can result in noticeably slower performance if finalValue is a complex expression, and it also means that changes to the value of finalValue within statement can affect execution of the loop.

可見,由於每次迴圈後都要對迴圈條件重新求值,因此while迴圈的執行效率明顯低於for。因此,如何可能的話,應該儘量多使用for迴圈。為了提高效率,迴圈條件應儘量簡單,能用常量的地方一定要使用常量。

3.在C語言(嚴格的說,上述情況只在Visual C++中驗證過;在其他C環境如C++Builder和GCC中尚未驗證)中,for和while迴圈的處理方法是一致的:在每次迴圈後都要重新求值。

就效率而言,如果迴圈條件中使用的是變數而非常量,特別是迴圈條件是一個比較複雜的的時候,那麼C中的for迴圈應該是比Object Pascal中的for迴圈效率要低一點(僅僅是理論上講)。

4.知道了for和while迴圈在不同語言中的表現,我們就知道應該如何在編寫迴圈的時候避免一些錯誤。例如,下面的程式碼在Delphi中不會有問題:

var

  sl : TStringList;

  I : integer;

begin

  sl := TStringList.Create;

  sl.Add(‘1’);

  sl.Add(‘2’);

  for I:=0 to sl.Count do sl.Add(‘temp string’);

而在VC中,這樣的程式碼就會讓程式進入死迴圈:

CStringArray sa;

sa.Add(“1’);

sa.Add(“2”);

for (int I=0;I

  sa.Add(“temp string”);

在我的機器上,這一段程式在長時間的執行後終於彈出一個“不足”的對話方塊,而這時幾乎所有正在執行的程式都慢的好像龜爬了。希望在你的程式中不會看到這種讓人不快的現象。

 

 

後記:儘管for迴圈和while迴圈的這種微小的差別並不如何令人感興趣,但是一個嚴謹的程式設計師應該知道這些細節。有些程式設計師迷戀於各種各樣讓自己的程式漂亮起來的小,而對程式語言本身的許多東西卻忽略掉了。隨著許多非科班出身的愛好者加入程式設計隊伍,一些不好和不正規的程式設計習慣以及捨本逐末的做法也蔓延開來,流風所及,很少人不受到影響。如果我不是正在啃Object Pascal Language Guide的話,根本不會注意到這麼小的地方,也不會想到我原本以為自己很熟悉了的Pascal語言中還隱藏著一些不為人知的東西。C++也是如此,關心 How to put a icon into the system tray的人,似乎永遠比關心 why the class should have a virtual destructor的人來的多。 基本功不怕深厚,只怕太淺。我的希望是,對於所謂XX天精通XXX,或者XXX從到精通之類的東西,大家還是少看為妙,多花一些時間,靜下心來讀些實實在在的技術資料,比什麼都強,雖然讀到頭皮發麻精力不濟的時候所在多有,但是隻要認真過一遍,你的層次絕對不同。

 


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752043/viewspace-991400/,如需轉載,請註明出處,否則將追究法律責任。

相關文章