C語言筆記5(指針上)

一、指針的概念

1.1變量和地址

  • 變量:直觀來說,int a、char ch、float num這些都是聲明變量,而a、ch、num就是變量
  • 地址:在計算機中,內存被分爲一小塊一小塊的,而每一塊都有一個編號,叫做地址。
  • 一般變量都存儲在內存當中。而每塊內存都有一個獨一無二的地址,這個地址就是指針
  • 如果把內存比作一個賓館,在聲明一個變量時(int a),就相當於在賓館前臺辦了入住手續。前臺會給你一個門卡和門牌號,簡單理解門牌號就是地址。

二、變量的指針與指針變量

指針爲變量的地址,而專門用來存儲另一個變量的地址的變量就是指針變量。

2.1、指針變量的定義及使用

(1)、指針變量的定義

定義指針變量的符合爲*,如下定義了三個指針變量。它們的變量名爲pi、pj、pf,而不是*pi、*pj、*pf。*號在此只用來聲明。

//聲明瞭兩個整型變量和一個浮點型變量
int i, j;
float f;

//聲明三個指針變量
int *pi, *pj;
float *pf;

(2)、指針變量的使用

  • 取地址符&:單目運算符“&”的功能是取操作對象的地址。常量、表達式和寄存器變量不能取地址,因爲它們不是存放在內存某個存儲單元中,而是放在寄存器中,寄存器無地址。
  • 指針運算符(間接尋址運算符)*:單目運算符“*”的功能是按照操作對象的地址值,訪問對應存儲單元。與“&”互爲逆運算。
//聲明一個變量i,初始化值爲10
int i = 10;

//利用取地址符&獲取i的地址
printf("%d", &i);

//定義一個指針變量pi,指向i的地址
int *pi = &i;

//利用指針運算符*獲取pi指向的內存,即爲i的值
printf("%d", *pi);

注:在C語言中,所有變量的聲明都必須放在最前面,但是有些編譯器你沒放前面也可以通過,這裏注意一下

(3)、&和*運算符的結合方向

“&”和“*”兩個運算符優先級相同,但按從右至左方向結合。可理解爲從右開始運算

//聲明一個變量i
int i = 10;
//聲明一個指針變量pi,指向i
int *pi = &i;
//輸出i的地址
printf("%d", &*pi);

上面的代碼定義了一個指向i的指針變量pi,而輸出i的地址使用了“&*pi”。首先,pi是一個指針變量,pi的內容爲i的地址。因爲運算符是右結合,則先是運算*pi。即爲pi地址中的內容,就是10。然後再取地址,&*pi即爲i的地址。

2.2、指針變量的初始化

void main(){
    int a = 10;
    //利用取地址符&,獲取變量a的地址,給指針變量pa賦值
    int *pa = &a;
}

2.3、指針運算

(1)賦值運算

void main(){
    int *px, *py, *pz, x;
    //1、指向某個地址
    px = &x;
    
    //2、賦予空指針
    py = NULL;
    
    //3、賦予指定地址
    pz = 4000;

}

(2)指針與整數的加減運算

  • 指針變量自增或自減,即指針向前或者向後移動一個存儲單元
  • 指針比那裏加上一個整型數,即指針向前或者向後移動指定的存儲單元

(3)關係運算

  • px < py,判斷px指向的地址是否小於py指向的地址
  • px == py,判斷px和py是否指向同一個地址
  • px == 0和px != 0表示px是否爲空指針

接下來來一個小練習:

//聲明函數
void invert(int *a, int start, int end);

/**
*    採用遞歸法對a數組的元素進行逆序
*/
void main(){
    
}

/**
*    實現函數
*    a爲數組首地址
*    i位起始逆序元素
*    j爲逆序結尾元素
*/
void invert(int *a, int start, int end){
    //臨時變量,用於交換
    int temp;        

    //當起始逆序元素小於逆序結尾元素時,說明還沒有逆序到中間元素    
    if(start < end){
        //將起始元素和結尾元素交換
        temp = a[start];
        a[start] = a[end];
        a[end] = temp;

        //交換後再次調用invert,將其餘元素逆序,此時start和end要同時向中間移動
        invert(a, start + 1, end - 1);
    }
}

三、指針與數組

3.1、指向數組的指針

  • 數組名即爲該數組的首地址,a爲一個數組,a = &a[0]。
  • 可以通過指針對數組元素進行訪問,*a = a[0]、*(a + 1) = a[1]。
  • 數組名不能進行指針的操作,像指針p++是合法的,但是數組a++是非法的。

3.2、字符指針和字符數組

