the 1st editon: 20120829
好久之前寫的文章, 再不發表可能就不會發表了, 寫技術文章真的很累人。
ref 2 提到, null pointer 和 0 pointer 是不一樣的, 但 c 似乎把 0 pointer 當 NULL pointer 來用。c++ 11 提供了 nullptr 這個 keyword 來表示 NULL pointer。
int main(void)
{
char *p=nullptr;
*p='a';
}
我們來看看 cb.c 這個對位址 0 做寫入動作的程式碼。
看到 L10, L11 大概有人要笑我連基本指標概念都沒有, 0 指標是不能 assign 值的。我的確不是很了解 c 語言的指標, 而我總是在想著, 位址 0 不也是一個位址嗎?哪有不可存取的道理, 其中有著什麼魔法呢?
在 gcc 中 NULL 被 define to (void *)0
char *p=((void *)0);
你和我有相同的疑問嗎?
L10, L11 在 linux 環境下執行, 沒有意外, 得到了 Segmentation fault。
而在 dos 環境下, 得到如下的結果。
9257 (57 92) 是絕對位址 0 的內容, 從 debug 可以看出 (5792), 執行 cb 得到 9257 (57 92), 9261 (61 92) (螢幕最上方的紅色字) 兩個的結果,
這是在
10 char *p=0;
11 *p='a';
前後印出的結果。用 debug 再看一次, 可以得到一樣的結果, 證明絕對位址 0 被改變了。0 pointer 不在是神祕的指標, 它就和一般指標一樣。在 ms dos 下, 絕對位址 0 紀錄某個中斷服務程式的位址。
那為什麼在 linux 下有問題呢?
自然是 linux 把 0 位址做了手腳, 發出個拒絕存取的舉動。這部份我還在研究, 暫時無法提供程式碼的例子。大概就是設定 mmu, 將 0 這個位址做了禁止讀寫的動作, 只要一讀寫 0, 就會發出一個 exception。
這裡有個類似的問題:
http://programmers.stackexchange.com/questions/147713/where-are-null-values-stored-or-are-they-stored-at-all
c_init.S 就是用來印出這兩個位址 0 的值。
source code:
simple os git commit: 5fa08df042d7404a4eb147835c0a048052c3ebbf
simple_os/c_runtime
順便提一下 c++, c++ 11 有 nullptr 這個 keyword, 不過在 g++ 4.7 測試下:
31 //char *p=0;
32 char *p=nullptr;
這兩行沒有差別, 都是翻成 char *p=0;
而在 x86 要存取位址 0 比想像中還要麻煩, 由於獨特的 segment 定址模式, cb.c L7,8,9 就是為了要存取絕對位址 0, 否則 L10 的 0 並不是真正的 0 而是 ds:0, ds 若不是 0, 那就不會存取到絕對位址 0, 真麻煩, 你得在 flat mode 才有這樣使用的機會, 怎麼設定呢? 需要進入 x86 保護模式才行。
那 coretex m3 可以嗎? 很遺憾, 雖然在 cm3 平台上 L10 的確代表著絕對位址 0, 但該位址是 flash 的位址 0, 無法寫入, 也許有方法可以改變位址的 mapping, 不過我沒研究就是, 怎麼樣, 想要直接對位址 0 寫入還沒那麼容易吧!
ref:
- 系統程式員成長計劃 (簡體中文版本) p35
- A zero pointer is not a null pointer: http://lwn.net/Articles/342558/
- 6.14 說真的, 真有機器用非零空指針嗎, 或者不同類型用不同的表達?(本文從英文 C-FAQ (2004 年 7 月 3 日修訂版) 翻譯而來)
沒有留言:
張貼留言
使用 google 的 reCAPTCHA 驗證碼, 總算可以輕鬆留言了。
我實在受不了 spam 了, 又不想讓大家的眼睛花掉, 只好放棄匿名留言。這是沒辦法中的辦法了。留言的朋友需要有 google 帳號。