了解指標前,需要先認識記憶體
什麼是記憶體?
從軟體的角度,記憶體可以想像成一塊連續的儲存空間,用來儲存資料,最小的儲存單位是位元組,一個位元組 (Byte) 是八個位元 (Bit)。雖然可以操縱位元,但還是以位元組為最小單位做儲存。
舉例:
用雞蛋盒對照記憶體,雞蛋盒就是儲存空間,雞蛋就是資料,每個擺放雞蛋的空間就是最小的儲存單位。
圖片來源:維基百科
如何指定存取哪些記憶體位元組?
記憶體可以儲存相當多的位元組 (Byte),為了指定要存取哪一個,每一個位元組都有一個號碼做代表,就是記憶體位址 (Memory Address),因為是連續的儲存空間,所以記憶體位址也是連續的。
舉例:
以 4GB 記憶體來說,可以儲存 2 的 32 次方個位元組 = 4,294,967,296 個位元組,因記憶體位址從 0 開始,所以範圍就是從 0 到 4,294,967,295 (0x0000 0000 - 0xFFFF FFFF)
變數 (Variables) 是什麼?
變數其實就是記憶體中的一塊空間,宣告變數時會指定變數名稱與資料型態 (Data Type),不同的資料型態佔用不同大小的儲存單位。
關於變數你還需要知道的是
1. 變數名稱
2. 變數的記憶體位址 (在變數名稱前加上 & 可取得變數的記憶體位址)
3. 變數佔用的儲存單位大小 (使用 sizeof() 可以取得佔用的單位數)
4. 變數內容值
範例
#include
int main(int argc, char *argv[])
{
char var1 = 0x10;
short var2 = 0x20;
int var3 = 0x30;
long var4 = 0x40;
int var5 = 0x50;
printf("Name Address Size Value\n");
printf("var1(char) %p %d-bytes %#x\n", &var1, sizeof(var1), var1);
printf("var2(short) %p %d-bytes %#x\n", &var2, sizeof(var2), var2);
printf("var3(int) %p %d-bytes %#x\n", &var3, sizeof(var3), var3);
printf("var4(long) %p %d-bytes %#lx\n", &var4, sizeof(var4), var4);
printf("var5(int) %p %d-bytes %#x\n", &var5, sizeof(var5), var5);
return 0;
}
結果
Name Address Size Value
var1(char) 0xbfd63131 1-bytes 0x10
var2(short) 0xbfd63132 2-bytes 0x20
var3(int) 0xbfd63134 4-bytes 0x30
var4(long) 0xbfd63138 4-bytes 0x40
var5(int) 0xbfd6313c 4-bytes 0x50
注意:變數佔用的儲存單位,在不同的平台可能不一樣。
指標 (Pointer) 是什麼?
指標其實也是變數,但儲存的內容值是記憶體位址 (Memory Address),與其他變數的目的不同。
其他變數儲存的內容值可能用做運算或是計數等用途,而指標是為了操作所儲存記憶體位址的記憶體內容值。
通常說指標指向某變數,意思是此指標變數的內容值是某變數的記憶體位址。
宣告指標與取出指標指向之記憶體位址的內容值皆是使用 * 符號,使用方式參考如下範例。
範例
#include
int main(int argc, char *argv[])
{
char var1 = 0x10;
short var2 = 0x20;
int var3 = 0x30;
long var4 = 0x40;
// Declare pointer
char *ptr1 = &var1;
short *ptr2 = &var2;
int *ptr3 = &var3;
long *ptr4 = &var4;
printf("Name Address Size Value Value pointed to\n");
printf("var1(char) %p %d-bytes %#x\n", &var1, sizeof(var1), var1);
printf("var2(short) %p %d-bytes %#x\n", &var2, sizeof(var2), var2);
printf("var3(int) %p %d-bytes %#x\n", &var3, sizeof(var3), var3);
printf("var4(long) %p %d-bytes %#lx\n", &var4, sizeof(var4), var4);
printf("ptr1(char *) %p %d-bytes %p %#x\n", &ptr1, sizeof(ptr1), ptr1, *ptr1);
printf("ptr2(short *) %p %d-bytes %p %#x\n", &ptr2, sizeof(ptr2), ptr2, *ptr2);
printf("ptr3(int *) %p %d-bytes %p %#x\n", &ptr3, sizeof(ptr3), ptr3, *ptr3);
printf("ptr4(long *) %p %d-bytes %p %#lx\n", &ptr4, sizeof(ptr4), ptr4, *ptr4);
return 0;
}
結果
Name Address Size Value Value pointed to
var1(char) 0xbfbd3895 1-bytes 0x10
var2(short) 0xbfbd3896 2-bytes 0x20
var3(int) 0xbfbd3898 4-bytes 0x30
var4(long) 0xbfbd389c 4-bytes 0x40
ptr1(char *) 0xbfbd38a0 4-bytes 0xbfbd3895 0x10
ptr2(short *) 0xbfbd38a4 4-bytes 0xbfbd3896 0x20
ptr3(int *) 0xbfbd38a8 4-bytes 0xbfbd3898 0x30
ptr4(long *) 0xbfbd38ac 4-bytes 0xbfbd389c 0x40
注意:因為指標儲存的是記憶體位址,所以無論是什麼型態的指標,其所佔用的儲存單位都是固定的。
指標 (Pointer) 可以指向什麼,存取的方式?
基本上只要佔用記憶體,有記憶體位址的都可以指向,因此在使用上要十分注意。
舉例:
1. 基本型態變數 (Basic Type Variable)
2. 陣列 (Array)
3. 結構 (struct)
4. 指標 (Pointer)
底下範例展示,如何操作指標來修改指向的變數(記憶體位址)之內容值。
範例
#include
struct obj
{
char member1;
short member2;
int member3;
long member4;
};
int main(int argc, char *argv[])
{
/* 1. Variable (basic types) */
char data1 = 1;
short data2 = 2;
int data3 = 3;
long data4 = 4;
char *ptr1 = &data1;
short *ptr2 = &data2;
int *ptr3 = &data3;
long *ptr4 = &data4;
printf("1. Variable (basic types)\n\n");
printf(" &data1 = %p\n", &data1);
printf(" ptr1 = %p\n", ptr1);
printf(" data1 = %d\n", data1);
printf(" *ptr1 = %d\n\n", *ptr1);
printf(" &data2 = %p\n", &data2);
printf(" ptr2 = %p\n", ptr2);
printf(" data2 = %d\n", data2);
printf(" *ptr2 = %d\n\n", *ptr2);
printf(" &data3 = %p\n", &data3);
printf(" ptr3 = %p\n", ptr3);
printf(" data3 = %d\n", data3);
printf(" *ptr3 = %d\n\n", *ptr3);
printf(" &data4 = %p\n", &data4);
printf(" ptr4 = %p\n", ptr4);
printf(" data4 = %ld\n", data4);
printf(" *ptr4 = %ld\n\n", *ptr4);
*ptr1 = 10;
*ptr2 = 20;
*ptr3 = 30;
*ptr4 = 40;
printf(" After assigning values to pointer\n");
printf(" data1 = %d\n", data1);
printf(" data2 = %d\n", data2);
printf(" data3 = %d\n", data3);
printf(" data4 = %ld\n\n", data4);
/* 2. Array */
int array1[5] = {0, 1, 2, 3, 4};
int *ptr_a1 = array1;
printf("2. Array\n\n");
printf(" array1 = %p\n", array1);
printf(" ptr_a1 = %p\n", ptr_a1);
printf(" array1 = {%d %d %d %d %d}\n", array1[0], array1[1], array1[2], array1[3], array1[4]);
printf(" ptr_a1[2] = %d\n\n", ptr_a1[2]);
ptr_a1[2] = 20;
printf(" After assigning values to pointer\n");
printf(" array1 = {%d %d %d %d %d}\n\n", array1[0], array1[1], array1[2], array1[3], array1[4]);
/* 3. Struct */
printf("3. Struct\n\n");
struct obj object;
object.member1 = 11;
object.member2 = 12;
object.member3 = 13;
object.member4 = 14;
struct obj *ptr_s = &object;
printf(" &object = %p\n", &object);
printf(" ptr_s = %p\n", ptr_s);
printf(" object.member1 = %d\n", object.member1);
printf(" object.member2 = %d\n", object.member2);
printf(" object.member3 = %d\n", object.member3);
printf(" object.member4 = %ld\n", object.member4);
printf(" ptr_s->member2 = %d\n\n", ptr_s->member2);
ptr_s->member2 = 120;
printf(" After assigning values to pointer\n");
printf(" object.member1 = %d\n", object.member1);
printf(" object.member2 = %d\n", object.member2);
printf(" object.member3 = %d\n", object.member3);
printf(" object.member4 = %ld\n\n", object.member4);
/* 4. Pointer */
int data5 = 50;
int *ptr5 = &data5;
//two-dimensional pointer
int **pptr = &ptr5;
printf("4. Pointer\n\n");
printf(" &data5 = %p\n", &data5);
printf(" ptr5 = %p\n", ptr5);
printf(" &ptr5 = %p\n", &ptr5);
printf(" pptr = %p\n", pptr);
printf(" *pptr = %p\n", *pptr);
printf(" data5 = %d\n", data5);
printf(" *ptr5 = %d\n", *ptr5);
printf(" **pptr = %d\n", **pptr);
return 0;
}
結果
1. Variable (basic types)
&data1 = 0xbfd8ec81
ptr1 = 0xbfd8ec81
data1 = 1
*ptr1 = 1
&data2 = 0xbfd8ec82
ptr2 = 0xbfd8ec82
data2 = 2
*ptr2 = 2
&data3 = 0xbfd8ec84
ptr3 = 0xbfd8ec84
data3 = 3
*ptr3 = 3
&data4 = 0xbfd8ec88
ptr4 = 0xbfd8ec88
data4 = 4
*ptr4 = 4
After assigning values to pointer
data1 = 10
data2 = 20
data3 = 30
data4 = 40
2. Array
array1 = 0xbfd8ecbc
ptr_a1 = 0xbfd8ecbc
array1 = {0 1 2 3 4}
ptr_a1[2] = 2
After assigning values to pointer
array1 = {0 1 20 3 4}
3. Struct
&object = 0xbfd8ecb0
ptr_s = 0xbfd8ecb0
object.member1 = 11
object.member2 = 12
object.member3 = 13
object.member4 = 14
ptr_s->member2 = 12
After assigning values to pointer
object.member1 = 11
object.member2 = 120
object.member3 = 13
object.member4 = 14
4. Pointer
&data5 = 0xbfd8ec8c
ptr5 = 0xbfd8ec8c
&ptr5 = 0xbfd8ec90
pptr = 0xbfd8ec90
*pptr = 0xbfd8ec8c
data5 = 50
*ptr5 = 50
**pptr = 50
注意:作業系統 (Operating System) 通常有記憶體管理 (Memory Management Unit) ,所以一般的應用程式都是存取到虛擬記憶體位址 (Virtual Memory Address),就算操作到不應該使用的記憶體位址,也不至於讓作業系統 crash。