2012-10-08 55 views
16

我不是帕斯卡的新手,但直到現在我還不知道爲什麼DelphiFree Pascal通常會將參數和返回值聲明爲帶符號整數,而我看到它們應該總是正數。例如:爲什麼Delphi和Free Pascal通常更喜歡有符號整數數據類型來無符號數?

  • Pos()返回整型的類型。有可能成爲負面嗎?
  • SetLength()NewLength參數聲明爲Integer類型。字符串是否有負值?
  • System.THandle聲明爲Longint。有句柄的負數嗎?

在Delphi和Free Pascal中有很多決定。這背後有什麼考慮?

+2

FPC的考慮很簡單:與Delphi兼容。德爾福對POS的考慮是(通過TurboPascal)可能回到1974 Pascal報告 –

+4

Arioch:FPC也通過TP。這種東西早在FPC開始研究Delphi兼容性之前就已經決定了。 –

回答

16

在Pascal中,整數(有符號)是基本類型。所有其他整數類型是整數的子範圍。 (這在Borland方言中並非完全正確,在TP中用longint表示,在Delphi中用int64表示,但足夠接近)。

一個重要的原因是,如果中間結果的計算結果爲負數,並且使用無符號整數進行計算,則會觸發範圍檢查錯誤,並且由於大多數較早的編程語言不假定2補數整數,範圍檢查關閉)甚至可能會損壞。

的THandle情況要簡單得多。德爾福沒有一個合適的32位無符號直到D4,但只有一個31位的紅衣主教。 (因爲32位無符號整數不是整數的子範圍,後面的無符號整數是int64的一個子集,它將問題轉移到僅在D2010左右添加的uint64)

因此,在標題中的很多地方簽名類型用於winapi使用無符號類型的地方,可能是爲了避免在這些版本中第32位被破壞,並且自定義卡住了。

但WINAPI案件不同於一般的情況不同。

以後添加一些帕斯卡(和Modula2/3)實施方式中通過在尺寸大於字長較大設定整數繞過這個陷阱,並且要求所有數值類型聲明一個適當的子範圍,如在下面的程序。

第一個持有主假定一切都是整數的一個子集,第二個允許編譯器再次縮放幾乎都記錄下來,以適應在寄存器中,特別是如果CPU有一些操作比字操作大。 (像86,其中32位* 32位的MUL給出了一個64位的結果,或可以檢測字長溢出使用狀態比特(例如,以產生範圍例外而不做了充分的2 *字長加法相加)

var x : 0..20; 
     y : -10..10; 

    begin 
    // any expression of x and y has a range -10..20 
+2

在最近的版本中,Delphi已經轉向在適當的時候使用Windows API類型的無符號類型。例如,現在'THandle'映射到'NativeUInt'。 –

+0

我會期待的,但是我碰巧編譯了昨天最新的VST(5.01),據說它是XE2兼容的。在VST實現的接口定義中,我仍然得到了許多簽名的<->無符號差異。 –

13

那麼,一開始THandle被錯誤地聲明。它在Windows頭文件中沒有簽名,在Delphi中應該如此。事實上,我認爲這在最近發佈的Delphi中得到了糾正。

我想象一下,無符號簽名的偏好在很大程度上是歷史性的,並不是特別重要。但是,我可以想到一個重要的例子。考慮for循環:

for i := 0 to Count-1 do 

如果i沒有符號,Count是0,那麼這個循環從0到$FFFFFFFF這是不是你想要的。使用有符號的整數循環變量避免了這個問題。

帕斯卡是這裏的語法的受害者。等效的C或C++循環沒有這種麻煩

for (unsigned int i=0; i<Count; i++) 

由於語法差異和使用比較運算符作爲停止條件。

這也可能是字符串或動態數組上的Length()返回帶符號值的原因。爲了保持一致性,SetLength()應該接受簽名值。假設返回值Pos()用於索引字符串,它也應該被簽名。

這裏是話題的另一個堆棧溢出討論:Should I use unsigned integers for counting members?

當然,我瘋狂地推測這裏。也許沒有設計,只是出於習慣,使用有符號值的先例已經確立併成爲了先例。

+0

+1,你的評論,以及Marco van de Voort的評論都值得接受。但是,我必須選擇其中之一。 – Astaroth

+2

@Astaroth Marco是[該主題的權威](http://www.freepascal.org/aboutus.var)。他的回答是正確的接受。 –

+0

這是C/C++勝過Delphi的一個領域。 AC/C++'for'循環比Delphi的for循環更加靈活,例如:for(unsigned i = 0; i

5
  • 某些與字符串相關的搜索函數在找不到任何內容時返回-1。
  • 我相信這背後的原因是MaxInt是2GB,這是32位Delphi中字符串的最大大小。這是因爲a single process can have up to 2GB memory
+3

在Delphi中,Pos()在沒有找到任何東西時返回0。 – MBo

+0

'Pos()'是一個不好的例子,但是一些其他的VCL函數/方法(例如'TStrings.IndexOf()')在沒有找到時返回-1。 – afrazier

+0

謝謝,相應地更新了答案;) – whosrdaddy

5

使用帶符號整數有很多原因,甚至有些可能適用於你不打算返回負值的情況

想象一下,我編寫了調用Pos的代碼,並且我想對結果進行數學計算。而有一個負的結果(Pos('x',s)-5)養範圍,檢查異常,下溢,成爲一個非常大的無符號數約4十億,或者去否定,如果Pos('x',s)回報1?其中任何一個對於那些很少考慮這些情況的新用戶來說,問題的來源很多,但歷史悠久的傳統就是通過使用Integer結果,您的工作是檢查負數和零結果,而不是將它們用作字符串偏移量。對於開始和高級程序員來說,使用Integer有一個優點,並且不會有「負值」滾動下來,並變成大的無符號值或引發範圍異常。其次,請記住,在開始編程時,通常在引入Cardinal之類的無符號類型之前,通常會引入Integer(帶符號)類型。初學者往往與功能的工作就像Pos,並且是有意義的使用將創造的副作用最少的不友好集類型。如果範圍大於你絕對需要的範圍(你可能需要的範圍是1到delphi中的最大字符串長度),那麼沒有負面影響。在32位Delphi中使用Cardinal類型對於Pos沒有任何好處,並且選擇它肯定會有缺點。

一旦你到達64位德爾福,但是,理論上你可以有比整數可以容納的字符串大,並且轉移到卡迪納爾不會解決所有潛在的問題。然而,有一個2 + GB串人的機會可能是零,而德爾福64位編譯器不允許一個>2 GB字符串,反正。在我的測試中,我可以在64位Delphi中實現幾乎1 GB的字符串。因此Win64字符串的實際長度限制約爲10億(1073741814)個字符,它使用了近2 GB的實際RAM。在這個限制下,我得到EIntOverflowEAccessViolation,看來我正在碰到Delphi運行時庫(RTL)錯誤,沒有正確定義的限制,所以你的里程可能會有所不同。

+0

「所以你的里程可能會有所不同」 - 無價,+1(: – ComputerSaysNo

相關問題