2017年3月15日 星期三

指標(pointer)

為何要使用指標

使用指標的好處是:我們不需要先跟電腦要記憶體空間,可以事後要用到時,再指定一個記憶體空間位置給它使用。

與之相對的陣列,在宣告時,需要事先告訴電腦我需要多少的空間,如此一來,空間大小就固定住了,不如指標可以隨時改變其空間的大小。

指標觀念

宣告指標的格式如下:
資料型態 *自訂變數名稱; 

其中*的功能為讀取指標變數所指向的記憶體空間位置所儲存之數值,稱為取值運算子
==================================================================
<範例1>
#include <stdio.h>

int main( ) 
{
    int *a;
    int b = 5;
    a = &b;
    printf("a = %d\n", a); //表示a所儲存的記憶體空間位置,以%d輸出代表將其記憶體位址編號以十進位表示
    printf("a = %p\n", a); //表示a所儲存的記憶體空間位置,以%p代表輸出記憶體位址編號(以十六進位表示)
    printf("*a = %d\n", *a); //表示*a所存儲的記憶體空間位置所儲存之數值
    printf("b = %d\n", b);
    return 0;
}
<說明>
其中的宣告方式:
    int *a;
    a = &b;
可以直接合併成:
    int *a = &b;
將上述的內容直接寫入範例1,可得如下:
#include <stdio.h>

int main( ) 
{
    int b = 5;
    int *a = &b;
    printf("a = %d\n", a); //表示a所儲存的記憶體空間位置,以%d輸出代表將其記憶體位址編號以十進位表示
    printf("a = %p\n", a); //表示a所儲存的記憶體空間位置,以%p代表輸出記憶體位址編號(以十六進位表示)
    printf("*a = %d\n", *a); //表示*a所存儲的記憶體空間位置所儲存之數值
    printf("b = %d\n", b);
    return 0;
}

==================================================================
<範例2>
#include <stdio.h>
int main( )
{
    char b = 'A';
    char *a = &b;
    printf("a = %d\n", a); //表示a所儲存的記憶體空間位置,以%d輸出代表將其記憶體位址編號以十進位表示
    printf("a = %p\n", a); //表示a所儲存的記憶體空間位置,以%p代表輸出記憶體位址編號(以十六進位表示)
    printf("*a = %c\n", *a); //表示*a所存儲的記憶體空間位置所儲存之字元
    printf("b = %c\n", b);
    return 0;
}
==================================================================
<範例3>*重要觀念:特別注意第8行程式碼*
#include <stdio.h>
int main( )
{
    char b[20] = "Test for a string";
    char *a = &b;
    printf("a = %d\n", a); //表示a所儲存的記憶體空間位置,以%d輸出代表將其記憶體位址編號以十進位表示
    printf("a = %p\n", a); //表示a所儲存的記憶體空間位置,以%p代表輸出記憶體位址編號(以十六進位表示)
    printf("a = %s\n", a); //特別注意:如果是宣告一個字串(陣列)時,要輸出該指標變數,使用%s,且不需要在a前方加上*
    printf("b = %s\n", b);
    return 0;
}
==================================================================
<範例4>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void a(char *name, int score)   //"設計函數"和"指標變數"的技巧
{
    printf("Name:%s / score:%d\n", name, score);
}
void F1()   //"設計函數"和"指標變數"的技巧
{
    char *name [2];
    int score[2];
    int i;

    name [0] = (char *) calloc(10, sizeof(char));  //問系統一個char的size為何,並跟系統要10個char的size之記憶體空間,最後再告訴系統以字元的方式來存取資料 (note:從右邊開始解釋程式碼)
    strcpy (name[0], "Jason");
    score[0] = 90;

    name [1] = (char *) calloc(10, sizeof(char));
    strcpy (name[1], "Jimmy");
    score[1] = 95;

    for (i=0; i< 2; i++)
    {
        a(name[i], score[i]);
    }
}
int main()
{
    F1();
    return 0;
}


<說明>
本例子示範的是指標陣列的用法
char *name [2];

