1. 程式人生 > >不一樣的C語言-當sizeof遇上陣列名

不一樣的C語言-當sizeof遇上陣列名

當sizeof遇上陣列名

一、 sizeof是何方神聖

說到sizeof,首先必須知道的一點:sizeof不是函式,sizeof是操作符。sizeof的作用就是,計算一個物件或型別所佔的記憶體位元組數。

1.1 sizeof的語法

sizeof():
這也正是為什麼sizeof會有那麼多人誤解其為函式。括號裡面可以是一個數據物件,也可以是一個數據型別。對於有括號的這種語法來說,你大可以不用理會括號裡面是一個數據型別還是一個數據物件。
sizeof object:
初次見面,以為是語法錯誤,其實這也是sizeof的一種用法,計算資料物件所佔的記憶體位元組數。如果計算的是一個數據型別,那麼請用上面的括號形式。如此看來,那麼多程式設計師使用()的語法形式是有道理的,因為放之四海皆準。

1.2 sizeof如何計算大小的

sizeof既然那麼偉大,那麼他是如何偉大的,什麼時候做了這個偉大的事情呢?我們知道,當我們定義一個變數的時候,我們就獲得了一個可以供我們使用的記憶體空間,那麼計算分配記憶體大小的事情又是誰做的呢?如果你對程式的編譯和執行稍作了解,你就會知道,這個事情是編譯器幫我們乾的。編譯器在編譯的時候,告知了系統(這麼說其實不太準確,暫且這麼理解),執行這個程式的時候,需要如何分配記憶體。那麼sizeof也正是發生在這個時期。編譯的時候,編譯器就能夠知道我們sizeof的型別或者物件所需要的記憶體大小。

1.3 為什麼需要sizeof來計算大小

如果你學習過JAVA,那麼你會發現,JAVA中根本不存在sizeof操作符。難道是因為JAVA不夠完善嗎?其實不然。JAVA之所以沒有sizeof操作符,也正如JAVA設計者所言:JAVA不需要sizeof。JAVA的執行時需要JVM的,不同的作業系統都有對應的JVM,在JVM中保證了所有型別的大小一致,不管你執行在32位還是64位的機器上,資料型別的大小都是已知的,這也正是JAVA可移植效能夠實現的最根本的原因。回到話題來,為什麼C/C++需要sizeof?顯然,對於一個合格的C程式設計師,我們應該知道,我們所寫的程式執行在不同結構的機器上的時候,型別的大小有所區別。當我們需要知道一個型別的大小才能繼續寫程式的時候,我們就需要sizeof。這就是為什麼我們需要sizeof。

二、 陣列並沒有那麼簡單

陣列和指標是C語言中的重頭戲,那麼陣列是什麼資料型別呢?按照其他書籍的介紹,陣列是複合型別(與基本資料型別結合構成的型別)。陣列是同一型別的資料的集合,在記憶體中的表現為一串連續的記憶體,記憶體的大小為單個數據型別的大小與資料量的乘積。在我個人看來,陣列更應該稱為資料結構。資料結構中的順序表,其實現的核心就是陣列。由此可見,陣列更像是一種C語言預先定義的資料結構。

2.1 陣列的語法

資料型別 陣列名[資料個數]
倘若我們在定義的時候就初始化,那麼陣列中第一個維度的資料大小可以不寫。

2.2 陣列名到底是什麼

陣列名是一段記憶體空間的名字,其指向了這段記憶體空間。我們講到了sizeof,那麼sizeof(陣列名)計算出來的,理所應當計算的就是陣列名指向的這段記憶體空間的位元組數,事實也正是如此。但是,當我們對陣列名繼續深究下去,我們就會開始對sizeof(陣列名)疑惑了。
int arr[3] = {0,1,2};
printf(“%p”,arr);

上面的例子,列印的將會是陣列首元素的地址。由此我們可以推測:arr是一個指標。我們這麼推測是有理由的,因為指標才能儲存一個地址。但是倘若我們想現在下定論,就需要考慮sizeof(arr)。這時候你再來回答,sizeof(arr)的大小是多少。兩種答案:4或者12。答案是4的顯示,我們把arr當做一個指標,指標的記憶體在所佔的位元組數為4;12則是把陣列名看做一種資料結構,這個資料結構的大小為12位元組。正確答案大家知道是12。到這裡我們的疑惑就更深了,既然編譯器認為arr不是指標,但是arr在列印地址的時候,又儲存了一個地址。這就是我們需要說的:陣列名是一個類似於指標的資料結構變數名。在普通情況下,我們可以把陣列名當做指標來使用。繼續看下面的一個例子:
int arr[3] = {0,1,2};
int arr2[3] = {3,4,5};
arr = arr2;

上面的程式碼我們想完成的事情是將arr2陣列賦值給arr陣列,但是編譯的時候是錯誤的,因為arr在表現為指標屬性的時候,實際上是一個常量指標。我們不會對一個已經初始化了的變數再次賦值。

2.3 陣列作為函式形參

當陣列作為函式形參的時候,陣列名則淪為指標,既然是一個指標,我們就可以對其自增、自減和修改。在函式體內,陣列名僅僅作為一個數組的指標。我們知道了他是指標,只要我們搞清楚這個指標實際指向的資料型別,那麼對於指標的使用就顯得簡單了。

三、 sizeof與陣列

int arr[2][2] = {
{0,1},
{2,3}
};
printf(“%d,”,sizeof(arr));
printf(“%d,”,sizeof(arr[0]));
printf(“%d\n”,sizeof(arr[0][0]));

三個printf將會列印什麼?答案:16,8,4

3.1 sizeof(arr)

弄清楚答案的緣由我們先了解一下多維陣列。
C/C++中,多維陣列實際上是一位陣列。多維陣列在分配記憶體的時候,是分配一段完整的連續的記憶體空間。這麼說可能理解不深刻。我們先來了解一下JAVA中的多維陣列,就以二維陣列來看。JAVA中的二維陣列在分配空間的時候,首先會分配一個大小為2的陣列,其儲存的是2個一維陣列的起始地址。這兩個一維陣列在記憶體上並不一定是連續的。再看看C中的陣列,就能理解其完整的連續的意思。
正是因為如此,所以sizeof(arr)才會列印16。因為sizeof計算的是陣列名對應的記憶體空間的大小,不管維度大小。

3.2 sizeof(arr[0])

接下來我們看一下sizeof(arr[0])。如果外面沒有sizeof操作符,arr[0]在此處如果換成指標來看待,其運算如下:(arr + 0 * 2),其指向的依然是陣列的首行首元素的地址。但是在sizeof操作符下,arr[0]顯然不能夠當做指標來看待,這時應該理解為二維陣列的一維陣列(邏輯上是如此,實際上多維陣列還是一位陣列)。arr[0]指向的是第一行的一位陣列,我們可以理解為arr[0]就是一個數組名,其記憶體空間為arr陣列的前2個元素對應的記憶體空間,我們sizeof,得到的結果就應該為8。

3.3 sizeof(arr[0][0])

最後來看sizeof(arr[0][0]),arr[0][0]表示訪問的是二維陣列的首行一維陣列的首元素,其變數就是一個int型別,所以結果為4。
由此看來,陣列名與sizeof操作符搭配的時候,其表現也並不簡單,其依然還是保留了陣列名作為一種資料結構的特性。