文章目錄
- 這應該不是議資料和指標,應該是閱讀筆記吧!但是其這個題目更好點。
- C專家編程第四章內容:令人震驚的事實:數組和指標並不相同。自己也收穫不小,瞭解很多基礎的東西,對那些初入C開發人員有很好的協助。那廢話少說吧。
- 我們總以為資料和指標是完全等同的,兩者是可以互換的,這種說法是片面的。我們在編程中經常使用全域變數,在其他檔案中聲明中也可以使用這個全域變數。下面就舉個例子說明:
- /*一些使用mango的代碼*/
- 這裡,檔案1定義int 變數mango,但檔案2聲明它為指標int *型,這裡顯然是類型不符,也說明了數組和指標並不是完全等同的。這樣使用肯定是錯誤的,代碼不可能正常運行。那麼應該怎麼聲明呢, 如下:
- 1 那麼什麼是聲明?什麼是定義?
- 在C語言中對象必須有且只有一個定義,但可以有多個聲明。定義是一個特殊的聲明,它建立一個對象;聲明只是說明了在其他地方建立了這個對象,它允許在這裡使用:
- *******************************************************************************
- 定義 只能出現在一個地方 確定對象的類型並分配記憶體,用於建立新的對象
- 聲明 可以多次出現 描述對象的類型,用於指代其他地方定義的對象
- *******************************************************************************
- 只要記住下面的內容可以分清定義和聲明:
- 聲明相當於普通的聲明:它說明的並非自身,而是描述其他的地方建立的對象。
- 定義相當於特殊的聲明:它為對象分配記憶體。
- Extern 對象聲明告訴編譯器對象的類型和名字,對象的記憶體配置在別處進行。由於並未在聲明中為數組分配記憶體,所以並不需要提供關於數組長度的資訊。對於多維陣列需要提供除最左邊一維其他維的長度—這是給編譯器足夠的資訊產生相應的代碼。
- 2 、數組和指標式如何訪問的
- 這裡講述對數組的引用和對指標的引用有何不同之處,首先需要注意的是“地址y“和“地址y的內容”之間的區別。這裡一個相當微妙之處,是在大多數編程語音中我們用同一個符號來表示這兩個東西,有編譯器根據上下文環境來判斷他的具體含義。以一個簡潔的例子來說明:
- 出現在賦值符左邊的符號有事被稱為左值(由於它位於“左手邊”或“表示地點”),出現在賦值符右邊的符號有時則被稱為右值(由於它位於右手邊)。編譯器為每個變數分配一個地址(左值),這個地址在編譯時間可知,而且該變數在運行時一直儲存於這個地址。相反,儲存於變數中的值(右值)只有在運行時才知。如果需要用到變數中儲存的數值,編譯器就發出品質從指定地址讀取變數並將它儲存在寄存器中。
- 這也是為什麼在if(-1 == x)判斷語句中,如果將“==”誤寫為“=”編譯器會警示的原因(相信很多人只知道這麼用,不知道為什麼吧?)
- 這裡的關鍵在於每個符號在編譯時間可知。所以如果編譯器需要一個地址(可能還需要加上位移量)來執行某種操作。它可以直接進行操作,並不需要增加指令首先取得具體的地址,相反對於指標在運行時首先取得它的當前值,然後才能對它進行接觸引用操作。具體看:
- 這也是為什麼extern char a[] 與 extern char a[100]等價的原因。這兩個都說明a是個數組,也就是個記憶體位址,數組內的字元可以從這個地址中找到。編譯器並不需要知道這個地址有多長,因為它只產生偏離起始地址的位移地址。從資料中提取一個數值,只需要簡單的知道現實a的地址加上下標,需要的字元就位於這個地址中。
- 相反,如果聲明extern char *p,它告訴編譯器P是個指標,它指向的是一個字元。為了取得這個字元,必須知道P的內容,把他作為字元的地址取其內容就是這個字元,指標的訪問比較靈活,但需要增加一次額外的提取。
- 現在讓我看下定義成指標,當以數組方式引用會發生什麼,看下面的例子:
- 再議數組:
- 在C標準中對“什麼時候數組和指標時相同的”作了如下的聲明:
- 規則1:運算式中的數組名(與聲明不同)被編譯器當做一個指向該數組第一個元素的指標。
- 規則2:下標總是與指標的位移量相同。
- 規則3:在函數參數聲明中,數組名悲編譯器當做指向該數組第一個元素的指標
- 下面詳細介紹這3個規則的含義:
- 規則1:“運算式數組名”就是指標
- 上面的規則1,2一起理解,就是對數組下標的引用總是可以寫成“一個指向數組的起始地址的指標加上位移量”。例如,假如我們聲明:
- 規則2:C語言把數組下標作為指標的位移量
- 我們通常認為:在編寫數組演算法時,使用指標比數組 更效率 。這種說法在通常情況下是錯誤的。在一維數組和指標引用所產生的代碼並不具有顯著的差別。這裡可以參考前面的內容,自己在研究下。
- 規則3:作為函數參數的數組名等同於指標
- 數組形參是如何被引用的?
- 展示了對一個下標形式的數組形參進行訪問所需要的幾個步驟:
- 注意:有一種操作只能在指標裡進行而無法在數組中進行,那就是修改它的值。數組名是不可修改的左值,它的值是不能改變的。
這應該不是議資料和指標,應該是閱讀筆記吧!但是其這個題目更好點。 C專家編程第四章內容:令人震驚的事實:數組和指標並不相同。自己也收穫不小,瞭解很多基礎的東西,對那些初入C開發人員有很好的協助。那廢話少說吧。 我們總以為資料和指標是完全等同的,兩者是可以互換的,這種說法是片面的。我們在編程中經常使用全域變數,在其他檔案中聲明中也可以使用這個全域變數。下面就舉個例子說明:
檔案1:
Int mango[100];
檔案2:
Extern int *mango; /*①*/
...
/*一些使用mango的代碼*/ 這裡,檔案1定義int 變數mango,但檔案2聲明它為指標int *型,這裡顯然是類型不符,也說明了數組和指標並不是完全等同的。這樣使用肯定是錯誤的,代碼不可能正常運行。那麼應該怎麼聲明呢, 如下:
Extern int mango【】;/*②*/
說明:
①語句聲明mango是個int*型;②聲明mango為int 型數組,長度尚未確定,其儲存在別處定義。
1 那麼什麼是聲明?什麼是定義? 在C語言中對象必須有且只有一個定義,但可以有多個聲明。定義是一個特殊的聲明,它建立一個對象;聲明只是說明了在其他地方建立了這個對象,它允許在這裡使用:*******************************************************************************定義 只能出現在一個地方 確定對象的類型並分配記憶體,用於建立新的對象聲明 可以多次出現 描述對象的類型,用於指代其他地方定義的對象******************************************************************************* 只要記住下面的內容可以分清定義和聲明:聲明相當於普通的聲明:它說明的並非自身,而是描述其他的地方建立的對象。定義相當於特殊的聲明:它為對象分配記憶體。 Extern 對象聲明告訴編譯器對象的類型和名字,對象的記憶體配置在別處進行。由於並未在聲明中為數組分配記憶體,所以並不需要提供關於數組長度的資訊。對於多維陣列需要提供除最左邊一維其他維的長度—這是給編譯器足夠的資訊產生相應的代碼。 2 、數組和指標式如何訪問的 這裡講述對數組的引用和對指標的引用有何不同之處,首先需要注意的是“地址y“和“地址y的內容”之間的區別。這裡一個相當微妙之處,是在大多數編程語音中我們用同一個符號來表示這兩個東西,有編譯器根據上下文環境來判斷他的具體含義。以一個簡潔的例子來說明:
出現在賦值符左邊的符號有事被稱為左值(由於它位於“左手邊”或“表示地點”),出現在賦值符右邊的符號有時則被稱為右值(由於它位於右手邊)。編譯器為每個變數分配一個地址(左值),這個地址在編譯時間可知,而且該變數在運行時一直儲存於這個地址。相反,儲存於變數中的值(右值)只有在運行時才知。如果需要用到變數中儲存的數值,編譯器就發出品質從指定地址讀取變數並將它儲存在寄存器中。 這也是為什麼在if(-1 == x)判斷語句中,如果將“==”誤寫為“=”編譯器會警示的原因(相信很多人只知道這麼用,不知道為什麼吧?) 這裡的關鍵在於每個符號在編譯時間可知。所以如果編譯器需要一個地址(可能還需要加上位移量)來執行某種操作。它可以直接進行操作,並不需要增加指令首先取得具體的地址,相反對於指標在運行時首先取得它的當前值,然後才能對它進行接觸引用操作。具體看:
這也是為什麼extern char a[] 與 extern char a[100]等價的原因。這兩個都說明a是個數組,也就是個記憶體位址,數組內的字元可以從這個地址中找到。編譯器並不需要知道這個地址有多長,因為它只產生偏離起始地址的位移地址。從資料中提取一個數值,只需要簡單的知道現實a的地址加上下標,需要的字元就位於這個地址中。 相反,如果聲明extern char *p,它告訴編譯器P是個指標,它指向的是一個字元。為了取得這個字元,必須知道P的內容,把他作為字元的地址取其內容就是這個字元,指標的訪問比較靈活,但需要增加一次額外的提取。
定義成指標,但以數組方式引用
現在讓我看下定義成指標,當以數組方式引用會發生什麼,看下面的例子:
對照圖C的訪問方式
Char *p = “abcdefgh“; ... p[3]
和圖A的訪問方式:
Char a[] = “abcdefgh”; .... a[3]
在這兩種情況下,都可以取得字元‘d’,但是兩者的途徑是不一樣的。
當書寫了 extern char *p,然後用p【3】來引用其中的元素時,其實質是圖A和圖B的訪問方式的組合。首先,進行圖B所示的間接引用。然後,A所示用下標作為位移量進行直接存取。更為正式的說明是,編譯器將會:
1.
取得符號P的地址,提取儲存於此處的指標。
2.
把下標所表示的位移量與指標的值相加,產生一個地址。
3.
訪問上面的地址,取得數值。
到這也許已經知道為什麼文章開頭舉得例子中檔案2中聲明Extern int *mango; /*①*/
為什麼會出現編譯失敗的問題?既然把P聲明成指標,那麼不管P原先定義為指標還是數組,都會按照上面所示的三個步驟進行操作,但是只有當P原來定義成指標時,上面這個方法才是正確的。考慮上面那個問題。當用mango【i】這種形式提取這個聲明的內容時,實際上得到是一個字元,按照上面的方法,編譯器卻把它當成一個指標,把ACSII字元當作成地址顯然是錯誤的。
數組和指標的區別:
NO |
指標 |
數組 |
1 |
儲存資料的地址 |
儲存資料 |
2 |
簡潔訪問資料。首先取得指標的內容,把它當成地址,然後從這個地址取提取資料。如果指標有一個下標【i】,那麼就把指標的內用加上i的地址,從中提取資料 |
直接存取資料。A【i】只是簡單地以a+i為地址取得數組。 |
3 |
通常用於動態記憶體分配 |
通常用於固定數目其資料類型相同的元素 |
4 |
相關函數malloc()和free() |
隱藏分配和刪除 |
5 |
通常指匿名資料 |
自身即為資料名 |
再議數組:
1、
什麼時候數組和指標相同
在實際的運用過程中,數組和指標互換的情景比兩者不可互換的情形更為常見,讓我們分別來考慮“聲明”和“使用”兩種情況。
聲明本來還可以分為3中情況:
Ø 外部數組的聲明;
Ø 數組的定義(記住,定義是聲明的一種特殊情況,它分配記憶體空間,並提供一個初始值);
Ø 函數參數的聲明。
所有作為函數參數的數組名總是可以通過編譯器轉換為指標。在其他情況下,數組的聲明就是數組,指標的聲明就是指標,兩者不能混淆。但是在使用數組時,數組總是可以寫成指標的形式,兩者可以互換。下面是總結:
為什麼會發生混淆
當人們學習編程時,一開始總是把所有的代碼都放在一個函數裡。隨著水平的進步,他們把代碼分別放在幾個函數中,在水平繼續提高後,他們最終學會了如何用幾個檔案來構造個程式。在這個過程中,他們可以看到大量的作為函數參數的數組和指標,在這種情況下,他們是完全可以互換的,如下所示:
char my_array[10];char *my_ptr;...i = strlen(my_array);j = strlen(my_ptr);printf("%s,%s",my_array,my_ptr);
上面清晰地展示了數組和指標的課互換性,人們很容易忽視這隻是放生在一種特定的上下文環境中,也就是說作為一個函數調用的參數使用。更糟的是它可以如下編寫:
printf("array at location %x holds string %s",a,a);
在同一個語句中,即把數組名作為一個地址(指標),有把他作為一個字元數組。
在C標準中對“什麼時候數組和指標時相同的”作了如下的聲明:規則1:運算式中的數組名(與聲明不同)被編譯器當做一個指向該數組第一個元素的指標。規則2:下標總是與指標的位移量相同。規則3:在函數參數聲明中,數組名悲編譯器當做指向該數組第一個元素的指標 下面詳細介紹這3個規則的含義:規則1:“運算式數組名”就是指標 上面的規則1,2一起理解,就是對數組下標的引用總是可以寫成“一個指向數組的起始地址的指標加上位移量”。例如,假如我們聲明:
int a[10], *p, i = 2;/*我們可以通過下面集中方法訪問a[i]*/p = a;p[i];/*****************/p = a;*(p + i);/******************/p = a + i;*p;
應該還有很多方法。對數組的引用如a[i]在編譯時間總是被編譯器改寫成*(a+i)的形式,C語言要求編譯器必須具備這個概念性的行為。取指標或者數組名+方括弧【】中的下標 就是訪問元素的地址,再取其值。所以,你也記住,在運算式中,指標和數組時可以互換的,因為他們在編譯器的最終都是以指標的形式。因此我們也可以理解下面的取值的表示方法為什麼相同了並都是正確的。
a[6] = ......;6[a] = .....;
規則2:C語言把數組下標作為指標的位移量 我們通常認為:在編寫數組演算法時,使用指標比數組 更效率 。這種說法在通常情況下是錯誤的。在一維數組和指標引用所產生的代碼並不具有顯著的差別。這裡可以參考前面的內容,自己在研究下。規則3:作為函數參數的數組名等同於指標
| 術語 |
定義 |
例子 |
| 形參(parameter) |
他是一個變數,在函數定義或聲明的原型 中定義。又稱形式參數 |
int power(int base;int n); base,n都是形參 |
| 實參(argument) |
在函數實際調用過程中傳遞給函數的值。 |
I = power(10,2); 10和2是實參, |
在標準中規定,類型的數組 的形參的聲明應該調整為 類型的指標。下面三種寫法都是一樣的
my_function (int *a);my_function (int a[]);my_function (int a[200]);
數組形參是如何被引用的? 展示了對一個下標形式的數組形參進行訪問所需要的幾個步驟:
注意:有一種操作只能在指標裡進行而無法在數組中進行,那就是修改它的值。數組名是不可修改的左值,它的值是不能改變的。