b94902xxx 板


LINE

[陣列, 指標?] 使徒七最令人頭痛的地方就在於能隨意分離合體, 一下陣列一下指標 很容易讓各位駕駛員迷惑。在使徒七裡你務必要使用陣列來儲存所有的數 字, 然後把這個陣列「傳」給 qsort 函式來處理。 所以我們要先來熟悉 陣列的長相。 相信各位都了解,當我們宣告一個陣列時(比方說 int a[10];), 其實就是要系統在這個 block 中給我們一段連續的記憶體空間來使用, 這個陣列有 10 個元素依序由低記憶體位址排到高記憶體位址,每個元素 的大小是 sizeof(int) ,在大多數 32-bit 的處理器環境下這個值是 4, 表示 4 個位元組(byte),如果是 64-bit 的處理器則為 8,以 32-bit 為例的話,陣列應該是這樣的:(假設從記憶體位址 0x20 開始) 0x20 0x24 0x28 0x2C 0x38 0x3C 0x40 ──┬──┬──┬──┬ ┬──┬──┬── │a[0]│a[1]│a[2]│………│a[8]│a[9]│ ──┴──┴──┴──┴ ┴──┴──┴── 而在 C 語言裡,陣列的名稱用來表示陣列第一個元素的位址,所以在這 個例子中, a 的值是 0x20 ,資料型態是 int *。為什麼是 int * 呢? 因為它表示的是第一個元素 a[0] 的位址, a[0] 的資料型態是 int , 那麼指到 a[0] 位址的變數,自然資料型態是 int * 囉。 如果換到 N-dimentional 的陣列呢?我們以 2-dimentional 陣列 來看,int b[3][2]; 在記憶體中的長相應該是:(一樣假設從0x20開始) ← b[0] →← b[1] →← b[2] → 0x20 0x24 0x28 0x2c 0x30 0x34 0x38 ┬────┬────┬────┬────┬────┬────┬─ ... │ b[0][0]│ b[0][1]│ b[1][0]│ b[1][1]│ b[2][0]│ b[2][1]│... ┴────┴────┴────┴────┴────┴────┴─ 這樣的話,我們可以先看成有三個陣列 b[0], b[1], b[2],用上面的例 子同理可以推出 b[0], b[1], b[2] 的資料型態是 int *,所以再推廣下 去,就能夠得到 b 的資料型態就是 int ** 。 所以到目前為止,你應該可以看得出來 x[y] 這種表示法不過就是 把 x 的位址加上 y 個單位後,該記憶體位址的值。 上面這句話用 C 的寫法就會像是: x[y] == *(x+y); 你一定覺得很奇怪,如果以第一個例子來看的話, a[2] 就等同於 *(a+2); 可是圖例不是說了 a[2] 的位址在 0x28 了嗎? a+2 怎麼會對呢? 你的質疑是對的,小學一年級的學生都知道 20 + 2 是 22 而不是 28, 難道你好人助教是在畫唬爛嗎?.... 請你再注意黃色的那句話,加 y 並不是純數字上的增加 y ,而是加上 y 個單位。還記得 a 陣列每個元素的資料型態是 int 嗎?所以一個 元素的單位是 4個byte (用 32-bit 環境),所以 a + 2 在位址的計算 上就會是 a + 2*sizeof(int),這個單位的換算 compiler 會幫你作完, 所以你只要快快樂樂地寫 *(a+2) 就完全等於 a[2] 了。而在第二個例子, b[0], b[1], b[3] 都是一個 int[2] 的陣列,所以對 b 來說,一個 offset 單位就會是 2*sizeof(int),所以 b + 1 的值就會是 28 了。 這時一定有人會舉一反三,既然陣列的構造如此,只要有一個 base address, 再一個 offset 就可以取值了,那我不就可以寫成這樣: int a[10]; int *p, *q; p = &a[3]; q = &a[6]; 這樣 p[0] == a[3], p[1] == a[4], ...., p[6] == a[9] q[-3] == a[3], q[-2] == a[4], q[-1] == a[5], q[0] == a[6] ... (按:使徒七強烈暗示) 沒錯!這樣寫沒有問題,但要注意的是, p[7] 是不合法的,因為你之前已經 說了你只要連續 10 個 int 的空間,p[7] 會存取到 a[10],在某些系統下 你存取不合法的位址是會讓程式當掉的,但如果系統配置記憶體時剛好在 a 的後面配置其它合法的變數時,p[7] 雖然是合法的記憶體位址,但你可能就 會拿到奇怪的值而讓你的程式出現許多神妙的 bug,所以在用指標或陣列 時必須要格外地小心。 撐到這裡,我十分佩服你沒有直接按 end 略過,但我必須提醒的一點就是: 對一個指標變數而言,在它前面使用 * 運算子表示一個取值的動作 (de-reference),而它取值必須要看這個指標的資料型態來決定。 假如一個指標是 int * ,那你對它取值的時候,就會從你給它的位址開始 抓 sizeof(int) 個 byte 來算出對應的數。這也說明了你不能直接對 void * 的指標變數取值。 void * 雖然可以接任何的指標(因為不管是 哪種資料型態,指標變數的大小都是 sizeof(int),以 32-bit 的系統 來說就是 4 byte),但是當你取值的時候,compiler 沒辦法知道你接 的是哪個資料型態的變數,它只知道目前這個變數是 void *,所以無法 直接取值。以下是個簡單的例子: #define INT 1 #define DOUBLE 2 1 void show(void *p, int option) 2 { 3 switch(option) { 4 case INT: 5 printf("%d\n", *(int*)p); 6 break; 7 case DOUBLE: 8 printf("%f\n", *(double*)p); 9 break; 10 } 11 } ..... int a=10; double b = 20.5; show(&a, INT); show(&b, DOUBLE); 在這個例子中,我們由 option 來決定指標 p 應該要轉型成什麼資料型態, 然後才能取值,在第 5 列我們可以看到,我先將 p 轉型成 int * ,然後 才對它取值,而在第 8 列則是先轉型成 double * 之後才取值。 在使徒七中會用到 comp function,照 qsort 的介面來看,你的 comp function 要有兩個參數 const void *a 及 const void *b。會這樣來 制定介面,是要給使用 qsort 的人方便,因為設計 qsort 的人不知道 你要排序的陣列長什麼樣, comp function 是用來兩兩元素比對使用 的,所以 comp function 的部份保留了彈性用 void * 來接參數, 但你在實作 comp function 的時候就要注意,在 comp function 裡的 運算都必須要經過一個型態轉換(cast)的動作,你必須要把a, b 兩個參數 先轉型到正確的資料型態才能作運算。 關於 qsort 及 comp function 更詳細的秘技先留在後面的 part。 閱讀完這篇文章,你必須先將等級提升到了解陣列及指標的關係後才能 開啟火星之門朝火星邁進。 (待續) --



