1. 程式人生 > 其它 >OC學習3——C語言特性之指標

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. 定義函式指標變數:函式返回值型別 (*指標變數名)();
  2. 將任何已有的函式賦值給函式指標變數:指標變數名 = 函式名 ; 
  3. 使用函式指標變數來呼叫函式:(*函式指標變數名)(引數);
 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等型別,也可以返回一個指標。但是當函式返回一個指標的時候需要注意,由於函式返回的指標只儲存了一個地址值,如果該指標指向的是被呼叫函式中的區域性變數,這就非常危險了,因為函式中的區域性變數在函式呼叫結束之後會被自動釋放,這樣會導致該記憶體中所儲存的資料是不確定的。所以,為哦了保證函式返回的指標是有效的,有三種方式:

  1. 如果函式返回的指標是指向函式中的區域性變數,該區域性變數應該使用static修飾。
  2. 讓函式返回的指標指向暫時不會被釋放的資料,如指向main()函式中的變數。
  3. 讓函式返回的指標指向全域性變數。

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、指向指標變數的指標:也稱指標的指標。指標變數也是變數,也需要儲存在記憶體中,因此指標變數也有自己的儲存地址,如果再次定一個一個指標變數來儲存這個地址,則這個指標變數就說指向指標變數的指標。

型別** 變數名;