chapter 6 指針


大學C語言程序設計

chapter 6 指針

1. 何謂指針(Pointer)

編譯系統根據程序中定義的變量類型,分配一定長度的空間,也就是我們所說的內存。
而內存是具有唯一地址的,CPU會通過內存尋址方式對內存中的某個指定數據的地址進行定位,從而找到這個數據。
指針就是用來保存這個地址的變量,可以通過訪問指針(也就是訪問這個地址),從而達到訪問這個數據的目的。

【百科】
在計算機科學中,指針(Pointer)是編程語言中的一個對象,利用地址,它的值直接指向(points to)存在電腦存儲器中另一個地方的值。
由於通過地址能找到所需的變量單元,可以說,地址指向該變量單元。因此,將地址形象化的稱為"指針"。意思是通過它能找到以它為地址的內存單元。
在高級語言中,指針有效地取代了在低級語言,如匯編語言與機器碼,直接使用通用暫存器的地方,但它可能只適用於合法地址之中。
指針參考了存儲器中某個地址,通過被稱為反參考指針的動作,可以取出在那個地址中存儲的值。
作個比喻,假設將電腦存儲器當成一本書,一張內容記錄了某個頁碼加上行號的便利貼,可以被當成是一個指向特定頁面的指針;
根據便利粘貼面的頁碼與行號,翻到那個頁面,把那個頁面的那一行文字讀出來,就相當於是對這個指針進行反參考的動作。

在信息工程中指針是一個用來指示一個內存地址的計算機語言的變量或中央處理器(CPU)中寄存器(Register)【用來指向該內存地址所對應的變量或數組】。
指針一般出現在比較接近機器語言的語言,如匯編語言或C語言。面向對象的語言如Java一般避免用指針。
指針一般指向一個函數或一個變量。
在使用一個指針時,一個程序既可以直接使用這個指針所儲存的內存地址,又可以使用這個地址里儲存的函數的值。

另外,指針也指鍾表中用來指示對應時間的部件。

當然,我們這里的指針是指程序語言中的指針變量,是一個指向某一個地址的變量。
平常我們可以通過變量名去訪問某個元素,那么這樣的訪問方式是:直接訪問
而我們用一個指針變量來存放變量名的地址,通過訪問指針變量的方式,找到原變量的地址,從而訪問該變量對應的元素的訪問方式是:間接訪問
指針指向的是地址,那么我們可以說變量 a的指針是 2000,而不能說 變量 a的指針變量是 2000,指針是一個地址,指針變量是一個存放地址的變量。

2. 指針變量的定義

定義格式:類型名 * 指針變量名;

int a=1;    float b=3.14;    double c=3.1415926;    char d='A';
int * pointer1;    //pointer1 是指向 int 類型的指針變量,簡稱 int 指針
float * pointer2;  //pointer2 是指向 float 類型的指針變量,簡稱 float 指針
double * pointer3; //pointer3 是指向 double 類型的指針變量,簡稱 double 指針
char * pointer4;   //pointer4 是指向 char 類型的指針變量,簡稱 char 指針

//上述左邊 int/float/double/char 是 pointer1~4 的基類型,聲明時就必須指定

pointer1 = &a;     //將變量 a的地址賦給 pointer1,且包含着 a 是 int 類型的信息
pointer2 = &b;     //將變量 b的地址賦給 pointer2,且包含着 b 是 float 類型的信息。
pointer3 = &c;     //將變量 c的地址賦給 pointer3,且包含着 c 是 double 類型的信息。
pointer4 = &d;     //將變量 d的地址賦給 pointer4,且包含着 d 是 char 類型的信息。

也可以使用如下方式:
int * pointer1 = &a;
float * pointer2 = &b;
double * pointer3 = &c;
char * pointer4 = &d;

int *arr[10];      //聲明一個指針數組,該數組有10個元素,其中每個元素都是一個指向 int 類型的指針
int (*arr)[10];    //聲明一個數組指針,該指針指向一個 int 類型的一維數組
int **pointer;     //聲明一個指針 pointer ,該指針指向一個 int 類型的指針
#include<stdio.h>

void swap(int *pointer1, int *pointer2){
    int temp = *pointer1;
    *pointer1 = *pointer2;
    *pointer2 = temp;
}

int main(){
    int a=1,b=2;
    int*pointer1=&a, *pointer2=&b;
    printf("a=%d\t b=%d\n", a, b);
    printf("pointer1=%d\t pointer2=%d\n", pointer1, pointer2);
    swap(pointer1, pointer2);
    printf("a=%d\t b=%d\n", a, b);
    printf("pointer1=%d\t pointer2=%d\n", pointer1, pointer2);
    printf("*pointer1=%d\t *pointer2=%d\n", *pointer1,*pointer2);
}

輸出結果:
a=1      b=2
pointer1=6618636         pointer2=6618632
a=2      b=1
pointer1=6618636         pointer2=6618632
*pointer1=2      *pointer2=1

3. 通過指針引用數組

int a[10]={0,1,2,3,4,5,6,7,8,9};
int *pointer1 = &a[0]; //將數組a的首元素地址賦指針變量 pointer1
int *pointer2 = a;     //將數組a的首元素地址賦指針變量 pointer2

在指針 p 指向一個數組元素時,可以對指針進行以下運算:
p+1;    //指向同一數組中的下一個元素
p++;    //指向同一數組中的下一個元素
++p;    //指向同一數組中的下一個元素
p-1;    //指向同一數組中的上一個元素
p--;    //指向同一數組中的上一個元素
--p;    //指向同一數組中的上一個元素
p1-p2;  //只有p1,p2都指向同一數組中的元素時才有意義,否則無實際意義


