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。讓後把指標變數作為引數傳入。