C語言中文網 目錄
首頁 > 編程筆記 > C語言筆記 > 指針 閱讀:6,829

指針變量的初始化,C語言指針變量初始化詳解

本節來解決如何給一個指針變量初始化。即怎樣使一個指針變量指向另一個變量。

前面章節中的某些程序實際上已經使用了,即可以用賦值語句使一個指針變量得到另一個變量的地址,從而使它指向該變量。比如:
int i, *j;
j = &i;
這樣就將變量 i 的地址放到了指針變量 j 中,通過 i 的地址,j 就能找到 i 中的數據,所以 j 就“指向”了變量 i。其中 & 是“取地址運算符”,與 scanf 中的 & 是一樣的概念;* 為“指針運算符”,功能是取其內部所存變量地址所指向變量中的內容。因為 j 是定義成指針型變量,所以 j 中只能存放變量的地址,所以變量i前一定要加 &。需要注意的是,指針變量中只能存放地址,不要將一個整數或任何其他非地址類型的數據賦給一個指針變量。

此外,還有兩點需要注意:
  1. j 不是 i,i 也不是 j。修改j的值不會影響i的值,修改 i 的值也不會影響 j 的值。j 是變量 i 的地址,而 i 是變量 i 里面的數據。一個是“內存單元的地址”,另一個是“內存單元的內容”。
  2. 定義指針變量時的“*j”和程序中用到的“*j”含義不同。定義指針變量時的“*j”只是一個聲明,此時的“*”僅表示該變量是一個指針變量,并沒有其他含義。而且此時該指針變量并未指向任何一個變量,至于具體指向哪個變量要在程序中指定,即給指針變量初始化。而當指定 j 指向變量 i 之后,*j 就完全等同于 i 了,可以相互替換。

下面給大家寫一個程序:
# include <stdio.h>
int main(void)
{
    int i = 3, *j;  //*j表示定義了一個指針變量j
    j = &i;
    printf("*j = %d\n", *j);  //此時*j完全等同于i
    printf("j = %d\n", j);    //j里面存儲的是變量i的地址
    return 0;
}
輸出結果是:
*j = 3
j = 1245052

下面再將上面這個程序修改一下:
# include <stdio.h>
int main(void)
{
    int i = 3;
    int *j = &i;  //*j表示定義了一個指針變量j, 并將變量i的地址賦給它
    printf("*j = %d\n", *j);  //此時*j完全等同于i
    printf("j = %d\n", j);    //j里面存儲的是變量i的地址
    return 0;
}
輸出結果是:
*j = 3
j = 1245052

這個程序與第一個程序有什么不同?同樣是將變量 i 的地址賦給指針變量 j,第一個程序是“j=&i;”,而第二個程序是“*j=&i;”。原因是,前者是定義指針變量后對它初始化,即先定義后初始化;而后者是定義指針變量時對它進行初始化,即定義時初始化。通過這個對比我們可以更鮮明地看出定義指針變量時的“*j”和程序中用到的“*j”含義的不同。

那么指針變量和指針變量之間可不可以相互賦值呢?我們看看下面這個程序:
# include <stdio.h>
int main(void)
{
    int *i, *j;
    int k = 3;
    i = &k;
    j = i;  //直接指針變量名之間進行賦值
    printf("*j = %d\n", *j);  //此時*j完全等同于k
    printf("j = %d\n", j);    // j里面存儲的是變量k的地址
    return 0;
}
輸出結果是:
*j = 3
j = 1245044

可見,可以直接將一個指針變量賦給另一個指針變量,只要將指針變量名賦給另一個指針變量名即可。但是需要注意的是:
  1. 這兩個指針變量的基類型一定要相同。
  2. 在賦值之前,賦值運算符“=”右邊的指針變量必須是已經初始化過的。也就是說,切忌將一個沒有初始化的指針變量賦給另一個指針變量。這是非常嚴重的語法錯誤。

同樣,也可以在定義指針變量時就給它賦初值:
# include <stdio.h>
int main(void)
{
    int k = 3;
    int *i = &k;
    int *j = i;
    printf("*j = %d\n", *j);  //此時*j完全等同于k
    printf("j = %d\n", j);    //j里面存儲的是變量k的地址
    return 0;
}
輸出結果是:
*j = 3
j = 1245048

注意,“int*j=i;”千萬不要寫成“int*j=*i;”。因為此時 *i 不是定義指針變量 i,而是完全等同于變量 k。所以 int 型變量不能賦給 int* 型的變量。

指針常見錯誤

1) 引用未初始化的指針變量

試圖引用未初始化的指針變量是初學者最容易犯的錯誤。未初始化的指針變量就是“野”指針,它指向的是無效的地址。

有些書上說:“如果指針變量不初始化,那么它可能指向內存中的任何一個存儲單元,這樣就會很危險。如果正好指向存儲著重要數據的內存單元,而且又不小心向這個內存單元中寫入了數據,把原來的重要數據給覆蓋了,這樣就會導致系統崩潰。”這種說法是不正確的!如果真是這樣的話就是編譯器的一個嚴重的 BUG!

