1. 程式人生 > 其它 >IDA PRO:慶祝成立創新 30 週年

IDA PRO:慶祝成立創新 30 週年

指標

什麼是指標

初學者可以簡單理解為指標就是變數的地址

怎麼獲取變數的地址呢?只要在變數的前面加上&,就表示變數的地址

#include <stdio.h>
int main(){
    int a = 1;
    printf("%d, %d\n", &a, a);
    return 0;
}

輸出結果:

2686748, 1

前面那一個數字是指變數a儲存的地址,後面指的是變數a的值

指標實際上是一個整數。

指標就是一個unsigned型別的整數。

指標變數

  • 指標變數用來存放指標

  • 指標變數的定義:通過在某種資料型別後面加星號*來表示這是一個指標變數,如下

int* p;
double* p;
char* p;

注意:星號“*”的位置在資料型別之後或者是變數名之前都是可以的,編譯器不會對此進行區分

如果一次有好幾個同種型別的指標變數要同時定義,星號只會結合於第一個變數名,例如下面的定義中,只有p1是int*型別的,而p2是int型的

int* p1, p2;

如果要讓後面的變數也是指標變數,需要在後面的每個變數名之前都加上星號;

int* p1, *p2, *p3;
  • 給指標賦值的方法一般是把變數的地址取出來,讓後賦給對應型別的指標變數:
int a;
int* p = &a;
//也可以寫成
int a;
int* p;
 p = &a;

注意:地址&a是賦值給p而不是*p的。(星號是型別的一部分,不屬於變數)

  • 如何得到指標變數存放的地址所指的元素呢?還是用星號*,示例如下

    #include<stdio.h>
    int main(){
    	int a;
    	int* p = &a;
    	a = 233;
    	printf("%d\n", *p);
    	return 0;
    }
    //輸出結果
    233
    

    在上面的程式碼執行過程中,定義了int型別的a後,並沒有對其進行初始化。a被賦值233後,指標變數的地址沒有變,但是指標變數所一定的元素已經改變了。

    *p儲存的是地址, *p是這個地址中存放的元素,那麼如果直接對 *p進行賦值,也是可以起到改變那個儲存的元素的功能的,如下面的例子

    #include <stdio.h>
    int main(){
    	int a;
    	int* p = &a;
    	*p = 233;
    	printf("%d, %d/n", *p ,a);
    	return 0;
    }
    //輸出結果
    233, 233
    
  • 指標變數也可以進行加減法,其中減法的結果就是兩個地址偏移的距離。

    對於一個int*型別的指標變數p來說,p+1就是指p所指的int型別變數的下一個int型別的變數地址。所以這個下一個是指跨越了一整個int型(即4Byte)。

    指標變數也支援自增的自減操作,所以可以用p++等同於p = p+1;

  • 對於指標變數來說,把其儲存的地址的型別稱為基變數。

    例如:int* p;其中的int就是它的基型別

指標與陣列

  • 在C語言中,陣列名稱也作為陣列的首地址使用,因此,有a == &a[0];
#include<stdio.h>
int main(){
	int a[10] = {1};
	int* p = a;
	printf("%d\n", *p);
	return 0;
}
//輸出結果
1

在上述程式碼中,a作為陣列a的首地址&a[0]而被賦值給指標變數p,因此輸出*p其實就是在輸出a[0]

所以,a+i == &a[i]; 注意:a+i其實只是一個地址,如果想要訪問其中的元素a[i],還需要加上星號,使其變成*(a+i)後才可以和a[i]等價

由此可以得到一種輸入陣列元素新穎的寫法;

scanf("%d", a+i);

原先在a+i的位置填寫的是&a[i],但是a+i和&a[i]等價,所以這樣寫是合適的

下面是讀取一整個陣列並輸出的例子:

#include<stdio.h>
int main(){
	int a[10];
	for(int i = 0; i < 10; i++){
		scanf("%d", a+i);
	}
	for(int i = 0; i < 10; i++){
		printf(“%d”, *(a + i));
	}
	return 0;
}
  • 另外,由於指標變數可以使用自增操作,因此可以這樣列舉陣列中的元素:
