2013-04-26 68 views
4

爲什麼int a[5] = {1,2,3,4,5,6}發出警告,而int a[5] = {1,2,3,4,5}; a[5] = 6;不?爲什麼溢出數組初始化會發出警告,但溢出賦值不會?

當我最初聲明數組大小爲5時,這是否是一個很好的做法?

如果我不知道數組的大小,該怎麼辦?我可以聲明這樣的int a[]嗎?

+1

請注意你的意思是「a [5] = 6;'。 – djechlin 2013-04-26 17:00:21

+0

@djechlin謝謝,是的,我的意思是'a [5] = 6;'。更新。 – mushroom 2013-04-26 17:09:41

+0

任何人都可以給這個更精確的標題? – djechlin 2013-04-26 17:10:05

回答

8

int a [5] = {1,2,3,4,5,6}爲什麼int a [5] = {1,2,3,4,5}; a [5] = 6;纔不是?

由於您知道初始化語句中變量的大小,該賦值給出警告,並且顯然違反了聲明的大小。你沒有aa[6] = 6這行的數組大小,所以對於編譯器來說,它看起來沒問題。當然,警告的級別從編譯器變爲編譯器,並且對於某些編譯器,您可以指定額外的警告。

例如,使用gcc,您可以使用標記-Wextra-Wall來獲取很多警告。接收警告是一件好事,因爲編譯器可以幫助您找到可能的警告,而無需調試代碼。當然,如果你解決這些問題:-)

它們纔是好它是一個很好的做法,以做到這一點時,我最初宣佈陣列 大小爲5?

這是從來沒有分配到內存中的一個地方,你沒有宣佈一個整數一個很好的做法 - 這個值被寫入在那裏你不能確定,而且可以覆蓋另一個變量,或更糟糕的是,部分覆蓋了一些其他變量或堆棧。由於這種內容與編譯器和編譯器不同,正如@PascalCuoq所指出的那樣,它被稱爲未定義的行爲,這是你想要不惜一切代價避免的東西。當然,由於它是未定義的,可能會發生這樣的情況,即你的程序在聲明之後執行得很好,但這是一個非常糟糕的做法。

但是,初始化一個固定大小的數組沒有任何問題,如果它不會改變的話。您應該避免幻數並使用常量,例如MAX_NUMBER_OF_PERMUTATIONSCURRENCIES_SIZE

我可以這樣聲明:int a []?

將它聲明爲int a[]是初始化固定數組和編譯器可以指定元素數的簡寫。例如:

int a[] = {1,2,3}; //this is good 
int b[3] = {1,2,3}; //same from above 

在過去,通常是宣佈int a[];但它在每一個編譯器不工作,所以應儘量避免。 (感謝@PascalCuoq指出這一點)

如果我不知道我的數組的大小?

如果你不知道你的數組的大小,你應該聲明爲指針,像int * a和使用mallocrealloccalloc和類似的系統調用自己管理的內存。請做好工作並瞭解free - 世界將在稍後謝謝你。如果您正在尋找動態內存分配,您應該閱讀指針而不是數組。

+0

有人可以修復我的英語嗎?我不是母語的... – fotanus 2013-04-26 17:45:57

+0

謝謝@AndrewColeson – fotanus 2013-04-26 18:37:20

+0

您在回答「我可以像這樣聲明int a []?」,而是使用'malloc()',...「你應該聲明爲你寫的。需要一個指針和'int a [];'不** **聲明一個指針。 – 2013-04-26 18:46:15

6

int a [5] = {1,2,3,4,5,6}爲什麼int a [5] = {1,2,3,4,5}; a [6] = 6;纔不是?

警告只是編譯器試圖幫助你。每次你做錯了事,編譯器都不必警告。編寫不正確的程序的方法太多,編譯器無法警告所有的程序。

當我最初宣佈數組大小爲5時,這樣做是否是一種好的做法?

No.當a是大小5的陣列,訪問a[6]a[5]調用未定義的行爲(譯註:它是非常差)。關於未定義行爲的傳統說法是,它允許編譯器使守護進程飛出你的nose

在1992年初在該組的討論,經常說 「當編譯器遇到[給定未定義構造]它是合法的,它使惡魔飛出你的鼻子「(暗示 ,編譯器可以選擇任何任意離奇的方式來解釋代碼而不違反ANSI C標準)。

因此,總之,總是avoid未定義的行爲在你的C程序中。

如果我不知道陣列的大小怎麼辦?我可以像這樣int a []聲明它嗎?

不,你不能。在聲明數組時,您必須知道數組的大小。 int a[];會聲明一個不完整的數組,這不是你想要的(here是一個關於不完整類型的鏈接,但是如果你想要訪問五元素數組的第六個元素,你只是不想聽到不完整的類型然而)。如果您不知道最終需要的尺寸,請了解malloc()realloc()

+0

感謝您的明確解釋。然而,我仍然對'int a []'部分感到困惑,因爲它與Neil的回答相矛盾:「你可以聲明數組爲'int a []',但是你聲明的只是一個指針到一個數組「。 – mushroom 2013-04-26 17:01:46

+3

@jon這正是爲什麼你應該讓自己一本好書,而不是從互聯網上的陌生人的答案分段學習C.這一個晚期版本應該沒問題。如果你已經閱讀過,我可以爲你找一本好書:http://en.wikipedia.org/wiki/The_C_Programming_Language – 2013-04-26 17:04:13

+0

「不,它調用未定義的行爲(翻譯:它非常糟糕)。」你能否詳細說明或給我一個鏈接?我不知道這是一件壞事。 – fotanus 2013-04-26 17:05:56

2

在你的第二個例子中,編譯器正在做指針數學來確定把整數放在哪裏。在較早的C實現中,數組不是一級類型,所以沒有可用的範圍檢查。一些較新的可以檢測出界外錯誤。

如果你需要動態分配數組,這是正確的模式:

int *a; 
// calloc(elements, size) 
// create an array of 6 elements, 0..5 
a = calloc(6, sizeof(int)); 
// use like a[5]=6, be sure to free(a) later. 

檢查callocfree引用。

2

的問題:爲什麼

int a[5] = {1,2,3,4,5,6}; 

給予警告,同時

int a[5] = {1,2,3,4,5}; 
a[5] = 6; 

不?

這實際上是一個很好的問題,我沒有一個很好的答案。

至於C語言標準說什麼,初始化是約束違反,這意味着符合標準的編譯必須問題進行診斷,並可以拒絕該方案。 (海灣合作委員會這樣做,-pedantic-errors選項,我建議使用)。

在第二種情況下,a[5] = 6;未定義的行爲。該語言不需要診斷,但它肯定允許一個,並且足夠聰明的編譯器可能警告它。當編譯器看到a[5]的賦值時,它知道(或可能知道)a只有5個元素,並且您試圖訪問第6個元素。海灣合作委員會,至少不這樣做,至少不是我試過的選項。

爲什麼標準要求診斷第一個病例但不是第二個病例?因爲在編譯時總能檢測到第一個錯誤。編譯器知道a有多大,因爲聲明正在創建它;它必須爲它分配內存。在這樣的聲明中,編譯時總是知道大小。 (有可變長度數組,但不能使用這種初始化工具。)

a[5] = 6;中,編譯器將不得不執行更多的分析來檢測錯誤,這並非總是可能。你可以將其輕鬆地寫:

a[n] = 6; 

這將是一樣糟糕,如果n恰好是5 - 但是編譯器必須確定在編譯時n將不得不的價值運行時間來診斷它。編譯器有時可以進行這種分析,但有些情況下理論上不可能。

那麼爲什麼不gcc診斷a[5] = 6;的特殊情況?這可能只是gcc的開發者選擇投入時間和其他資源的問題。

+0

我非常喜歡你的討論,但我不會說「理論上不可能」警告在運行時可能發生的越界訪問。我只想說,「出於理論上的原因,編譯器製造商必須接受錯誤肯定或者錯誤否定,或者兩者兼而有之,即使他們有最好的診斷意圖。」正如你已經指出''[5]'不是一個困難的情況。 – 2013-04-26 17:46:56

+0

@PascalCuoq:如果用戶輸入的索引值或從文件讀取的索引值,我會說在理論上可能發出編譯時警告,說它超出了界限。你總是可以發出一個警告,說明它可能會超出範圍,但是你可能會在虛假警告中淹死。有些提及停機問題可能適用於此。 – 2013-04-26 19:05:14

+1

我並不是建議引用Halting問題。 「理論上的原因」足夠模糊,說得對,而我覺得Halting問題太多了。事實上,我定期討論這個問題,所以我將它寫下來,以備將來重複使用。無論如何,這肯定不適合這個評論:http://blog.frama-c.com/index.php?post/2013/04/26/Of-compiler-warnings-discussions – 2013-04-26 21:37:33