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。

『Linux』如何用 od 來做 hex dump (How to use od to dump a file in hexadecimal)

  

沒有 hexdump 可用時,使用 od + less 也可以達到效果,指令如下

When hexdump command is not available, you can use od plus less to dump a file in hexadecimal.

 

od -A x -t x1z [FILE] | less

 

底下範例是用 od 來 dump od 本身

The following example uses od to dump itself.





『Linux』十分鐘開始使用 vim

使用 VIM 時會用到指令,其實熟練少數常用指令足以應付大部分的使用情形。底下將以幾個範例來說明。

範例一 (安裝vim、建檔、編輯、存檔、關閉檔案)

1. 安裝 vim

sudo apt-get install vim

 

2. 使用 vim 建立並開啟一個 src.c 的新檔案(指令同開舊檔)

vi src.c



 











3. 輸入 :set nu 後按 Enter 表示進入 < 命令模式 >  並開啟顯示行號功能  (剛開啟檔案是在 < 一般模式 >,模式切換參考下圖)


































4. 按 i 進入 < 編輯模式 > (左下角出現 - - INSERT - -)開始編輯程式碼



 

5. 按 Esc 回到 < 一般模式 > (左下角 - - INSERT - - 消失)



 











6. 輸入 :w 後按 Enter 表示進入 < 命令模式 > 並執行存檔動作,如下兩圖
















 











7. 輸入 :q 後按 Enter 表示進入 < 命令模式 > 並關閉檔案



提示:

  1. !!! 重要 !!! 無論在 < 輸入模式 >< 命令模式 > 只要按 Esc 即可回到 < 一般模式 >,再根據需求按 i: 切換模式即可。
  2. :wq 是存檔並關閉檔案,:q!存檔並關閉檔案 (注意: 未儲存的檔案內容將會遺失)。

 

範例二 (快速移動游標、行複製、貼上、行刪除、回上一步)

1. 開啟 src.c

vi src.c














2. 在 < 一般模式 > gg 將游標移到第一行 (按 G 可將游標移到最後一行),再按 yy 可複製游標那一行內容

3. 按 p 將複製的內容貼到游標那行的下一行



 











4. 按 i 進入 < 編輯模式 > (左下角出現 - - INSERT - -)將 stdio.h 改成 string.h



5. 按 Esc 回到 < 一般模式 > (左下角 - - INSERT - - 消失)



 











6. 按 gg 將游標移到第一行,按 dd 可刪除游標那一行的內容



 











7. 按 u 回上一步 (Undo)



 

提示:

  1. ngg 移動到第 n 行,例如: 輸入 10yy,游標會移到第 10 行。
  2. nyy 複製 n 行,例如: 輸入 10yy,複製游標那行與底下9行。
  3. ndd 刪除 n 行,例如: 輸入 10dd,刪除游標那行與底下9行。
  4. Page Up 與 Page Down 可捲動畫面。

 

範例三(搜尋、取代)

1. 在 < 一般模式 > 輸入 / 進入 < 命令模式 >,接著輸入要搜尋的關鍵字(例如: VIM)後按 Enter,游標會移動至搜尋到的關鍵字上



 

2. 接著每按一次 n游標移動到下一個搜尋到的關鍵字

 

3. 文字取代,在 < 一般模式 > 輸入 : 進入 < 命令模式 >,接著輸入被取代字(例如: VIM)與取代字(例如: English),%s/VIM/English/gc,按 Enter 後,會逐一詢問是否取代,按 y 則取代





























提示

  1. %s/VIM/English/gc 的 c (confirm) 表示每次取代前都要詢問,不需要可以不加。
  2. 上面提到按 n 讓游標移動到下一個搜尋到的關鍵字,而按 N 讓游標移動到上一個搜尋到的關鍵字。
  3. !!! 好用 !!! 將游標移動到檔案內容中想搜尋的關鍵字上之後,按 * 可直接搜尋此關鍵字,接著搭配上面提到的 nN 使用。

『好喝杏仁茶』推薦/總整理

 最後更新日期:2021/02/01

每個人的口味與喜好不同,底下個人感想僅供參考

01. 澎湖二崁杏仁茶 FB
※ 地址:澎湖縣西嶼鄉二崁村36號
※ 電話:06-998-3891
※ 個人感想:推,心中第一名,訂一箱12瓶,免運費,但一周內要喝完,可冷凍放比較久。






















02. 杏福的仁 FB
※ 地址:台北市北投區育仁路8巷9號
※ 營業時間:週一至週日 09:00 - 23:00
※ 個人感想:只喝過有糖的,微微杏仁顆粒,好喝。

03. 淡水老街 - 哈味杏仁茶 FB
※ 地址:新北市淡水區公明街75號
※ 營業時間:週一至週六 10:00 - 22:00
          週日      10:00 - 22:30

04. 原杏杏仁茶 FB
忠孝店
※ 地址:台北市大安區忠孝東路四段248巷1號1樓(近捷運忠孝敦化3號出口,星巴克巷內)
※ 營業時間:週一至週四 14:00 - 22:00
          週五、週六 14:00 - 23:00
          週日      14:00 - 22:00
※ 個人感想:不太甜,有杏仁微顆粒,喝冰的有淡淡的杏仁味,滿清新的。





















































05. 于記杏仁 FB
永和比漾店
※ 地址:新北市永和區中山路一段238號(比漾廣場:B2食漾空間)
※ 營業時間:11:00 – 21:30 (一至四及例假日)
          11:00 – 22:00 (週五、週六及例假日前一日)

其他門市介紹

06. 樂華夜市 - 古早味杏仁茶油條
※ 地址:新北市永和區永平路149號
※ 個人感想:甜甜的,無杏仁微顆粒,有杏仁味但不會過重,喝起來很順口。

07. 文化養生豆漿豆花
※ 地址:新北市永和區永貞路267號
※ 個人感想:小甜,無杏仁微顆粒,有杏仁味但不會過重,有點米漿口感。


















































08. 莊記杏仁茶坊
※ 地址:新北市永和區智光街102號1樓之1







































































09. 波比雪花冰杏仁茶
※ 地址:新北市板橋區莒光路200巷33號































10. 古早味杏仁茶
※ 地址:台北市萬華區西昌街247號

11. 蓮鄉冰糖蓮子
※ 地址:新北市永和區得和路180號
※ 個人感想:無杏仁微顆粒,清香杏仁味,口感不稠。
















12. 杏本善 FB
※ 地址:台南市中西區西門路二段372巷8弄4號
※ 個人感想:清香杏仁味,老闆用心製作,店內裝潢別有一番風味。