最近剛剛看完primer,灰頭土臉的,越看越遲鈍,最近又在研究c陷阱與缺陷,看到關於大端(big-endian)與小端(little-endian)的問題時,忽然想到同學上個月問過的乙個筆試問題,於是乎決定死活要整出來,以給自己一一點信心,正好還在猶豫怎麼寫寫自己的primer觀後感,先解決這個問題作為預熱吧。
問題是求下面程式的輸出(假設int型資料佔4個位元組,且為intel x86 cpu):
int main(void)
;int *ptr1=(int *)(&a+1);
int *ptr2=(int *)((int)a+1);
printf("%x,%x",ptr1[-1],*ptr2);
getchar();
return 0;
}這個問題雖然程式很簡短,但是卻涉及到陣列首位址、位址的偏移、強制型別轉換、大小端等問題,初學者解決起來還是要花一定精力的(比如我),其實這個題還考驗乙個人的認真程度,我一開始就忽略了printf函式已經規定了輸出要按照十六進製制格式,做的時候還傻乎乎的當成int型的,結果除錯的時候就越想越不對勁,一鬧脾氣,把他們的整數輸出也給貼出來了,下面會慢慢說到。
慢慢來分析:
int a[5]=; //宣告了乙個陣列,包括五個元素
int *ptr1=(int *)(&a+1);
&a,我們先看a,a如果單獨拿出來就代表了陣列a[5]的首位址,且a的型別為int[5],&a則為取首位址的位址,且&a的型別為int[5]*,
這裡比較拗口,請朋友們自行去參閱關於指標的知識,並且,這裡如果把"&"去掉,你得到的printf的第乙個輸出絕對不是5,具體為多少,賣個關子,朋友們自行解決。&a+1則為指標的加法運算,+1並不是在我們得到的&a的位址上簡單的加一,而是加
&a所指向的型別的位元組數!例如此處,&a指向int[5]型別的a,+1其實是位址在原來的基礎上偏移4*5=20個位元組!指向了a[5]的末尾元素a[4]的下乙個位置(但我們只能讀其位址並不能對其解引用得到裡面的具體值!),這在c++中是允許的。
繼續往下說(int *)(&a+1),眾所周知,在乙個型別外面加上(),就是把後面的元素強制型別轉換,即把&a+1的型別由int[5]*強制轉換為int*型別,會出什麼事呢?我們看一下輸出語句中ptr1[-1],這裡相當於*(ptr1 + (-1))即*(ptr1 -1),即ptr1指向的型別然後向前偏移一位,根據後面我們要講的記憶體的大小端規則,和上面說的指標的加減法則,可以得知-1實際是偏移了4個位元組。最後輸出為0x05,也就是我們看到的5。
好了,下面要講樓主比較痛苦的大小端了,看看這個不厚道的語句int *ptr2=(int *)((int)a+1);
先把a由int[5]很不厚道的強制轉換為int型變數,然後對其加一,再把它強制轉換為int*型別。
這個問題怎麼整呢?最好的辦法是開啟編譯器,一步一步除錯的看結果!比如樓主電腦,此時a值為0x0012ff4c,轉換為int變成1245004,int值+1為1245005,也就是十六進製制的0x0012ff4d,通過強制轉換為指標型別也可以得到(int *)((int)a+1)值為0x0012ff4d。可以發現,上式的結果就是使得指標指向了a[0]的第二個位元組!這麼說不好理解,下面看圖:
例如資料:0x12345678
則大端序為:資料最高優先位的0x12對應記憶體的最低位址 -> 0x34 -> 0x56 -> 資料的最低優先位0x78對應記憶體的最高位址
小端序為:資料最低優先位的0x78對應記憶體的最低位址 -> 0x56-> 0x34-> 資料的最高優先位0x12對應記憶體的最高位址
由此,可知a[5]=在小端機中記憶體的排序為:(按樓主電腦)
現在應該能好好的看明白小端排序了吧?
此時的ptr2其實是加了乙個整型數值1,相當於在記憶體中由a[0]的第乙個位元組指到第二個位元組,下圖顯示了ptr2加一前後的指向:
那麼後面的問題就很簡單了,printf("%x,%x",ptr1[-1],*ptr2);,此時ptr2指向了a[0]的00,按照小端原則,指標指向的是記憶體低位址,其儲存的為資料的低優先位,那麼按照整形資料儲存4個位元組來算,其指向的資料變為0x0012ff4d~0x0012ff50,也就是0x02 00 00 00,這也是其輸出2 00 00 00的原因了!因為樓主一開始沒有注意到printf的%x,而是以為成d%,因此很糾結編譯器中*ptr2的值為33554432,這個也很簡單,將0x02 00 00 00轉換為十進位制也就是1*2^25了,有一點需要注意,ptr2+4指向的位址為0x0012ff5d,但是我們的已知記憶體到了0x0012ff5f也就結束了,所以其所指向的為乙個未知數。
樓主其實也是乙個大菜鳥,祝大家遇到的問題都能順利解決,也祝自己能夠靜下心來的學習c++!
一步一步實現乙個簡單的OS HelloWorld
先弄個helloworld吧,雖然這個網上一大堆,不過不知道的人還是不少,就簡單的弄乙個吧。系統啟動時,第一步是bios自檢 這個不管 然後從cmos中設定的預設引導介質中載入第乙個扇區 512位元組 到記憶體的0x07c00處 0x0000 0x7c00 之後,跳轉執行。所以,引導啟動系統的第一部...
一步一步實現乙個簡單的OS 載入核心
這一塊更新的 比較多,所以就不方便全部貼出來了。具體的 大家直接到群裡面找就可以了 os0.3 另外,我配置了bochs虛擬機器,這樣大家載下來就可以使用bochs虛擬機器直接除錯了。下面我就直接貼一下主要的 吧,setupasm.s 這裡,跳轉到c函式直接call就可以了 start32 初始化保...
一步一步實現乙個簡單的OS 異常處理
簡單的對idt進行初始化,實現系統異常顯示 mmu裡面有大部分巨集定義是抄 自己動手寫作業系統裡面的pm.inc檔案中的 main.c 核心入口 int kmain traps.c 初始化,異常處理 void init traps 裝載idt lidt sys traps,sizeof struct...