編譯器的設計人員是不會允許這么大的 BUG 存在的。那么如果指針變量未初始化,編譯器的設計人員是如何處理這個問題的呢?肯定不可能讓它亂指。以VC++6.0這個編譯器為例,如果指針變量未初始化,那么編譯器會讓它指向一個固定的、不用的地址。下面來寫一個程序:
# include <stdio.h>
int main(void)
{
    int *p, *q;
    printf("p = %#X\n", p);
    printf("q = %#X\n", q);
    return 0;
}
輸出結果是:
p = 0XCCCCCCCC
q = 0XCCCCCCCC

可見,在 VC++6.0 中只要指針變量未初始化,那么編譯器就讓它指向 0XCCCCCCCC 這個內存單元。而且這個內存單元是程序所不能訪問的,訪問就會觸發異常,所以也不怕往里面寫東西。

而如果在 VS 2008 這個編譯器中,程序雖然能編譯通過,但是在運行的時候直接出錯,它并不會像 VC++6.0 那樣還能輸出所指向的內存單元的地址。

下面來看一個程序:
# include <stdio.h>
int main(void)
{
    int i = 3, *j;
    *j = i;
    return 0;
}
程序中,j 是 int* 型的指針變量。j 中存放的應該是內存空間的地址,然后“變量 i 賦給 *j”表示將變量i中的值放到該地址所指向的內存空間中。但是現在 j 中并沒有存放一個地址,程序中并沒有給它初始化,那么它指向的就是 0XCCCCCCCC 這個內存單元。這個內存單元是不允許訪問的,即不允許往里面寫數據。而把 i 賦給 *j 就是試圖往這個內存空間中寫數據,程序執行時就會出錯。但這種錯誤在編譯的時候并不會報錯,只有在執行的時候才會出錯,即傳說中的“段錯誤”。所以,一定要確保指針變量在引用之前已經被初始化為指向有效的地址。

在實際編程中,這種錯誤常見的另一個地方是用 scanf 給指針變量所指向的內存單元賦值。我們看看下面這個程序:
# include <stdio.h>
int main(void)
{
    int *i;
    scanf("%d", i);
    return 0;
}
該程序試圖給指針變量 i 所指向的內存單元賦值。但現在指針變量 i 并沒有初始化,所以程序執行時出錯。所以同樣,在使用 scanf 時必須要先給指針變量 i 初始化。比如像下面這樣寫:
# include <stdio.h>
int main(void)
{
    int *i, j;
    i = &j;  //先給指針變量i初始化
    scanf("%d", i);  //i本身就是地址, 所以不用加&
    printf("%d\n", *i);
    return 0;
}
輸出結果是:
10
10

能不能使用 scanf 給指針變量初始化?指針變量里面存放的是地址,而內存中有數不清的單元,每個單元都有一個地址,你知道每個單元的地址嗎?你知道哪些地址是空閑可用的,而哪些地址正存儲著重要數據不能用嗎?不知道的話怎么用scanf給它初始化呢?萬一隨便寫一個地址正好是存儲著非常重要的數據的內存單元地址,那系統就真的崩潰了!

隨著大家編程能力的不斷提高,慢慢地就會發現,其實編程最重要、最核心的就是如何處理內存的問題,如何與內存打交道。

2) 往一個存放NULL地址的指針變量里面寫入數據

這也是編程中最容易犯的錯誤,不僅是初學編程的,即使是有一些經驗的程序員也會不小心犯這個錯誤。我們把前面的程序改一下:
# include <stdio.h>
int main(void)
{
    int i = 3;
    int *j = NULL;
    *j = i;
    return 0;
}
之前是沒有給指針變量j初始化,現在初始化了,但是將它初始化為指向 NULL。NULL 也是一個指針變量。NULL 指向的是內存中地址為 0 的內存空間。以 32 位操作系統為例,內存單元地址的范圍為 0x00000000~0xffff ffff。其中 0x00000000 就是 NULL 所指向的內存單元的地址。但是在操作系統中,該內存單元是不可用的。凡是試圖往該內存單元中寫入數據的操作都會被視為非法操作,從而導致程序錯誤。同樣,這種錯誤在編譯的時候也不會報錯,只有在執行的時候才會出錯。這種錯誤也屬于“段錯誤”。

然而雖然這么寫是錯誤的,但是將一個指針變量初始化為指向 NULL,這在實際編程中是經常使用的。就跟前面講普通變量在定義時給它初始化為 0 一樣,指針變量如果在定義時不知道指向哪里就將其初始化為指向 NULL。只是此時要注意的是,在該指針變量指向有效地址之前不要往該地址中寫入數據。也就是說,該指針變量還要二次賦值。

既然不能往里面寫數據,而且還容易犯錯,為什么還要這樣給它初始化呢?直接同前面定義普通變量時一樣,在定義時也不初始化,等到后面知道該給它賦什么值時再給它賦值不行嗎?可以!但還是建議大家將它初始化為 NULL,就同前面將普通變量在定義時初始化為 0 一樣。這是很好的一種編程習慣。

最后關于 NULL 再補充一點,NULL 是定義在 stdio.h 頭文件中的符號常量,它表示的值是 0。

精美而實用的網站,提供C語言C++STLLinuxShellJavaGo語言等教程,以及socketGCCviSwing設計模式JSP等專題。

Copyright ?2011-2018 biancheng.net, 陜ICP備15000209號

底部Logo