Skip to content

[C 語言] 指標(pointer)

Last Updated on 2021-04-29 by Clay

指標pointer)是一種指向變數記憶體位址的變數。一般而言,當我們今天跟系統宣告了一個變數並賦值時,程式會向記憶體申請空間來儲存:

  • 變數位址(起始位址)
  • 變數值
  • 變數名稱

而使用指標,我們便能宣告一個變數指向某個特定變數的記憶體位址(搭配 & 符號取得地址)。

這樣做有什麼好處呢?指標其實是存在許多優點的,比方說:

  • 替函式提供傳入參數的方法(比方說陣列(Array))
  • 減少複製大塊記憶體,減少資源浪費
  • 支援某些資料結構(鏈結串列(Linked-List))

當然指標也不是沒有缺點的,有時錯誤的使用方法甚至會引起系統崩潰。


如何使用指標

要宣告指標,需要在資料型態後加上 * 符號。

data-type *p;

而怎麼進行指標變數的操作呢?我們來看段簡短的範例程式碼:

#include <stdio.h>


// Main
int main() {
    int x=1, y=2, z[10];
    int *p;              // p is a pointer to int

    p = &x;              // p now points to x
    y = *p;              // y is now 1
    *p = 0;              // x is now 0
    p = &z[0];           // p is now points to z[0]

    return 0;
}



指標與函式參數

剛剛在闡述指標優點時有提到,指標替函式提供了傳入參數的方法。這是什麼意思呢?

我們來設計一個函式 swap(x, y) ,目的是將 xy 兩個變數的值做交換。

錯誤示範

#include <stdio.h>


void swap(int x, int y) {
    int temp = x;
    x = y;
    y = temp;
}


// Main
int main() {
    int x=1, y=2;
    swap(x, y);

    printf("x: %d\ny: %d\n", x, y);

    return 0;
}


Output:

x: 1
y: 2

如你所見,xy 的值根本沒有交換。這是因為在 xy 的值傳入 swap(x, y) 時另外複製了一份,而在 swap(x, y) 中交換的正是複製的變數,而無法真正影響外部傳入的變數。(當然,你可以回傳交換後的變數值,然後再次宣告 x y


正確方法

#include <stdio.h>


void swap(int *px, int *py) {
    int temp = *px;
    *px = *py;
    *py = temp;
}


// Main
int main() {
    int x=1, y=2;
    swap(&x, &y);

    printf("x: %d\ny: %d\n", x, y);

    return 0;
}


Output:

x: 2
y: 1

可以看到,使用指標操作的話,就像是操作原本的變數交換值一樣。


指標與陣列

在宣告陣列(Array)之後,我們變數記憶體位址即是首項元素的記憶體位址。來看段範例程式碼(%p 為印出記憶體位址)。

#include <stdio.h>


// Main
int main() {
    int a[3] = {1, 2, 3};

    printf("a:     %p\n", a);
    printf("&a[0]: %p\n", &a[0]);

    return 0;
}


Ouptut:

a:     0x7ffee0c7c47c
&a[0]: 0x7ffee0c7c47c

記憶體位址是不是一模一樣呢?

除此之外,陣列的索引index)則是首位元素記憶體位址的『位移量』。只要取得首項元素的記憶體位址賦值給指標變數,每次增加一個單位逐項增加,便會與陣列不同元素的記憶體位置相同。

#include <stdio.h>


// Main
int main() {
    int a[3] = {1, 2, 3};
    int *p = a;

    for (int i=0; i<sizeof(a)/sizeof(a[0]); ++i) {
        printf("p + %d: %p\n", i, p+i);
        printf("&a[%d]: %p\n", i, &a[i]);
        printf("===================================\n");
    }
    
    return 0;
}


Output:

p + 0: 0x7ffee102747c
&a[0]: 0x7ffee102747c
===================================
p + 1: 0x7ffee1027480
&a[1]: 0x7ffee1027480
===================================
p + 2: 0x7ffee1027484
&a[2]: 0x7ffee1027484
===================================

而想要使用指標來取陣列中的值也很容易:

#include <stdio.h>


// Main
int main() {
    int a[3] = {1, 2, 3};
    int *p = a;

    for (int i=0; i<sizeof(a)/sizeof(a[0]); ++i) {
        printf("*(p + %d): %d\n", i, *(p+i));
    }
    
    return 0;
}


Output:

*(p + 0): 1
*(p + 1): 2
*(p + 2): 3

字元指標(Character Pointer)

在 C 語言中,字串其實就是字元以陣列的方法儲存。而在上一小節我已經紀錄了如何透過指標操作陣列。

而使用指標變數儲存字串,其字串內容是不可更改的(使用字元陣列儲存倒是可以)。故在使用指標變數儲存字串時建議加上 const

#include <stdio.h>


// Main
int main() {
    const char *pText = "Hello";
    char text[] = "world";

    text[0] = 'W';
    printf("%s %s\n", pText, text);

    return 0;
}


Output:

Hello World

References


Read More

Tags:

Leave a Reply