題目:
int a[]={1,2,3,4,5,6,7,8,9};
short *p=(short*)(a+2);
short *q=(short*)a;
a[p-q]=?
回答:
這道題實際上是有陷阱的,首先你得先向面試官確定一下,int和short的儲存空間,因為在C++標準中,只規定了short和int的最小儲存空間為16位,而具體的大小則和編譯器有關。比如在VC中int為4個位元組,short為2個位元組。
C++的內建類型與其在電腦的儲存空間中的表示方式緊密相關。我們可以用地址表示從該地址開始的任何幾個不同大小的位的集合。要讓地址有意義,就必須要知道儲存在該地址的值的類型。一旦知道了該地址的值的類型,就知道了表示該類型的值需要多少位和如何解釋這些位。因此在C++中定義一個指標時,我們總是要給出這個指標所指向的值的類型,比如,int *a。
大多數電腦將儲存空間中的每一個位元組和一個稱為地址的數關聯起來(也就是按位元組編址),也就是這是一個一對一的關係。如果地址加1的話,那麼地址將指向當前位元組的下一個位元組。(注意是地址而不是指標)。
下面我們來看一下測試結果,測試是在VC++環境下進行的:
代碼:
int a[]={1,2,3,4,5,6,7,8,9};
short *p=(short*)(a+2);
short *q=(short*)a;
cout<<*p<<endl;
cout<<*q<<endl;
cout<<p-q<<endl;
cout<<a<<endl;
int *pa=a;
cout<<++pa<<endl;
cout<<pa-a<<endl;
結果:
a 的值是001DFA9C,而pa的地址是001DFAA0,這裡的數值給出的是地址,由於a和pa是int*指標,所以中間差了4個位元組,也就是說在這裡的一個地址是對應一個位元組。但是為什麼pa和a的差明明是1,而對應的地址值卻差了4,我猜應該是對指標的加減以及自增長都進行了重載運算,這樣的目的就是為了前面所說的,僅僅知道地址是不夠的,我們還需要該地址的值的類型,這樣就知道了表示該類型的值需要多少位和如何解釋這些位。估計這也大概是設計指標的目的,同時這也體現了指標和地址的區別。
我們再回到這道題本身p和q差了8個位元組,由於p,q都是short*,而short在VC++中是佔2個位元組的,所以p-q應該是4。
底下我們跳出這道題,看到下面代碼的結果:
代碼:
int a[]={1,2,3,4,5,6,7,8,9};
short *p=(short*)(a+1);
short *q=(short*)a;
short* q1=q+1;
short* q2=q+2;
cout<<q<<’/t’<<*q<<endl;
cout<<q1<<’/t’<<*q1<<endl;
cout<<q2<<’/t’<<*q2<<endl;
cout<<p<<’/t’<<*p<<endl;
結果:
分析:結果可能跟我們想的有點不一樣,其實之前對於short* q=(short*)a;cou<<*q<<endl;顯示結果為1就沒有一點好奇嗎?這裡就涉及到了位元組的儲存順序,即所謂的高位位元組優先(BIG ENDIAN)和低位位元組優先(LITTLE ENDIAN),進行網路編程的時候,就資料傳送前就經常存在這麼一步轉化。其它通常的BIG ENDIAN和LITTLE ENDIAN僅僅指位元組順序,在硬體設計者的術語中,在一個位元組的內部中也分BIG ENDIAN和LITTLE ENDIAN,但是這些bit順序的不同是透明的,程式員只需要按照邏輯順序來看待和操作位元組內部的bit即可。
關於BIG ENDIAN和LITTLE ENDIAN,在以下這篇文章中說得很清楚:
http://www.cppblog.com/aaxron/archive/2011/02/28/140786.aspx
以下的**之間的就是引用的這篇文章的內容:
*********************************************************************
位元組順序是指占記憶體多於一個位元組類型的資料在記憶體中的存放順序,通常有小端、大端兩種位元組順序。
小端位元組序指低位元組資料存放在記憶體低地址處,高位元組資料存放在記憶體高地址處;
大端位元組序是高位元組資料存放在低地址處,低位元組資料存放在高地址處。
基於X86平台的PC機是小端位元組序的
因為現行的電腦都是以八位一個位元組為儲存單位,那麼一個16位的整數,也就是C語言中的short,在記憶體中可能有兩種儲存順序big-endian和litte-endian。考慮一個short整數0×3132(0×32是低位,0×31是高位),把它賦值給一個short變數,那麼它在記憶體中的儲存可能有如下兩種情況:
可以做個實驗
在Windows上下如下程式
#include <stdio.h>
#include <assert.h>
int main(void)
{
short test;
FILE* fp;
test = 0×3132; /**//* (31ASIIC碼的’1′, 32ASIIC碼的’2′) */
if ((fp = fopen(“c://test.txt”, ”wb”)) == NULL)
assert(0);
fwrite(&test, sizeof(short), 1, fp);
fclose(fp);
return 0;
}
然後在C盤下開啟test.txt檔案,可以看見內容是21,而test等於0×3132,可以明顯的看出來x86的位元組順序是低位在前。如果我們把這段同樣的代碼放到(big-endian)的機器上執行,那麼打出來的檔案就是12。這在本機中使用是沒有問題的。但當你把這個檔案從一個big-endian機器複製到一個little-endian機器上時就出現問題了。
如上述例子,我們在big-endian的機器上建立了這個test檔案,把其複製到little-endian的機器上再用fread讀到一個short裡面,我們得到的就不再是0×3132而是0×3231了,這樣讀到的資料就是錯誤的,所以在兩個位元組順序不一樣的機器上傳輸資料時需要特別小心位元組順序,理解了位元組順序在可以協助我們寫出移植行更高的代碼。
正因為有位元組順序的差別,所以在網路傳輸的時候定義了所有位元組順序相關的資料都使用big-endian,BSD的代碼中定義了四個宏來理:
#define ntohs(n) //網路位元組順序到主機位元組順序 n代表net, h代表host, s代表short
#define htons(n) //主機位元組順序到網路位元組順序 n代表net, h代表host, s代表short
#define ntohl(n) //網路位元組順序到主機位元組順序 n代表net, h代表host, s代表long
#define htonl(n) //主機位元組順序到網路位元組順序 n代表net, h代表host, s代表long
*********************************************************************
這下應該明白為什麼了吧。。。另外再提醒一下:我們只需要關注位元組順序即可,位元組內部的順序我們只要認為和邏輯順序一致即可。