#include<stdio.h>
int main(){
	int a[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
	for(int* p = a; p < a + 10; p++){
		printf("%d ", *p);
	}
	return 0;
}
  • 最後是指標的減法,如下:
#include<stdio.h>
int main(){
	int a[10] = {1, 4, 9, 16, 25, 36, 49};
	int* p = a;
    int* q = &a[5];
    printf("q = %d\n", q);//2686708
    printf("p = %\nd", p);//2686688
    printf("q - p = %d\n", q - p);//5
    return 0;
}

上面的程式碼中,q和p中的具體數值和執行環境有關,因此可能會得到跟上面不同的結果,但是兩者之間一定是相差20的。但是p-q的輸出卻是5?因為q-p就是指兩個地址之間的距離。兩個int型的指標相減,等價於在求兩個指標之間差了幾個int

使用指標變數作為函式引數

#include<stdio.h>
 void change(int* p){
 	*p =233;
 }
 
 int main(){
	int a = 1;
	int* p = &a;
	change(p);
	printf("%d", a);
	return 0;
}
//輸出結果
233

在上面程式碼中,把int*型別的指標變數p賦值為a的地址,讓後通過change函式把指標變數p作為引數傳入。此時傳入的其實是a的地址。在change函式中,使用 *p 修改地址中存放的資料,也就是改變了a的本身。當最後輸出a時,就已經改變了的值。這種傳遞方式被稱為地址傳遞

看下面例子:使用指標作為引數,交換兩個數

首先回顧如何交換兩個數

#include<stdio.h>

int main(){
	int a = 1; b = 2;
	int temp = a;
	a = b;
	printf("a = %d, b = %d\n", a, b);
	return 0
}

如果想要把交換功能寫成函式,要怎麼做呢?首先需要指出,下面這種寫法是做不到的:

#include<stdio.h>

void swap(int a , int b){
	int temp = a;
	a = b;
	b = temp;
}

int main(){
	int a = 1, b = 2;
	swap(a,b);
	printf("a = %d, b = %d\n", a, b);
	return 0;
}//輸出結果
a = 1, b = 2

這是因為函式在接受引數的過程中是單向一次性的值傳遞,也就是說,在呼叫swap(a,b)時只是把a和b的值傳進去了,這樣相當於產生了一個副本,對這個副本的操作不會影響到main函式中a和b的值。接下來介紹使用指標的方法

指標存放的是地址,那麼使用指標變數作為引數時傳進來的也是地址。只有在獲取地址的情況下對元素進行操作,才能真正地修改變數

#include<stdio.h>

void swap(int* a, int* b){//把&a 和&b作為引數傳入,使得swap函式中的指標變數a存放&a,指標變數b存放&b。這時,在swap函式中的a和b都是地址,而*a和*b就是地址中存放的元素。
	int temp = *a;
	*a = *b;
	*b = temp;
}

int main(){
	int a = 1, b = 2;
	int* p1 = &a, *p2 = &b;
	swap(p1,p2);
	printf("a = %d, b = %d\n", *p1, *p2);
	return 0;
}
//輸出結果
 a = 1, b = 2;

錯誤寫法一:

void swap(int* a, int* b){
	int* temp;
	*temp = *a;
	*a = *b;
	*b = *temp;
}

問題在於定義指標變數temp時,temp沒有被初始化,指標變數temp中存放的地址是隨機的,如果該隨機地址指向的是系統工作區間,那麼就會出錯(而且這樣的概率特別大)

錯誤寫法二:

void swap(int* a, int* b){
	int* temp = a;
	a = b;
	b = temp;
}

這種寫法的思想在於直接把兩個地址交換,認為交換地址之後元素就交換了。其實這種想法產生於很大的誤區:swap函式裡交換完地址之後main函式裡的a與b的地址也被交換。因為函式引數傳送方式是單向一次性的,main函式傳給swap的“地址”其實是一個“無符號整型”的數,其本身也跟普通變數一樣只是“值傳遞”,swap函式對地址本身進行修改並不能對main函式裡的地址修改,能夠使main函式資料發生變化的只能是swap函式中對地址指向的資料進行的修改。

引用

  • 引用的含義:不使用指標打到修改傳入引數的目的,不產生副本,而是給原變數起了一個別名,且對引用變數的操作就是對原變數的操作。

  • 引用的方法:只需要在函式的引數型別後面加一個&就可以了(&加在int後面或者變數名前面都可以,一般寫在變數名前面)示例如下

    #include<stdio.h>
    void change(int&x){
    x = 1;
    }
    int main(){
    	int x = 10;
    	change(x);
    	printf("%d\n", x);
    	return 0;
    }
    //輸出結果
    1
    

    注意:要把引用的&和取地址運算子&區分開來,引用並不是取地址的意思。

  • 指標的引用

    #include<stdio.h>
    
    void swap(int* &p1,int* &p2){
    	int* temp = p1;
    	p1 = p2;
    	p2 = temp;
    }
    
    int main(){
    	int a = 1, b = 2;
    	int *p1 = &a, *p2 = &b;
    	swap(p1, p2);
    	printf("a = %d, b = %d\n", *p1, *p2);
    	return 0;
    }
    

    需要強調的是引用是產生變數的別名,因此常量不可以使用引用。於是上面的程式碼中不可以寫成swap(&a,&b),而必須用指標變數p1和p2存放&a、&b。讓後把指標變數作為引數傳入。