2022年2月2日 星期三

『C語言』指標教學 程式範例/完整說明

 了解指標前,需要先認識記憶體

什麼是記憶體?

從軟體的角度,記憶體可以想像成一塊連續的儲存空間,用來儲存資料最小的儲存單位是位元組,一個位元組 (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。

沒有留言:

張貼留言