2011-09-20 131 views
35

這是一個相當愚蠢的問題,但當爲C或C++中的數組定義for循環時,爲什麼通常使用int而不是unsigned int爲什麼int和unsigned int用於C和C++ for循環?

for(int i;i<arraySize;i++){} 
for(unsigned int i;i<arraySize;i++){} 

我承認做得比數組索引其他東西的時候使用int的好處,並使用C++的容器時,迭代器的好處。這是否僅僅因爲在循環數組時無關緊要?或者我應該一起避免它,並使用不同的類型,如size_t

+15

這是寫少。 – slartibartfast

+0

另外:更具可讀性 – Alex

+0

這就好比你爲什麼不總是打電話給其他人姓名,而只是叫他們的名字? – user482594

回答

29

這是一個更普遍的現象,通常人們不會使用正確的整數類型。 Modern C具有比原始整數類型更可取的語義類型定義。例如,「尺寸」的所有東西都應該輸入爲size_t。如果系統地爲應用程序變量使用語義類型,那麼對於這些​​類型,循環變量也變得更容易。

而且我看到了幾個使用int左右難以檢測到的錯誤。代碼突然在大矩陣和類似的東西上崩潰。只要用正確的類型編碼就可以避免這種情況。

+5

大小的正確類型是'size_t',不幸的是'size_t'已經使用錯誤的類型本身來定義(無符號),並且這是大量錯誤的來源。我更喜歡使用語義上正確的代碼類型(例如'int'),而不是使用形式正確但語義錯誤的類型。使用'int's,你可能會遇到非常大(非常大)值的錯誤......使用'unsigned'值的瘋狂行爲更接近日常使用(0)。 – 6502

+1

@ 6502,意見似乎在很大程度上有所不同。你可以看看我的博客文章:http://gustedt.wordpress.com/2013/07/15/a-praise-of-size_t-and-other- unsigned-types/ –

+2

@JensGustedt:that the語義錯誤不是一種意見,除非你認爲'b.size() - b.size()'應該是大約四十億,當'b'有一個元素並且'a'沒有時是正確的。有人認爲'unsigned'對於非負數來說是一個很棒的想法,但你的觀點是正確的,但我的印象是,他們太過重視名稱而不是真正的意思。在那些認爲無符號是計數器和索引不好的想法的人中有Bjarne Stroustrup ...參見http://stackoverflow.com/q/10168079/320726 – 6502

0

我使用int因爲它需要較少的物理類型,並且無關緊要 - 它們佔用相同的空間量,除非您的陣列有幾十億個元素,如果您不使用16位編譯器,我通常不是。

+5

不使用int也給變量提供了更多的上下文,並且可以被視爲自編碼代碼。也可以在這裏閱讀:http://www.viva64.com/en/a/0050/ –

4

沒有太大區別。 int的一個好處是它正在簽署。因此int i < 0是有道理的,而unsigned i < 0並不多。

如果計算了索引,這可能是有益的(例如,如果某些結果爲負數,您可能會遇到永不進入循環的情況)。

是的,這是少寫:-)

+0

'typedef unsigned us;'並且寫的更多。 – 2011-09-20 19:05:13

+3

@WTP - 你是其中一個誰也不會理解諷刺,即使是在它旁邊的「:-)」?那麼,我認爲沒有治療方法...... – littleadv

+0

負尺寸或負指數沒有意義 –

2

使用int索引數組是傳統的,但仍然被廣泛採用。 int只是一個通用的數字類型,並不對應於平臺的尋址功能。如果它恰好比這更短或更長,那麼當嘗試索引超出的非常大的數組時,可能會遇到奇怪的結果。

在現代平臺上,off_t,ptrdiff_tsize_t保證更多的便攜性。

這些類型的另一個優點是他們給上下文給讀取代碼的人。當你看到上面的類型時,你知道代碼將執行數組下標或指針算術,而不僅僅是任何計算。因此,如果你想編寫防彈,可移植和上下文敏感的代碼,你可以通過幾次擊鍵來完成。

GCC甚至支持typeof擴展,免除了您來自全國各地的地方輸入相同的類型名稱:

typeof(arraySize) i; 

for (i = 0; i < arraySize; i++) { 
    ... 
} 

然後,如果你改變arraySize類型,i變化自動類型。

+2

儘管公平,但除了最晦澀的32位和64位平臺之外,您至少需要40億個元素這些問題纔會顯現出來。具有較小「int」的平臺通常也具有較少的內存,從而使得「int」足夠普遍。 – delnan

+1

@delnan:不是那麼簡單。這種推理在過去導致了非常嚴重的漏洞,即使是那些認爲自己是像DJB這樣的安全之神的人...... –

0

這真的取決於編碼器。一些編碼者更喜歡類型完美主義,所以他們會使用他們比較的任何類型。舉例來說,如果他們通過C字符串迭代,你可能會看到:

size_t sz = strlen("hello"); 
for (size_t i = 0; i < sz; i++) { 
    ... 
} 

而如果他們只是在做一些10倍,你可能仍然可以看到int

for (int i = 0; i < 10; i++) { 
    ... 
} 
0

因爲除非你有一個數組大小超過兩千兆字節的類型char或4千兆字節的類型short或8千兆字節的類型int等等,變量是否簽名並不重要。

那麼,爲什麼輸入更多,當你可以鍵入更少?

+1

但是,如果'arraySize'是可變的並且你想寫防彈代碼, off_t','ptrdiff_t'和'size_t'仍然有一定的意義。 –

+0

是的,如果你可能擁有這樣超級巨大的數組,那麼這是絕對必要的,但是由於人們通常不這樣做,所以他們只是使用易於編寫的'int'。例如,如果你正在用O(n^2)對一個'int'數組進行排序,那麼如果有超過2M個元素,則基本上必須等待數組被排序,如果你有8GB記憶。所以你會發現,通常即使你把索引做得很對,大部分程序在給定輸入時都沒有用處。那麼爲什麼讓他們防彈? – Shahbaz

+0

@Shahbaz:我們大多數人會發現,如果通過一個巨型陣列需要花費數週才能完成,但是通過一個巨型陣列產生一個根shell會發現它是完全不可接受的。 –

0

除了鍵入時間較短的問題之外,原因是它允許使用負數。

由於我們無法預先說明一個值是否可能是負數,因此大多數帶有整數參數的函數都採用帶符號的變量。由於大多數函數使用帶符號整數,因此使用帶符號整數來處理循環等工作通常較少。否則,你有可能不得不添加一些類型轉換。

當我們轉向64位平臺時,對於大多數目的而言,有符號整數的無符號範圍應該足夠綽綽有餘。在這些情況下,沒有太多理由不使用有符號整數。

+0

負值是一個關鍵點,而您的唯一答案不僅僅是提供一個標記。但是,可悲的是,在簽名參數類型和未簽名參數類型之間存在隱式標準轉換,這意味着混合這些參數類型可能只是填充而不是您描述的「不得不添加一些類型轉換」的不方便而安全的場景。而且「當我們轉向64位平臺時,有符號整數的無符號範圍......」對於大多數編譯器/操作系統來說實際上並沒有增長 - 「int」仍然是32位,「長」移動從32到64. –

4

這完全是懶惰和無知。您應該始終使用正確的指數類型,除非您有進一步的信息限制可能的指數範圍,size_t是正確的類型。

當然,如果維度是從文件中的單字節字段讀取的,那麼您知道它在0-255範圍內,並且int將是完全合理的索引類型。同樣,如果循環固定次數(如0到99),int也可以。但還有另一個不使用int的原因:如果在循環體中使用i%2以不同的方式處理偶數/奇數索引,則i%2是當i有符號時,比i是無符號時要昂貴很多...

+1

請參閱我的答案#3,它不是「純粹」懶惰/無知 – chacham15

+3

這並不是'不會改變代碼錯誤的事實。這裏有一個解決方法:'for(size_t i = 10; i - > 0;)' –

31