如:int a[10]={0,1,2,3,4,5,6,7,8,9};
int *p = a;
*(p+i)  或 *(a+i) 都等同於 a[i]

[]實際上是變址運算符,即 a[i] 按 a+i 計算地址,然后找出此地址單元中的值。

#include<stdio.h>
int main(){
    int a[10]={1,2,3,4,5,6,7,8,9,0};
    for(int *p=a; p<(a+10); p++){
        printf("%d ", *p); //1 2 3 4 5 6 7 8 9 0
    }
    return 0;
}

數組名用作函數參數

void print(int arr[], int l, int r);
void print(int arr[], int l, int r){
    for(int i=l; i<=r; i++){
        printf("%d ",arr[i]);
    }printf("\n");
}

實參數組名代表一個固定的地址,或者說指針常量,但形參數組名並不是一個固定地址,而是按照指針變量處理。
所以形參可以用數組名,也可以使用指針變量名

void print(int *arr, int l, int r);
void print(int *arr, int l, int r){
    for(int i=l; i<=r; i++){
        printf("%d ",arr[i]);
    }printf("\n");
}

4. 通過指針引用多維數組

#include<stdio.h>
#include<stdlib.h>
int a[10][10];
int main(){
    int n=10;
    for(int i=0; i<n; i++){
        for(int j=0; j<=n; j++){
            a[i][j]=i*j+1;
        }
    }
    printf("地址:%d %d %d\n", a[0], *a, *(a+0));
    printf("地址:%d %d %d\n", a, a+1, a+2);
    printf("地址:%d %d %d\n", a[1], a[1]+1, a[1]+2);

    printf("元素:%d %d %d\n", *a[0], **a, **(a+0));
    printf("元素:%d %d %d\n", **a, **(a+1), **(a+2));
    printf("元素:%d %d %d\n", *a[1], *(a[1]+1), *(a[1]+2));
    printf("元素:%d %d %d\n", *a[1], *(*(a+1)+1), *(*(a+1)+2));
    return 0;
}

輸出結果:
地址:4223040 4223040 4223040
地址:4223040 4223080 4223120
地址:4223080 4223084 4223088
元素:1 1 1
元素:1 1 1
元素:1 2 3
元素:1 2 3

5. 通過指針引用字符串

#include<stdio.h>
int main(){
    char str1[]="Hello C1";
    char *str2 ="Hello C2";

    printf("str1 = %s\n", str1);// Hello C1
    printf("str2 = %s\n", str2);// Hello C2

    printf("str1 = ");
    for(char *p=str1; *p!='\0'; p++){
        printf("%c", *p);
    }printf("\n");              // Hello C1

    printf("str2 = ");
    for(char *p=str2; *p!='\0'; ){
        printf("%c", *p++);
    } printf("\n");            // Hello C2

    char *pre=str1;
    printf("*pre   = %c\n", *pre);   // H
    printf("*pre++ = %c\n", *pre++); // H
    printf("*pre   = %c\n", *pre);   // e
    printf("*++pre = %c\n", *++pre); // l
    printf("*pre   = %c\n", *pre);   // l
    printf("*++pre = %c\n", ++*pre); // m
    printf("*pre   = %c\n", *pre);   // m

    // *pre++ 由於 * 和 ++ 同優先級,結合方向為 自右而左,因此等價與 *(pre++)
    // 其執行是:先引用 pre 的值,實現 *pre 的運算,再使 pre++
    // *(++pre) 是先對 pre++,再引用 pre 的值。
    return 0;
}

6. 動態內存分配與它的指針變量

全局變量是分配在內存中的靜態存儲區的,非靜態的局部變量(包括形參)是分配在內存中的動態存儲區的,這個存儲區是一個稱為棧(stack)的區域。C語言允許建立動態存儲分配區域,存放臨時用的數據。這些臨時數據存放在一個特別的存儲區,稱為堆(heap)區。由於未在聲明的部分定義他們為變量或數組,因此不能通過變量名或數組名去引用這些數據,只能通過指針來引用。

對內存的動態分配時通過系統提供的庫函數來實現的,主要用:
malloc,calloc,realloc,free這4個函數。

1.用malloc函數開辟動態存儲區,其函數原型為:

void *malloc(unsigned int size);

malloc(100);   //  開辟100字節的臨時分配域,函數值為其第一個字節的地址

在內存的動態存儲區分配一個長度為size的連續空間,形參size的類型為無符號整形。

2.用calloc函數開辟動態存儲區,其函數原型為:

void *calloc(unsigned n, unsigned int size);

p=calloc(50,4);  // 開辟50x4個字節的臨時分配域,把起始地址賦給指針變量p

在內存中分配n個長度為size的連續空間。

3.用realloc函數重新分配動態存儲區,其函數原型為:

void *realloc(void *p, unsigned int size);

recalloc(p, 50);  // 將p所指向的已分配的動態空間改為50字節

如果已經通過 malloc 或 calloc 函數獲得了動態空間,想改變大小,可以用 recalloc 函數重新分配。

4.用free函數釋放動態存儲區,其函數原型為:

void free(void *p);

free(p);  // 釋放指針變量 p 所指向的已分配的動態空間

其作用是釋放指針變量 p 所指向的動態空間,使這部分空間能重新被其他變量使用。
p 應是最后一次調用 calloc或 malloc 函數時得到的函數返回值。如:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM