看板 C_and_CPP 關於我們 聯絡資訊
※ 引述《purpose (purpose)》之銘言: : 接前一篇的討論,我來解釋看看為什麼 const int 和 const int * 就可以? : 首先強調一下,類似的事件,都會有一個被害人跟加害人。 : 而這樣的轉型: : int **ptr; : const int **thirdparty = ptr; : 往往是被加害人利用來作案的第三者。 : 被害人: : 受到 const 保護,可能被存放在唯讀區域的資料物件, : 比如 : const Dog lucky; 或 const int maxNum; : 加害人: : Dog *p2lucky; 或 int *p2maxNum; : 加害人往往透過 p2lucky->modify(xxx); 或 *p2maxNum = -1; 這樣的指令, : 去破壞編譯器承諾的 const 保護。 : 但是正常情況下,編譯器不允許 p2maxNum = &maxNum; 這類轉型, : 所以被害者與加害者,就好像白富美與魯肥宅一樣, : 肥宅連走近白富美一公尺都做不到,因此犯罪事件無法發生。 : 但是,這個世界上是有掃廁所阿姨的,阿姨剛好就是能溝通白富美與魯肥宅的關鍵, : 阿姨即為案件中的第三者 const int **thirdparty; : 首先肥宅會喬裝成好人,騙阿姨把掃廁所的工作機會交給他: : const int **thirdparty = &p2maxNum; : 編譯器就好像阿姨的上司,如果是遵守 C++ 標準的編譯器, : 會在這個時候就禁止阿姨讓「喬裝後的肥宅」(&p2maxNum) 混進來。 : 當肥宅混進去後,就會利用掃廁所的機會,接觸被害人: : *thirdparty = &maxNum; : 已知 thirdparty 等於 &p2maxNum,代入上式得到 : *(&p2maxNum) = &maxNum; : 將 *& 相抵銷,就得到 : p2maxNum = &maxNum; : 所以編譯器原本禁止的轉型,最終還是被無知的掃廁所阿姨破壞掉,讓肥宅得逞: : *p2maxNum = -1; : 然後不意外的話,會因為違法寫入記憶體,程式在執行時期會當掉。 : 回到最前面的問題,為什麼 const int ** 不行,而 const int * 就可以。 : 以上面的白富美為例,她的型態是 const int,要攻擊她就得是 int * 的指標。 : 今天 const int* 如果接受了攻擊者 int * 的位置: : const int *掃廁所阿姨 = 魯肥宅; // int *魯肥宅 : 那就沒有空間可以去跟白富美接觸。 : 反之,如果 : const int *掃廁所阿姨 = &白富美: // const int 白富美; : 那同樣也沒有空間去跟肥宅接觸。 : 因此 const int * 這樣的阿姨,是毫無殺傷力的阿姨,編譯器無須限制,無須理會。 對於(int *)可以轉(int const *)我還是有點不了解 例如以下程式 #include <stdio.h> #include <stdlib.h> void func(int const *b, int *fackb) { printf("value of *b: %d\n", *b); *fackb += *b; printf("value of *b: %d\n", *b); } int main(void) { int *a = (int *)malloc(sizeof(int)); *a = 5; func(a, a); free(a); return 0; } root@localhost:~#./a.out value of *b: 5 value of *b: 10 root@localhost:~# 像這樣的一隻程式,編譯器(gcc, g++, clang)都沒有任何抱怨 那這樣不就違反了*b是const的承諾了嗎? -- Sent from my Android -- ※ 發信站: 批踢踢實業坊(ptt.cc), 來自: 114.37.141.187 ※ 文章網址: https://www.ptt.cc/bbs/C_and_CPP/M.1423468631.A.405.html
azureblaze: 你寫入的是fackb沒錯啊02/09 16:00
azureblaze: const int*b 保證的是不能經由b修改02/09 16:01
※ 編輯: OPIV (114.37.141.187), 02/09/2015 16:05:44
purpose: 有的書用遙控器來形容指標或參考,const 加在 b 就只是02/09 17:33
purpose: 承諾這隻遙控器的轉台功能被拿掉,轉台鍵按下去也不會02/09 17:34
purpose: 通電發生作用。fackb 就是你跑去外面買另一隻遙控器來轉02/09 17:35
purpose: 修正一下三樓推文,應該說 const 被加在 *b02/09 17:50
TobyH4cker: *b += *fackb; 應該就不能了02/09 22:24
adcvc: int const *a和const int *a是一樣的02/09 23:12
小弟還是有一點問題耶...... int main() { //* //* 第一個例子 //* //* (int const *) = (int *) => OK //* int *ip; int const *icp2ip = ip; //* This is OK //* 修改 const 的方法: //* *ip = (int); //* //* 第二個例子: //* //* (int *) = (int const *) => WARNING //* int const *icp; int *ip2icp = icp; //* This is NOT permitted //* 修改 const 的方法: //* *ip2icp = (int); //* //* 在第一個例子裡 //* *icp2ip 的值可以被 *ip 更改 //* //* 在第二個例子裡 //* *icp 的值可以被 *ip2icp 更改 //* //* 那麼為什麼第一個合法第二個就不行呢? //* return 0; } ※ 編輯: OPIV (114.37.141.187), 02/10/2015 14:00:03
adcvc: 根據小弟對指標的淺見 int const *icp沒有給出始值時應該就02/10 14:38
adcvc: 抱歉,沒給初始值也沒關係哈哈哈。anyway第一個合法是因為02/10 14:41
adcvc: 第一個可行是因為icp2ip是不可改變ip位置內的值的02/10 14:43
adcvc: 而第二個不行是因為*ip2icp會改到*icp的值,這樣會違反02/10 14:44
adcvc: const int的意義,所以不能這樣使用。02/10 14:44
adcvc: 回答得有點亂= = sorry02/10 14:45
沒關係^^ 只是,第一個不是還是可以用 *ip = xxx; 來更改 *icp2ip 嗎? int *ip = (int *)malloc(sizeof(int)); const int *icp2ip = ip; *ip = 5; printf("%d", *icp2ip); // 5 *ip = 6; printf("%d", *icp2ip); // 6 ※ 編輯: OPIV (114.37.141.187), 02/10/2015 14:50:32 ※ 編輯: OPIV (114.37.141.187), 02/10/2015 14:58:39
purpose: 那就還是一樣,指標是遙控器,malloc出來的地方是電視機02/10 14:56
purpose: const 限制的始終還是遙控器本身,當 const 在 * 左邊 02/10 14:57
purpose: 也只是代表,遙控器被限制成不能把電視轉台,而不代表 02/10 14:58
purpose: malloc製造出來的電視機,從此被你鎖住,變成不可更改 02/10 14:59
※ 編輯: OPIV (114.37.141.187), 02/10/2015 15:03:19 感謝 p大! 我好像懂了! 如果把"=" 看做是複製遙控器,那麼複製遙控器就有四種可能性: 1. (int *) = (int *) 2. (int *) = (const int *) 3. (const int *) = (int *) 4. (const int *) = (const int *) 要複製遙控器的時候,不可以用一般的遙控器去複製被鎖定的遙控器,即 (int *) = ( const int *),因為這樣"被鎖定"的特性就漏失了 而 (const int *) = (int *) 則可以理解成是複製了一般的遙控器之後再把它鎖定,這 樣應該是很合理的 所以 (int *) = (const *int) 是不合法的 而(const int *) = (int *) 則是合法的 ※ 編輯: OPIV (114.37.141.187), 02/10/2015 15:21:49 ※ 編輯: OPIV (114.37.141.187), 02/10/2015 15:25:37
purpose: 這種規定很常見,比如 Windows 提供 CreateFile() 函數 02/10 15:26
purpose: 你可以呼叫他去到處亂開別人的檔案、設備,然後 OS 傳回02/10 15:26
purpose: 的類指標,會看你帳號來給于權限,你的轉型就好像要把02/10 15:27
purpose: 本來你只能唯讀的東西,開成有完全控制權限的指標一樣02/10 15:28
糟糕!那我又有新問題了= = 如果照上面這樣講,這句應該是不會有 warning 的才對啊 (int const *const *) = (int **) 但是 clang 居然叫了!!! ※ 編輯: OPIV (114.37.141.187), 02/10/2015 15:57:26
RealJack: (int const*const*)=(int **)在g++沒問題,要不要貼個02/10 17:12
RealJack: 警告訊息參考一下02/10 17:12
R 大 here you are: test3.c:6:20: warning: initializing 'const int *const *' with an expression of type 'int **' discards qualifiers in nested pointer types [-Wincompatible-pointer-types-discards-qualifiers] int const *const *b = a; ^ ~ 1 warning generated. 剛剛試過了,會 warning 的有 clang test.c gcc test.c 不會 warning 的有 clang test.cpp g++ test.cpp g++ test.c ※ 編輯: OPIV (114.37.141.187), 02/10/2015 18:40:20
purpose: C 語言的隱式轉型規則,只有簡單加上一個 const 所以不會02/10 18:59
purpose: 變成 const int *const * 但是 C++ 的隱式轉型規則可以 02/10 19:00
purpose: 直接把 int ** 隱式轉成 const int *const * 這很安全 02/10 19:01
所以說,我應該要強制轉型囉!? 這樣聽起來意思應該是說 C 的隱式轉型不會自動轉,所以丟 warning 告知你一下,C++ 會,所以沒差,是這樣嗎? 那前面的(const int *) = (int *)那些為什麼不需要強制轉型呢? 有沒有什麼資料是關於轉型的規則的呢? ※ 編輯: OPIV (114.37.141.187), 02/10/2015 22:38:50
purpose: http://c-faq.com/ansi/constmismatch.html 倒數第二段02/10 23:31
purpose: 所以 int ***p; int ** const*p2 = p; 對 gcc 是完全OK02/10 23:32
嗯嗯 感謝 p 大的幫助 我找到一個把 const 說明得很好的文件 應該對很多人會有幫助 "http://www.dansaks.com/articles/1998-08 What const Really Means.pdf" ※ 編輯: OPIV (114.37.141.187), 02/11/2015 12:04:22