從邏輯角度來看,使用int爲數組索引更爲正確。

unsigned C和C++中的語義並不真正意味着「不是負面」,而更像是「位掩碼」或「模整數」。

要理解爲什麼unsigned是不是一個很好的類型爲「非負」數請考慮

  • 添加可能爲負整數到非負整數你會得到一個非負整數
  • 兩個非負整數的差異始終是一個非負整數
  • 由負整數乘法非負整數你會得到一個非負結果

很明顯,上述短語沒有任何意義......但C和C++語言的確是如此。

實際上,使用容器大小的unsigned類型是C++的一個設計錯誤,不幸的是我們現在註定要永遠使用這個錯誤的選擇(爲了向後兼容)。你可能會喜歡「unsigned」這個名字,因爲它與「非否定」類似,但名稱無關緊要,重要的是語義......而且unsigned與「非否定」非常相似。

爲此編碼的載體大多數循環時,我個人傾向於形式是:

for (int i=0,n=v.size(); i<n; i++) { 
    ... 
} 

(當然假設矢量大小的迭代過程中沒有改變,而我實際需要的指數在身體,否則for (auto& x : v)...更好)。

儘快從unsigned跑開,使用普通整數有避免因設計錯誤unsigned size_t造成的陷阱的優勢。例如,考慮:

// draw lines connecting the dots 
for (size_t i=0; i<pts.size()-1; i++) { 
    drawLine(pts[i], pts[i+1]); 
} 

上面的代碼中會出現問題,如果pts向量是空的,因爲pts.size()-1在這種情況下,一個巨大的廢話數。處理a < b-1a+1 < b不一樣的表達方式,即使對於常用的值,也像是在雷區中跳舞。

從歷史上看,size_t無符號的理由是爲了能夠使用額外的比特作爲值,例如,能夠在陣列中擁有65535個元素,而不是在16位平臺上擁有32767個元素。在我看來,即使在那個時候,這個錯誤的語義選擇的額外成本也不值得(但如果32767個元素不夠用,那麼65535就不夠長)。

無符號值很好,非常有用,但不代表容器大小或索引;對於規模和索引規則有符號整數的工作要好得多,因爲語義是你期望的。

當您需要模運算屬性或想要在位級別工作時,無符號值是理想的類型。

+1

我認爲你是對的,因爲java(一種「改進的」C++)不支持unsigned int。另外我認爲寫這行的正確方法是:size_t arr_index;對於(size_t i = 1; i <= pts.size(); i ++){ \t arr_index = i_1; } – carlos

+2

@carlos:否。如果'size_t'已被正確定義,那麼**將是正確的方式。不幸的是,一個設計錯誤使'size_t'成爲'unsigned',因此這些值最終具有位掩碼語義。除非你認爲容器的大小是正確的,否則使用'size_t'是錯誤的選擇。不幸的是由標準C++庫做出的選擇,但沒有人強迫我在我的代碼中重複同樣的錯誤。我的建議是,儘量遠離'size_t'並儘可能使用常規整數,而不是使用邏輯,以便它可以與'size_t'一起使用。 – 6502

+2

這不僅僅是16位平臺。使用當前的'size_t',您可以使用例如例如大小的「矢量」具有3G/1G內存拆分的IA-32 Linux 2.1G。如果'size_t'被簽名,如果你將你的向量從<2G增加到更多,會怎樣?突然間大小會變成負值。這只是沒有任何意義。該語言不應強加這種人爲限制。 – Ruslan

0

考慮以下簡單的例子:

int max = some_user_input; // or some_calculation_result 
for(unsigned int i = 0; i < max; ++i) 
    do_something; 

如果max恰好是負值,說-1,則-1將被視爲UINT_MAX(當兩個整數與SAM等級,但不同牌子的煩躁進行比較,簽名的將被視爲未簽名的)。在另一方面,下面的代碼不會有這個問題:

int max = some_user_input; 
for(int i = 0; i < max; ++i) 
    do_something; 

給出了否定的max輸入,循環將被安全地跳過。