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號
※ 個人感想:清香杏仁味,老闆用心製作,店內裝潢別有一番風味。

2018年11月11日 星期日

『Linux』什麼是 DNS SRV 軟體安裝/設定教學

建立 XMPP 測試環境時,遇到的第一個問題就是 XMPP server 的 FQDN 名稱解析,根據定義 XMPP 的 RFC 6120 指出,FQDN 名稱解析會優先使用 DNS SRV (RFC 2782),本文將以 bind9 來實做 DNS SRV。

FQDN: Fully Qualified Domain Name

RR: Resource Record

在實做之前先來談談一般 client 是如何找到 server,可能情形有

1. 已經知道確切的 server IP address
2. 知道 server 的 FQDN,透過 DNS 取得 IP address
3. 使用 broadcast 方式去詢問,例如:DHCP discover

而 DNS SRV 是一種取得 server FQDN 的方法,用 service name, 使用的協定 (ex: TCP/UDP) 與 domain name 來詢問 DNS server,以得知 domain 裡支援此服務之 server(s) 的 FQDN,取得的 FQDN 可能不只一個。


接著根據 DNS SRV response 的 additional section 內含之資訊有兩種可能
1. 包含 FQDN 的 IP address,不需再詢問 DNS server
2. 不包含,所以需要再用 FQDN 去詢問 DNS server 以取得 IP address

用一句節錄自 RFC 2782 的敘述來說明 DNS SRV RR 的用途
The SRV RR allows administrators to use several servers for a single domain, to move services from host to host with little fuss, and to designate some hosts as primary servers for a service and others as backups.

SRV RR 允許管理者在單一個 domain 裡使用多個 server(s)(多個 servers 提供相同服務),當提供服務的主機換成另外一台主機時,還能夠維持良好的使用者體驗,且能指定某些主機做為服務的主要 servers 而其他主機做為備份 servers。


Bind9 設定實例


DNS SRV RR 的 format 如下

_Service._Proto.Name TTL Class SRV Priority Weight Port Target

底下是 bind9 的 SRV RR 實例 (domain 以 example.com 為例)

_xmpp-client._tcp   IN    SRV 0        5      5222 xmpp.example.com
_xmpp-server._tcp   IN    SRV 0        5      5269 xmpp.example.com

對應到 DNS SRV RR 的格式如下

_Service._Proto     Class SRV Priority Weight Port Target

安裝設定步驟 (以 Ubuntu 為例)

1. 安裝 bind9

   apt-get install bind9


2. 建立 db.example.com


   cd /etc/bind/

   cp db.empty db.example.com

3. 設定 db.example.com


   把 db.example.com 中的 localhost 都取代成 example.com
   再將上面兩行實例加入 db.example.com
   還要新增對應的 A RR or AAAA RR



4. 新增 zone

   將底下四行加入 named.conf.local

   zone "example.com" {
        type master;
        file "/etc/bind/db.example.com"; 
   };

5. 重啟 bind9


   service bind9 restart


測試 DNS SRV


此處使用 dig 指令來做測試


dig -t SRV _xmpp-client._tcp.example.com




從 dig 輸出可以看到查到的 FQDN 是 xmpp.example.com 且 additional section 還有包含 xmpp.example.com 的 IP address

補充
DNS SRV(RFC 2782)指出 SRV RR 的 service name 由 RFC 1700 定義,但 RFC 6335 (RFC 2782 的 update) 提到 DNS SRV 並沒有指出是 RFC 1700 的哪個章節有定義,且 IANA 也沒有分配 service name 的程序,造成不受 IANA 管理的非正式 service name 的產生。

2018年7月10日 星期二

水電 | 換洗手台水龍頭

需要的工具與材料:
1. 四分水龍頭 x 1
2. 一字起子 x 1

步驟一:確認水龍頭的型式

一般洗手台的水龍頭都是四分的水龍頭,四分指的是水龍頭底部管子的尺寸,就是照片中手握住的金屬管狀處(如圖一),因為管狀處是要連接水管,所以尺寸不要買錯。


圖一





















步驟二:將圖一的白色塑膠環轉下來

圖一底部金屬管狀處有兩個東西,第一個是黑色橡膠環,第二個是白色塑膠環(如圖二)。請先將白色塑膠環轉下來並收好,等一下要用。


圖二




















步驟三:找到水龍頭水閥

觀察洗手台的底部,發現有兩條鋼絲軟管 (如圖三),選擇要換掉的水龍頭之軟管,順著軟管找到一個一字型的水閥 (如圖四紅色圈圈處)。

圖三




















圖四

















步驟四:關閉水龍頭水閥

先將水龍頭打開讓水流出來,再使用一字起子插入水閥的一字凹痕中,順或逆旋轉水閥到水不再從水龍頭流出。

注意:此步驟很重要,換水龍頭必須在關閉水龍頭的水閥後才進行,否則水龍頭一拆下來,水會從水管噴出來。

步驟五:轉下連接水龍頭的鋼絲軟管

從洗手台底部,用手去旋轉連接在水龍頭底部的鋼絲軟管,鋼絲軟管的尾端有可以旋轉的部分 (如圖五紅色圈圈處),整個轉開之後,管子內還有一些水,沒有關係。

圖五




















步驟六:轉下水龍頭底部的白色塑膠環

再從洗手台底部,用手去旋轉水龍頭底部的白色塑膠環(如圖六),轉下來後,水龍頭就已經可以拆下來(如圖七)。

圖六




















圖七




















步驟七:將新的水龍頭插入洗手台

將新的水龍頭連同前面提到的黑色橡膠環一起插入洗臉盆(如圖八)。

圖八




















步驟八:逆操作 步驟六 -> 步驟五 -> 步驟四

將步驟二拆下來的白色塑膠環,從洗手台底部,旋轉鎖回新水龍頭的底管,鎖好後,嘗試開關新水龍頭,確認新水龍頭沒有左右移動,表示白色塑膠環有鎖緊。

將鋼絲軟管,從洗手台底部,旋轉鎖回新水龍頭的底管。

使用一字起子轉開水龍頭水閥,打開新水龍頭,確認水有流出,根據水流量決定水閥轉開的程度。

恭喜到此為止,安裝完畢。

番外篇:

新水龍頭裡有一個白色圓形塑膠塊(如圖九),我有把它用夾子拔出來,水流比較順,不確定用途。

圖九