※ 發信站: 批踢踢實業坊(ptt.cc)
◆ From: 140.112.217.117
1F:推 purincess:辛苦助教了^^ 11/03 02:09
2F:推 doudi: 好人助教我們敬佩您! 11/03 02:11
3F:推 ericsk:不要拿去年的來推齊...= =~ 11/03 02:15
4F:推 shotluck: 好人助教我們敬佩您! 778銀,親手打耶! 11/03 02:18
5F:推 LPH66: 好人助教我們敬佩您! 11/03 02:35
6F:→ hamigwa: 好人助教我們敬佩您! 11/03 07:05
7F:推 tsucci: 好人助教我們敬佩您! 去年多受您的關愛ORz 11/03 07:12
8F:推 ddio: 好人助教我們敬佩您! 11/03 07:43
9F:推 anauma: 好人助教我們敬佩您! 11/03 07:47
10F:→ sa033766: 好人助教我們敬佩您! 亂入XD 11/03 11:00
11F:推 alex1025: 好人助教我們敬佩您! 我是雙班XD 11/03 12:47
12F:推 kcir: 好人助教我們敬佩您! 11/03 16:25
13F:推 alex1025: 好人助教我們敬佩您! 11/03 20:23
14F:推 david4751125: 好人助教我們敬佩您! (驚嘆號是"全形"唷~) 11/03 22:25
15F:推 Sinze: 好人助教我們敬佩您! 超感謝的..恍然大悟 11/03 22:30
16F:推 meconin: 好人助教我們敬佩您! 11/03 22:49
17F:推 victo: 好人助教我們敬佩您! 11/03 22:51
※ 編輯: ericsk 來自: 140.112.31.143 (11/03 22:54)
18F:推 dongogo: 好人助教我們敬佩您! 繼續推! 11/03 23:04
19F:推 iippchen: 好人助教我們敬佩您! 11/03 23:06
20F:推 einstein177: 好人助教我們敬佩您! 11/03 23:15
21F:推 NeantD: 好人助教我們敬佩您! 11/03 23:46
22F:推 yjpetergto: 好人助教我們敬佩您! 11/03 23:56
23F:推 yuyumagic424: 好人助教我們敬佩您! 11/04 08:17







like.gif 您可能會有興趣的文章
icon.png[問題/行為] 貓晚上進房間會不會有憋尿問題
icon.pngRe: [閒聊] 選了錯誤的女孩成為魔法少女 XDDDDDDDDDD
icon.png[正妹] 瑞典 一張
icon.png[心得] EMS高領長版毛衣.墨小樓MC1002
icon.png[分享] 丹龍隔熱紙GE55+33+22
icon.png[問題] 清洗洗衣機
icon.png[尋物] 窗台下的空間
icon.png[閒聊] 双極の女神1 木魔爵
icon.png[售車] 新竹 1997 march 1297cc 白色 四門
icon.png[討論] 能從照片感受到攝影者心情嗎
icon.png[狂賀] 賀賀賀賀 賀!島村卯月!總選舉NO.1
icon.png[難過] 羨慕白皮膚的女生
icon.png閱讀文章
icon.png[黑特]
icon.png[問題] SBK S1安裝於安全帽位置
icon.png[分享] 舊woo100絕版開箱!!
icon.pngRe: [無言] 關於小包衛生紙
icon.png[開箱] E5-2683V3 RX480Strix 快睿C1 簡單測試
icon.png[心得] 蒼の海賊龍 地獄 執行者16PT
icon.png[售車] 1999年Virage iO 1.8EXi
icon.png[心得] 挑戰33 LV10 獅子座pt solo
icon.png[閒聊] 手把手教你不被桶之新手主購教學
icon.png[分享] Civic Type R 量產版官方照無預警流出
icon.png[售車] Golf 4 2.0 銀色 自排
icon.png[出售] Graco提籃汽座(有底座)2000元誠可議
icon.png[問題] 請問補牙材質掉了還能再補嗎?(台中半年內
icon.png[問題] 44th 單曲 生寫竟然都給重複的啊啊!
icon.png[心得] 華南紅卡/icash 核卡
icon.png[問題] 拔牙矯正這樣正常嗎
icon.png[贈送] 老莫高業 初業 102年版
icon.png[情報] 三大行動支付 本季掀戰火
icon.png[寶寶] 博客來Amos水蠟筆5/1特價五折
icon.pngRe: [心得] 新鮮人一些面試分享
icon.png[心得] 蒼の海賊龍 地獄 麒麟25PT
icon.pngRe: [閒聊] (君の名は。雷慎入) 君名二創漫畫翻譯
icon.pngRe: [閒聊] OGN中場影片:失蹤人口局 (英文字幕)
icon.png[問題] 台灣大哥大4G訊號差
icon.png[出售] [全國]全新千尋侘草LED燈, 水草

請輸入看板名稱,例如:WOW站內搜尋

TOP