看板 C_and_CPP 關於我們 聯絡資訊
想要補充三點 LPH66 大沒有提到的東西(但我寫的可能不適合初學者讀 orz) 第一個,printf 的 %p 只能用來印 void* 不能用來印其他種指標!(感覺可以 寫一篇「如何印指標」到 FAQ 裡面了...)編譯器沒辦法幫你轉型成 void*. 註:我覺得標準「希望」char* 也可以用,但是把標準翻遍還是覺得沒有寫得非 常清楚。 ※ 引述《LPH66 (-858993460)》之銘言: : 是的 這個某處並不是紀錄在某個變數裡 : 以我以前回過的一篇文的講法就是「它是綁在 a 這個符號上」 : 因此當有和它相關的位移運算時它就會把這個某處給代進去去運算 : 因此最後編譯出來的程式中 這個值將會直接出現在指令上而不是存在某個變數裡 第二個我想講一個經典例子:萬一有人寫了兩個檔案,一個有 | int a[100]; 另一個檔案有 | extern int *a; 在很多實作中會死很慘。原因是指標和陣列是不一樣的東西。 : --- : 我們將這個由陣列意義轉變成指標意義的概念稱做 decay : 說成 "陣列 decay 成指標" (以下講 C)第三個就是我用很囉唆的方法講一下指標和陣列的錯覺好了。我個人 覺得最好先把指標和陣列當成完全不同的東西,然後另外記得這個自動轉換規則就好: | Except when it is the operand of the sizeof operator or the unary & operator, | or is a string literal used to initialize an array, an expression that has | type "array of *type*" is converted to an expression with type "pointer to | *type*" that points to the initial element of the array object and is not | an lvalue. If the array object has register storage class, the behavior is | undefined. 複製全文是給想知道所有規則的人看的。以下以慢速度解析到底發生什麼事情: | int a[2][3] = {1,2,3,4,5,6}; a[0] 會被翻譯成 *(a+0), 然後(精神上)執行下列的運算: (1) a 算出一個型態 int[2][3], 代表那個陣列的 lvalue. (2) 按照上述規則先被轉型為型態 int(*)[3], 指向 a 第一個元素的指標,然後 (3) a+0 還是得到一個型態為 int(*)[3], 指向 a 第一個元素的指標;最後 (4) *(a+0) 得到型態為 int[3], 代表 a 第一個元素的 lvalue. (a[0])[3] 則會被翻譯成 *(*(a+0)+3) 就從上面的部份繼續吧 xD (5) *(a+0) 先被轉型為型態 int*, 指向 a[0] 第一個元素的指標,然後 (6) *(a+0)+3 得到一個型態為 int*, 指向 a[0] 第四個元素的指標;最後 (7) *(*(a+0)+3) 得到型態為 int, 代表 a[0] 第四個元素的 lvalue. C 語言讓人產生錯覺的地方就是步驟 (2) 和 (5). 然後... | int (*b)[3]; | b = a; (1-1) b 算出一個型態 int (*)[3], 代表那個指標的 lvalue. (2-1) a 算出一個型態 int [2][3], 代表那個陣列的 lvalue. (2-2) a 根據上面規則轉型為型態 int (*)[3], 指向 a 第一個元素的指標 (3) b = a 算出的值型態為 int (*)[3] 但不是 lvalue. 副作用是把左邊代表的 物件的內容,更新為右邊算出來的值。兩邊型態一樣所以不太需要管轉型。 (註:步驟 (1-1) 可能實際上在步驟 (2-1) 甚至步驟 (2-2) 之後。) 個人覺得先把陣列和指標當成完全不同的東西,然後再去記 C 語言很多地方會自 動轉型(也就是所謂的 "decay")觀念會比較清楚一點點。 -- ※ 發信站: 批踢踢實業坊(ptt.cc) ◆ From: 140.112.30.39 ※ 編輯: Favonia 來自: 140.112.30.39 (07/24 20:36)
Nairoda:問一下 (3) b = a 是個 expression, 傳回 b 的值, 所以 F 07/24 20:53
Nairoda:大才說 "算出的值型態為 int (*)[3] 但不是 lvalue." 嗎? 07/24 20:53
嗯 a=b 是一個 expression (但這跟 int a=b; 的等號不同)
Nairoda:而接下來的 "副作用" 是指 "賦值" 這件事嗎 ? 還是指某種 07/24 20:56
Nairoda:作用? 07/24 20:56
這裡的副作用就是賦值這件事情沒錯! ※ 編輯: Favonia 來自: 140.112.30.39 (07/24 20:57)
Nairoda:多謝 L 與 F 大! 07/24 21:20
※ 編輯: Favonia 來自: 140.112.30.39 (07/24 21:25)
firejox:正常人不會去寫那種東西....extern int *a;... 07/24 22:17
firejox:另外也不要去擔心%p 與void*的事了... 07/24 22:25
tropical72:我好奇的是,真的有程式碼release後還會輸出address嗎? 07/24 22:26
james732:第一個我很意外... void *與int *我以為都只是一個'值' 07/24 22:29
james732:對於 %p 來說,void *與int *會有什麼差別呢? 07/24 22:29
因為不同型態的指標的表示法和對齊要求可能會不一樣。我現在用的電腦,根據 別的標準,剛好都一樣(連函數指標都一樣),所以很難給一個會壞掉的例子... 以下兩部份給有興趣看 C99 標準的人: | A pointer to void shall have the same representation and alignment | requirements as a pointer to a character type. 39) Similarly, pointers | to qualified or unqualified versions of compatible types shall have | the same representation and alignment requirements. All pointers to | structure types shall have the same representation and alignment | requirements as each other. All pointers to union types shall have | the same representation and alignment requirements as each other. | Pointers to other types need not have the same representation or | alignment requirements. @ C99 6.2.5/27 | 39) The same representation and alignment requirements are meant to | imply interchangeability as arguments to functions, return values | from functions, and members of unions. @ C99 註腳 39(沒有任何約束力)
stonehomelaa:extern int *a; 就是誤以為指標跟陣列是一樣的 07/24 22:32
firejox:debug的東西是不太會出現在release code裡... 07/24 22:33
編輯:補上一個附註(跟上面註腳 39 有關)。 ※ 編輯: Favonia 來自: 140.112.30.39 (07/25 04:01)
firejox:我記得那段是指用不同的指標存取出來的內容... 07/25 10:08
firejox:跟指標的位址似乎沒有關聯... 07/25 10:09
firejox:http://ppt.cc/;OrA 07/25 10:17
Wikipedia 這段討論的是 6.3.2.3(跟「指向的型態」的對齊要求有關),但我 引用的是 6.2.5(跟「自己的型態」的對齊要求有關)。這裡 printf 想要印出指標 自己,沒有要印出指標指到的內容,所以我想參考 6.2.5 是對的。 ※ 編輯: Favonia 來自: 140.112.30.39 (07/25 15:08)
firejox:我覺得你highlight的那段好像是不需要做轉型耶... 07/25 18:31
不太懂你的意思 @@ 它只有說 (1) 指向 void 和指向 char 表示法和對齊要求一樣 (2) 指向 T 和指向 xxx T 一樣的表示法和對齊要求一樣 (3) 指向結構的指標表示法和對齊要求一樣 (4) 指向 union 的指標表示法和對齊要求一樣 其他沒講的指標可以長得很不一樣 <-- 我 highlight 這句 ※ 編輯: Favonia 來自: 140.112.30.39 (07/25 21:43)
firejox:你highlight的那句不是說 不需要"一樣"嗎? 07/25 21:47
「不需要一樣」這句話是講給實作聽的。也就是實作可以在標準給的限制下隨便挑 自己喜歡的表示法;程式設計師不能假設表示法一樣。這句話不是對程式設計師說「不 一樣也可以交叉使用」。除了特別規定的例外外,程式設計師都不能假設交叉使用一定 會成功(如果只有參考語言標準)。 ※ 編輯: Favonia 來自: 140.112.30.39 (07/25 23:00)
LPH66:C FAQ 5.17 或許可以做個參考: 07/26 16:06