1. 程式人生 > >用交換函式來理解指標

用交換函式來理解指標

寫一個函式,交換a和b的值 #include<stdio.h>
void swap1(int a,int b)
{
 int tmp=a;
 a=b;
 b=tmp;
} int main() {
  int a=10;   int b=20;   printf("%d,%d\n",a,b);    Swap1(a,b);   printf("%d,%d\n",a,b);   return 0; } 如果呼叫這個函式,會交換值嗎?

值並沒有改變,為什麼呢? 因為我們只是在交換函式內部改變了a和b的值,一旦這個函式呼叫結束,函式內部的變數也就銷燬了,並不會傳到主函式中,這就是我們經常說的值傳遞。相當於只是拷貝了一個副本放在交換函式中,但是這個副本的改變不會影響主函式中的值。主函式和交換函式中的a,b並不是相同的變數。所以交換函式a和b的變化並不會影響到主函式中的a和b。
如果想看是不是相同的變數,只需要看他們的地址是不是相同的,地址相同才是相同的變數。 void swap2(int *a,int *b)
{
 int *tmp;
 tmp=a;
 a=b;
 b=tmp;
} 既然值傳遞不能進行交換,那我們傳遞地址呢? 呼叫swap2函式來試一下

呼叫了swap2函式,還是沒有改變它們的值,又是為什麼呢? 因為當我們把a,b的地址傳進去的時候,指標變數*a,*b 就儲存了主函式a,b的地址,但是我們賦值的時候,實質上我們只是把地址互相交換了,只是改變了原來指標的指向,並不會改變a,b裡面存放的值。想要改變裡面的值,要對指標變數解引用,才能修改指標變數所指的地址裡面的值。
void swap3(int *a,int *b)
{
 int *tmp;
 *tmp=*a;
 *a=*b;
 *b=*tmp;
} 既然沒有解引用,發生了錯誤,那我們就解引用,看看是不是正確的 呼叫swap3函式,看一下是不是正確的 當我們呼叫這個函式的時候,不僅沒有出現交換,程式還直接崩潰了,這是為什麼呢? 這裡要引入一個新的概念:野指標。野指標又稱懸掛指標,是沒有指向有效地址的指標,或者說沒有訪問許可權的地址。 當我們定義tmp的時候,並沒有初始化,所以它是一個野指標,可能隨機指向了某一個地址,當我們將a解引用賦值給tmp解引用的時候,是導致系統崩潰的原因。a解引用以後是20,但是tmp由於沒有初始化,它指向的地址空間並不允許它訪問或者修改裡面存放的值。因為沒有訪問許可權,所以系統直接崩潰了。 但是野指標編譯器不能預防,因為可能那個地址就是實際存在的。例如: int *tmp=(int *)10000;  *tmp=100; 這裡編譯不會有錯,因為這個地址就是實際存在的,但是執行的時候會崩潰,因為這個地址10000你是沒有訪問許可權的。所以野指標防不勝防,我們使用的時候要避免使用野指標,可以加一個斷言,防止野指標。 void swap(int *a,int *b)
{
 int tmp;
 tmp=*a;
 *a=*b;
 *b=tmp;
} 去掉野指標以後能不能交換值呢?呼叫swap函式試一試

終於將值交換了,這個程式為什麼成功了呢? 首先定義兩個指標變數是沒有錯誤的,我們想要通過指標變數去改變值,必須要解引用,當我們把a解引用時,其實就是將a指向的地址的值取出來,賦給了變數tmp;然後*a=*b 這一句就是將b指向的記憶體單元的值拿出來,放到a指向的記憶體單元。*b=tmp就是將tmp儲存的變數拿出來放到b所指向的記憶體單元中,即a記憶體單元的值儲存到了b記憶體單元,通過這個過程就可以將a,b的值進行交換了。 int main()
{
 int a=10;
 int b=20;
 printf("%d,%d\n",a,b);
 //swap1(a,b);
 //swap2(a,b);
 //swap3(&a,&b);
 swap(&a,&b);
 printf("%d,%d\n",a,b);
 return 0;
}
通過以上的測試,可以看出: 父函式呼叫子函式,子函式能修改父函式中的內容,或者子函式的改變能夠影響父函式,需要兩個條件: 1.傳指標    2.解引用 但是有這兩個條件也不一定改變值,可能會有野指標出現,要預防野指標。必要非充分條件