若以圖形化表示,大該如下:
然而,指標陣列並沒有一個實際的記憶體空間可以給我們的資料來存放,所以這個時候我們可以使用calloc來跟電腦要記憶體空間給指標變數使用,程式碼如下:
name [0] = (char *) calloc(10, sizeof(char));
name [1] = (char *) calloc(10, sizeof(char));

該函式calloc會跟電腦要10個char的記憶體空間,並回傳其記憶體空間位置的地址給指標變數,所以使用指標來儲存該記憶體空間的地址。
若以圖形化表示,大該如下:
使用calloc所配置的記憶體空間,在使用後,如果不需要了,可以使用free( )來歸還電腦記憶體空間,這就是所謂的指標動態記憶體空間配置的觀念,這樣做的好處是可以減少不必要的記憶體空間浪費、節省記憶體資源。
修改後的code:<note>第27行程式碼
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void a(char *name, int score)   //"設計函數"和"指標變數"的技巧
{
    printf("Name:%s / score:%d\n", name, score);
}
void F1()   //"設計函數"和"指標變數"的技巧
{
    char *name [2];
    int score[2];
    int i;

    name [0] = (char *) calloc(10, sizeof(char));  //問系統一個char的size為何,並跟系統要10個char的size之記憶體空間,最後再告訴系統以字元的方式來存取資料 (note:從右邊開始解釋程式碼)
    strcpy (name[0], "Jason");
    score[0] = 90;

    name [1] = (char *) calloc(10, sizeof(char));
    strcpy (name[1], "Jimmy");
    score[1] = 95;

    for (i=0; i< 2; i++)
    {
        a(name[i], score[i]);
    }
    free(name);
}
int main()
{
    F1();
    return 0;
}

==================================================================
<範例5>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct record { //"自訂結構資料型態"和"自訂資料型態的別名"的技巧
    char name[10];
    int score;
} R;

void a(char *name, int score)   //"設計函數"的技巧
{
    printf("Name:%s / score:%d\n", name, score);
}

void F2(){ //"設計函數"和"指標變數"的技巧
R data[2]; //類似我們宣告字元、整數、浮點數依樣,需要先告訴電腦我們的資料型態是char、int或float,只是在這邊我們使用的是剛剛自訂的資料型態R
int i;
    strcpy (data[0].name, "Jason");
    data[0].score = 90;

    strcpy (data[1].name, "Jimmy");
    data[1].score = 95;
    for (i=0; i<2; i++){
        a(data[i].name, data[i].score);
    }
}

int main()
{
    F2();
    return 0;
}
    

<說明>
本例子示範的是自訂結構資料型態的變數宣告,程式碼如下:
typedef struct record { //"自訂結構資料型態"和"自訂資料型態的別名"的技巧
    char name[10];
    int score;
} R;

void F2(){
    R data[2];
}

目的是要與指標陣列在圖形化表示上做個概念性的比較,自訂結構資料型態的變數宣告的圖形化表示如下:

==================================================================
<範例6>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct record { //"自訂結構資料型態"和"自訂資料型態的別名"的技巧
    char name[10];
    int score;
} R;

void a(char *name, int score)   //"設計函數"的技巧
{
    printf("Name:%s / score:%d\n", name, score);
}

void F3(){ //"設計函數"和"指標變數"的技巧
    R *data[2];
    int i;
    data[0] = (R *) malloc (sizeof(R));
    strcpy (data[0]->name, "Jason");
    data[0]->score = 90;

    data[1] = (R *) malloc (sizeof(R));
    strcpy (data[1]->name, "Jimmy");
    data[1]->score = 95;

    for (i=0; i<2; i++){
        a(data[i]->name, data[i]->score);
    }
}

int main()
{
    F3();
    return 0;
}

<說明>
本範例是示範以R為資料型態宣告指標陣列的方式,程式碼如下:

typedef struct record { //先定義清楚所自訂的資料型態內包含些什麼內容(例如:本程式自定的資料型態叫作struct record,別名是R,內容包含一個大小為10的name陣列與一個整數變數score
    char name[10];
    int score;
} R;

    R *data[2];  //以R為資料型態(就像之前我們所使用的int、float、double、char等),宣告指標陣列

2 則留言: