C語言筆記 — 指標(Pointers)

Sharon Peng
8 min readAug 9, 2019

--

指標!!是一個進入C語言的重要指標。也是比較難學習的部分,但若好好理解觀念,還是不會太難的。說來也慚愧,筆者我以前真的是每次遇到指標問題都還要在上網查一查呢~希望大家看過這篇就能搞懂指標的觀念。

本章重點:

  1. 介紹指標(Pointers)
  2. 如何宣告指標
  3. 指標陣列
  4. 函式與指標
  5. 函式與陣列的關係

1. 介紹指標

前情提要:

C語言當初是被設計為低階的程式語言(比較接近電腦的思考方式),也正因為如此,我們可以輕鬆地進入程式的記憶體位置並執行和記憶體有關的運算等等。

舉例來說,當我們在使用函式 scanf()時,我們要把某個值存到某個變數中時,前面會加”&” 。

int num;
scanf("%d", &num);
printf("%d", num);

&num就代表變數num的記憶體位置

當在存記憶體位置時,電腦都是用16進位(hexadecimal, hex)去存取。

int num;

printf("The address of num is %x.\n", &num);

%x 可以印出16進位。

%o 可以印出8進位。

會出現warning的原因是,%x的資料型態是unsigned int, 而&num的資料型態是int *,所以會有轉換的警告。

output:

The address of num is e2fceb78.

什麼是指標呢?

指標這個觀念是「陣列、字串、資料結構、演算法」的基礎,之後使用他們時,指標在裡面有很大的作用。

指標也算是一種變數,只是裡面存的不是一般的「數字」,而是記憶體位置。另一種說法是,指標會間接地「指向」指向變數的記憶體位置。

指標的宣告:

指標型態 * 變數名稱

舉例:

int num = 100;
int *ptr = NULL;
ptr = #
printf("The address of num is %x\n" &num);
printf("ptr contains address %x\n", ptr);
printf("The value of num is %d\n", num);
printf("ptr is pointing to the value %d", *ptr);

說明:

我們宣告變數 num = 100;

再宣告指標 int * ptr = NULL;(指標一開始先指向空的記憶體位址)

再將指標指向變數num的記憶體位址。

最後一行

printf("ptr is pointing to the value %d", *ptr);

我們現在知道 ptr 指向 num 的記憶體位址,再加上一個*代表我們要取出記憶體num的數值。

*上面程式碼有幾點要注意:

1. 指標的初始化NULL(代表空的意思),讓他一開始沒有預設的記憶體位址。

2. 給指標記憶體位址的方式,在變數前面加上”&”

3. 要讓指標取出裡面的內容,使用 「*」,*p,「*」也稱作間接運算子。

小總結:

& 記憶體位址 (取址)

* 取出內容 (取值)

2. 指標的小運用

指標也可以像一般變數一樣做運算

int num1 = 5;
int num2;
int *p = NULL;
p = &num1;
num2 = *num1 + 2; // num2 變成 (5+2) 7
num2 += *num1; // num2 變成 (7+5) 12
*p = num2; // num1 從5變成 12
(*p)++; // num1 變成 (12++) 13

說明:

宣告變數num1為5、宣告變數num2,宣告指標p指向空的記憶體。再把指標p指向num1的記憶體位址。

num2 = 取出num1內記憶體位址中的值 +2

num2 = 取出num1內記憶體位址中的值 + num2原本的值

取出p內記憶體位址中的值 等於目前num2的值

取出p內記憶體位址中的值++

3. 指標陣列

指標在陣列中也非常好使用,其實當我們再宣告陣列的時候,其實也再宣告指標。有了指標的輔助,我們可以把陣列中的元素用部分的數學運算子表示。

以下範例:

int arr[5] = {10, 20, 30, 40, 50};
int *ptr = NULL;
ptr = arr; //陣列也是一個指標,指向陣列中第一個數,ptr = &arr[0]
for(int i = 0; i < 5; ++i)
{
printf("%d", *(ptr + i));
}

output: 10 20 30 40 50

使用算術運算子,改變陣列的記憶體位址

int arr[5] = {10, 20, 30, 40, 50};
int *ptr = NULL;
ptr = arr; //陣列也是一個指標,指向陣列中第一個數,ptr = &arr[0]printf("%d %x\n", *ptr, ptr); // 10,
ptr++;
printf("%d %x\n", *ptr, ptr); // 20
ptr += 3;
printf("%d %x\n", *ptr, ptr); // 50
ptr--;
printf("%d %x\n", *ptr, ptr); // 40
ptr -= 2;
printf("%d %x\n", *ptr, ptr); // 20

不知道各位是否有注意到記憶體位址的變化,4個4個跳的,原因是int的佔4bytes記憶體位址。當變成double時,記憶體位址會8bytes的跳。

4. 函式與指標

當想要用指標呼叫函式的時候,該怎麽做?

「兩數交換」範例:

void swap(int *num1, int *num2); //先告訴電腦說我有一個函式int main()
{
int a = 10;
int b = 25;
printf("a is %d\n, b is %d", a, b);
swap(&a, &b); // 分別傳入記憶體位址
printf("a is %d\n, b is %d", a, b);
return 0;
}
void swap(int *num1, int *num2)
{
int temp;
temp = *num1;
*num1 = *num2;
*num2 = temp;
}

說明:

這個交換法,是直接將記憶體位址交換。設一個temp,先把num1的值存下來,因為會先把num1的直換掉(到時候會不見)。

5. 函式與陣列的關係

把陣列傳入函式的方法

範例1:

int sum(int *arr, int element) //先告訴電腦說我有一個函式int main()
{
int arr[5] = {10, 20, 30, 40, 50};
int total = 0;
total = sum(arr, 5); // 將回傳值傳到total中
printf("Total is %d", total);
return 0;
}
int sum(int *arr, int element) // *arr代表傳入陣列第一個數
{
for(int i = 0; i < element; ++i)
{
total += arr[i];
}
return (total);
}

範例2:

#include<stdio.h>int * even(); //先告訴電腦說我有一個函式int main()
{
int *arr; //用來存陣列

arr = even();
for(int i = 0; i < 5; ++i)
{
printf("%d\n", arr[i]);
}
return 0;
}
int * even() // 回傳值是一個指標
{
static int num[5];
int even = 0;
for(int i = 0; i < 5; ++i)
{
num[i] = even;
even += 2;
}
return (num); // 直接回傳陣列(指標)回去
}

*陣列arr[i]等同於*(arr+i)

Ex: arr[3] = *(arr+3) = 6

arr[1] = *(arr+1) = 2

終於把指標講完啦~~

如果還有什麼問題歡迎到下方留言,或者有什麼錯誤請務必告訴我。

那我們下次見

--

--