c語言 為什麼不進行下標檢查,總結下指標和陣列

2021-06-26 07:55:22 字數 2824 閱讀 1315

假設:

int array[ 10 ];

int *ap = array + 2;

ap[ -1 ]:下標引用就是間接訪問表示式,只要將它轉換成那種形式對它進行求值即可。ap只想第三個元素(下標為2),所以使用偏移量-1使我們得到它的前乙個元素,也就是 array[ 1 ]。

ap[ 9 ]: 表示式看上去正常,但是實際上存在問題。它對等的表示式是 ap[ 11 ],表示式的結果是乙個指標表示式,但是它所指向的位置越過了陣列的右邊界,根據標準它是非法的,但是很少由編譯器報錯。標準表示它的行為是未定義的,但在絕大多數機器上,它將訪問哪個碰巧儲存於陣列最後乙個元素後面第二個位置的值,我們沒有辦法**它的值。因此這個表示式將訪問某個任意變數的值,這個結果估計不是你想要的。

最後這兩個例子說明了:為什麼下標檢查在c是一件很困難的事情。標準並沒有提出這個要求,最早的c編譯器並沒有檢查下標,最新的也不會檢查。

因為下標引用可以用於任意的指標,而不僅僅是陣列名。作用於指標的下標引用的有效性既依賴於該指標當時恰好指向的內容,也依賴於下標的值。

c的下標檢查所涉及的開銷,比我們剛開始想的要多。編譯器必須在程式中插入指令,證實下標表示式的結果所引用的元素和指標表示式所指向的元素屬於同乙個陣列。

這個比較操作需要程式中所有陣列的位置和長度方面的資訊,這將占用一些空間。當程式執行時,這些資訊必須進行更新,以反映自動和動態分配的陣列,這又將占用一定的時間。因此,即使是那些提供了下標檢查的編譯器通常也會提供乙個開關,允許去掉下標檢查。

提供乙個有趣的例子,假定下面表示式所處的上下文環境和前面的相同,它的意思是什麼?

2[ array ]

它的答案:合法!轉換成對等的間接訪問表示式:

*( 2 + ( array ) )

內層括號是冗餘,可以去掉,所以它和下面的是等價的:

*( array + 2 )

這個詭異技巧可行,因為c實現下標的方法,對於編譯器而言,兩種形式並無差別,但是,你絕不該寫成 2[ array ] ,因為它大大影響了可讀性。

再次總結一下:

在絕大多數表示式中,陣列名的值是指向陣列第乙個元素的指標。只有兩個例外:一,sizeof 返回整個陣列所占用的byte的大小 而不是乙個指標所占用的byte的大小。二,單目操作符 & 返回乙個指向陣列的指標,而不是乙個指向陣列第乙個元素的指標的指標。

除了優先順序不同之外,下標表示式 array[ value ] 和間接訪問表示式 *( array + value ) 是一樣的,因此,下標不僅可以用於陣列名,也可以用於指標表示式中。

這樣以來,編譯器很難檢查下標的有效性!指標表示式可能比下標表示式效率更高,但是下標表示式決不能比指標表示式效率更高。但是,以犧牲程式可維護性為代價換來的程式執行時的效率的提高可不是好主意。

指標和陣列並不相等。陣列的屬性和指標的屬性大相徑庭。當我們宣告乙個陣列時,它同時也分配了一些記憶體空間,用於容納陣列元素。但是,當我們宣告乙個指標時,它只分配了用於容納指標本身的空間。

當陣列名作為函式引數傳遞時,實際傳遞給函式的是乙個指向陣列第乙個元素的指標。 函式所接收到的引數實際上是原函式引數的乙份拷貝,所以函式可以對其進行操作而不會影響實際的引數。 但是,對指標引數進行間接訪問(×)操作,允許修改原先陣列的元素。陣列形參既可以宣告為陣列,也可以宣告為指標。 這兩種宣告形式只有當它們作為函式的形參時才相等。

比如:對於一位陣列,char temp[ 10 ];  可以寫成 void fun( char *temp )或者void fun( char temp ) 

對於二維陣列,char temp[ 3 ][ 10 ]; 可以寫成 void fun( char temp[ ][ 10 ] )或者 void fun( char (*temp)[ 10 ] )

注意:對於多為陣列,只能將第一維寫成 (陣列 / 指標) 的形式,編譯器必須知道第二個以及以後各維的長度,才能對各下標進行求值,因此原型中必須宣告這些維的長度,第一維的長度不重要,因為在計算下標值的時候用不到它。

當陣列名作為函式的引數傳遞時,實際傳遞給函式的是乙個指向陣列第乙個元素的指標。函式所接收到的引數實際上是原引數的一根拷貝,所以函式可以對其進行操作而不會影響實際的引數。但是,對指標引數執行間接訪問(×)操作允許函式修改原先的陣列元素。陣列形參既可以宣告為陣列,也可以宣告為指標,見上面。這兩種形式只有當它們作為函式的形參時才是等價的。

陣列也可以用初始值列表進行初始化,初始值列表就是由一對花括號包圍的一組值。靜態變數(包括陣列)在程式載入到記憶體時得到初始值。自動變數(包括陣列)每次當執行流進入它們宣告所在的**塊時都要使用隱式賦值語句重新初始化。 如果初始值列表包含的值的個數少於陣列元素的個數,陣列最後幾個元素就用預設值進行初始化。 如果乙個被初始化的陣列的長度在宣告中未給出,編譯器將使用這個陣列的長度設定為剛好能夠容納初始列表中所有值的長度。字元陣列也能夠用一種很像字串常量的方式進行初始化。

多維陣列實際上是以為陣列的一種特型,就是它的每乙個元素本身也是乙個陣列。多維陣列中的元素根據「行」主序進行儲存,也就是最右邊的下標率先變化。 多維陣列名的值是乙個指向它第乙個元素的指標,也就是乙個指向陣列的指標。對該指標進行運算將根據它所指向陣列的長度對運算元進行調整。 多維陣列的下標引用也是指標表示式。 當乙個多維陣列名作為引數傳遞給乙個函式時,它所對應的函式形參的宣告中必須顯示指明第二維(和接下去所有維)的長度。 由於多維陣列實際上是複雜元素的一維陣列,乙個多維陣列的初始化列表就包含了這些複雜元素的值。 這些值的每乙個都可能包含巢狀的初始值列表,由陣列各維的長度決定。 如果多維陣列的初始化列表是完整的,它的內層花括號可以省略。 在多維陣列的初始值列表中,只有第一維的長度會被自動計算出來。

我們還可以建立指標陣列。 字串的列表可以以矩陣的形式儲存,也可以以指向字串常量的指標陣列形式儲存。 在矩陣中,每行必須與最長字串的長度一樣,但是它不需要任何指標。 指標陣列本身要占用空間,但是每個指標所指向的字串所占用的記憶體空間就是字串本身的長度。

C語言編譯器不檢查陣列下標越界

這兩天被人問了乙個問題說假如c c 訪問下表越界的陣列元素會報錯麼,於是充滿好奇心的我動手試了一下,wtf,果然沒有報錯,但是會給程式帶來莫名其妙的結果 比如十次的迴圈但是變成了死迴圈,但八次卻可以 例 1 include2 include34 int a 5 5int main 6 8 int a...

c語言為什麼要宣告

隨便舉個例子 你在乙個編譯單元 相當於乙個.c檔案中 這樣寫 int main 編譯器該怎麼編譯這個檔案呢?是壓入乙個int型的1,還是壓入乙個long型的1,還是壓入乙個double型的1,對於返回值,是當成float看待呢?還是當成int看待,然後轉化為float型別,這些不能確定的話,就無法編...

C語言 C 為什麼要記憶體對齊

例一 struct aa 例二 struct aa 首先請告訴我,例 一 例二對應的空間大小分別是多少?答案是 12位元組 8位元組。這是為什麼呢?明明有相同的成員,可是為什麼記憶體大小就不一樣呢?這就是接下看來我們要討論的記憶體對其問題了。將每乙個資料的起始位置,在記憶體的對其位置處。無論如何,為...