OC學習3——C語言特性之指標
1、指標是C語言中的一個非常重要的概念,實際上,OC系統類的變數、自定義類的變數等都是指標。定義指標變數的語法格式如下,其中*代表一個指標變數,整個語法代表定義一個指向特定型別的變數的指標變數。注意:指標變數不能儲存普通的數值,它只能儲存指標(也就是變數或物件的地址)。函式的形參傳遞方式有值傳遞和地址傳遞兩種,其中地址傳遞就是傳遞的指標。
型別* 變數名 ;
float* ft ;
int* a ;
2、關於指標,還有兩個最基本的運算子,取地址運算子(&)和取變數運算子(*)。
&:取地址運算子,單目運算子,後面通常緊跟一個變數,該運算子用於讀取該變數所在的記憶體地址。
*:取變數運算子,單目運算子,後面通常緊跟一個指標變數,該運算子用於讀取該指標變數所指向的記憶體中的變數。
3、陣列變數的本質就說指標常量,該指標常量指向第一個陣列元素。下面兩種賦值方式的本質是一樣的,而且大部分時候都會採用第二種方式來獲取陣列的首地址。所以,將陣列變數作為引數傳遞到函式中實際上是一種地址傳遞。
int arr[5] = {1, 2, 3, 4, 5} ;
int* p1 = &arr[0] ; //將第一個陣列元素的地址賦值給指標變數p1
int* p2 = arr ; //將陣列變數儲存的地址賦值給指標變數p2
需要指出的是,雖然陣列變數儲存的是陣列第一個元素的地址,但是陣列中儲存的地址是不能改變的,因此,陣列變數應稱為指標常量。所以,執行arr++,arr += 2這種語句都是試圖對arr陣列變數進行重新賦值,這都是錯誤的
4、指向多維陣列的指標變數,例如int arr[3][4] ;實際上是相當於定義瞭如下的三個陣列變數:
arr[0]:該陣列中再次包含了arr[0][0]、arr[0][1]、arr[0][2]、arr[0][3]四個元素,其中arr[0]表示指向arr[0][0]元素的地址的指標變數。
arr[1]:該陣列中再次包含了arr[1][0]、arr[1][1]、arr[1][2]、arr[1][3]四個元素,其中arr[1]表示指向arr[1][0]元素的地址的指標變數。
arr[2]:該陣列中再次包含了arr[2][0]、arr[2][1]、arr[2][2]、arr[2][3]四個元素,其中arr[2]表示指向arr[2][0]元素的地址的指標變數。
注意,arr[1][2]與 *(arr[1]+2) 、*(*(arr+1)+2)表示的意義是一樣的,都是表示取arr[1][2]的值
5、指標的運算除了取地址、取變數和賦值之外,還有一些其他的運算需要注意,具體介紹如下:
指標變數加(減)一個整數:當指標變數加或減n時,代表將該指標的地址加或減n*變數大小個位元組。舉例來說,對於int* p;型別的變數,假如當前p變數中儲存的地址為0x00010004,p+2則代表的地址是0x0001000C,因為一個int型別的資料佔據4個四節,所以p+2實際上是往後移兩個int型別的資料,相當於移8個位元組。而對於char* p1;型別,若p1儲存的地址是0x00020003,則p1+4則表示的地址是0x00020007。
當兩個指標變數指向同一個陣列的元素時,兩個指標變數可以相減:兩個指標變數相減,返回兩個指標所指陣列之間元素的個數。如果兩個指標不指向同一個陣列的元素,那麼這兩個指標變數相減沒有任何意義。
當兩個指標變數指向同一個陣列的元素時,兩個指標變數可以比較大小:指向前面的陣列元素的指標小於指向後面的陣列元素的指標。需要指出的是,如果兩個指標不指向同一個陣列的元素,那麼這兩個指標變數比較大小沒有任何意義。
6、C語言的底層沒有對字串進行定義,一般都是通過字元陣列進行儲存字串。此外,還可以通過字元指標來表示字串,即定義一個字元指標變數,然後將C格式的字串賦給該指標變數。
char* str = "I love IOS" ;
C語言的自字串在底層依然是才用字元陣列進行儲存的,而str則是一個char*型的指標變數,它指向該字元陣列的第一個元素,也就是指向該字元陣列的首地址。
7、指標變數除了可以指向普通的int變數、float變數和陣列之外,還可以指向函式的入口。當定義函式之後,C語言允許定義一個指標變數來指向該函式,然後就可以通過該指標變數來呼叫函數了,使用函式指標變數的語法格式步驟如下:
- 定義函式指標變數:函式返回值型別 (*指標變數名)();
- 將任何已有的函式賦值給函式指標變數:指標變數名 = 函式名 ;
- 使用函式指標變數來呼叫函式:(*函式指標變數名)(引數);
1 #import <Foundation/Foundation.h>
2
3 int max(int * data, int len)
4 {
5 int max = *data ;
6 //採用指標遍歷data陣列的元素
7 for(int *p = data; p < data+len; p++)
8 {
9 //保證max始終儲存較大的值
10 if(*p > max)
11 {
12 max = *p ;
13 }
14 }
15 return max ;
16 }
17
18 int main(int argc, char* argv[])
19 {
20 int data[] = {1,2,3,4};
21 //定義函式指標變數fnPt,並將max函式賦值給fnPt
22 int (*fnPt) () = max ;
23 //通過函式指標變數呼叫函式
24 NSLog(@"最大值 max = %d", (*fnPt) (data , 5)) ;
25 }
函式指標的主要作用就是(1)把指標函式當作形參傳遞給某些具有一定通用功能的模組。並封裝成介面來提高程式碼的靈活性和後期維護的便捷性;(2)有些地方必須使用函式函式指標才能完成給定的任務,如linux系統中的非同步訊號中斷處理,當發生某一觸發訊號時,需要呼叫相應的處理函式,此時需要使用函式指標來實現。
void (*signal(int signum,void(* handler)(int)))(int);
引數一為訊號條件,第二個引數為一個函式指標,它所指向的函式需要一個整型引數,無返回值。該函式的返回值也是一個函式指標,返回的指標所指向的函式有一個整型引數(一般不用)
8、函式既可以返回普通的int、float等型別,也可以返回一個指標。但是當函式返回一個指標的時候需要注意,由於函式返回的指標只儲存了一個地址值,如果該指標指向的是被呼叫函式中的區域性變數,這就非常危險了,因為函式中的區域性變數在函式呼叫結束之後會被自動釋放,這樣會導致該記憶體中所儲存的資料是不確定的。所以,為哦了保證函式返回的指標是有效的,有三種方式:
- 如果函式返回的指標是指向函式中的區域性變數,該區域性變數應該使用static修飾。
- 讓函式返回的指標指向暫時不會被釋放的資料,如指向main()函式中的變數。
- 讓函式返回的指標指向全域性變數。
9、指標陣列是值陣列中的每個元素都是一個指標變數,常見的main()函式的形參第二個引數定義是char* argv[]就是一個指標陣列。詳情可以參見陣列指標和指標陣列的區別
1 //指標陣列的定義語法
2 型別* 變數名[長度];
3
4 char* arr[3] ;
5 arr[0] = "hello world!";
6 arr[1] = "I love IOS";
7 arr[2] = "how are you ?";
8
9 //注意區分上面的指標陣列與下面的區別
10 型別 (*變數名)[長度];
11
12 //第二種寫法中(*變數名)先形成一個整體,代表一個指標變數,該指標指向一位陣列,因此表示定義一個指向一位陣列的指標變數
13 int a[3][4];
14 int (*p)[4]; //該語句是定義一個數組指標,指向含4個元素的一維陣列。
15 p=a; //將該二維陣列的首地址賦給p,也就是a[0]或&a[0][0]
16 p++; //該語句執行過後,也就是p=p+1;p跨過行a[0][]指向了行a[1][]
10、指向指標變數的指標:也稱指標的指標。指標變數也是變數,也需要儲存在記憶體中,因此指標變數也有自己的儲存地址,如果再次定一個一個指標變數來儲存這個地址,則這個指標變數就說指向指標變數的指標。
型別** 變數名;