在C語言中,系統本身沒有提供字符串數據類型,但可以使用兩種方式存儲一個字符串:字符數組方式和字符指針方式。

(1)字符數組方式

也就是我們比較常用的方式

void main(){
    //定義一個字符數組
    char sentence[] = "Do not go gengle into that good night!";
    printf("%s", sentence);
}

其中sentence就是字符數組的首地址。

(2)字符指針方式實現字符串

void main(){
    char *sentence = "Do not go gentle into that good night!";
    printf("%s", sentence);
}

來個小練習:

/**
*    用數組將字符串sentence複製到字符串copy
*/
void mian(){
    char *sentence = "Do not go gentle into that good night!", copy[50];
    int i;
    //當沒有遇到結束符時,一直循環
    for(i = 0; sentence[i] != '\0'; i++){
        //將數據複製到copy中
        copy[i] = sentence[i];
    }
    printf("複製後的copy是:%s", copy);
}

3.3、多級指針及指針數組

(1)多級指針

簡單來說就是指針的指針,指針變量作爲一個變量,也有自己的存儲空間。而這個存儲空間也有一個地址:

void main(){
    //定義一個普通變量
    int a = 10;

    //定義一個指針變量,指向a
    int *p = &a;
    
    //定義另一個指針變量,指向指針變量p,此時pp就是二級指針
    int **pp = &p;

    //輸出兩個指針
    printf("一級指針pa爲:%d\n", p);
    printf("二級指針ppa爲:%d", pp);

    //指針的指針和普通指針操作一樣,可以用*pp獲取pp指向地址中的內容,即p存儲的內容
    printf("p存儲的內容爲:%d", *pp);
}

注:因爲一級指針和二級指針性質不一樣,所以一級指針和二級指針之間不能賦值,如p = pp在編譯時會報錯(這是書中寫的,但是在我實際測試當中,可以賦值,可能是編譯器的問題)。

(2)指針數組

即一個元素爲指針的數組,定義如下:

int *a[10];

用一個練習熟悉指針數組,解釋全在註釋當中:

void main(){
    //定義並初始化一個int數組
    int a[5] = {1, 3, 5, 6, 8}, i;

    //定義一個指針數組,與a數組中元素對應
    int *p[5];
    for(i = 0; i < 5; i++){
        p[i] = &a[i];
    }

    //定義一個二級指針,存放指針數組的首地址。指針數組和普通數組一樣,數組名爲數組首地址
    int **pp = p;

    //利用指針數組首地址輸出數據
    for(i = 0; i < 5; i++){
            
        //數組a中第零個元素地址爲p,而p的的地址爲pp,所以**pp = a[0]
        //數組a中第一個元素地址爲p + 1,而p + 1的地址爲pp + 1,所有**(pp + 1) = a[1]
        //以此類推,**(pp + n) = a[n]
        printf("%d\t", **(pp + i));
    }
}

3.4、指針與多維數組

(1)多維數組的地址

假設有個二維數組a[4][2],那麼可以分兩個維度來理解這個數組。

  • 先去掉[2],只看“a[4]”一個維度。此時a只是個普通的一維數組,而後面的[2]也只是決定了數組a元素的性質
  • 在數組a中,有四個元素,我們取一個來分析第二個維度。其第一個元素爲a[0],我們將a[0]看做一個整體,不作爲數組元素,只作爲一個名稱X。那麼第二個維度就可以看做X[2],即一個有兩個元素的數組。
  • 由上面可知,X數組的首地址爲數組名,即X。X實際上是a[0],類推的話X1、X2等就是a[1]、a[2]。可以間接理解爲數組的第一個維度裝的全是地址,每個元素X的地址。

(2)多維數組的指針

舉個例子方便理解

void mian(){
    //創建一個普通二維數組
	int num[5][5] = {
		{1, 3, 4, 5, 6},
		{4, 5, 7, 8, 8},
		{6, 8, 9, 0, 1},
		{3, 4, 2, 1, 2},
		{4, 5, 6, 3, 2}
	};

    //聲明一個指針數組
	int *p_num[5];
	int i, j;

    //初始化指針數組,每個元素分別指向num[0][0]、num[1][0]、、、
	for(i = 0; i < 5; i++){
		p_num[i] = &num[i][0];
	}
		
    //利用指針數組p_num輸出num數組中的元素
	for(i = 0; i < 5; i++){

		printf("這是第%d輪數組\n", i+1); 
		for(j = 0; j < 5; j++){
            
            //將p_num[i]作爲一個數組首地址,數組存儲的內容爲*(p_num+j)
			printf("%d\t", *(p_num[i] + j));
		} 
		printf("\n");
	}


}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章