C筆試題
(判斷大端小端模式) 試題1:請寫一個C函數,若處理器是Big_endian的,則返回0;若是Little_endian的,則返回1
解答:
int checkCPU( )
{
{
union w
{
int a;
char b;
} c;
c.a = 1;
return(c.b ==1);
}
}
剖析:
嵌入式系統開發者應該對Little-endian和Big-endian模式非常瞭解。採用Little-endian模式的CPU對操作數的存放方式是從低字節到高字節,而Big-endian模式對操作數的存放方式是從高字節到低字節。例如,16bit寬的數0x1234在Little-endian模式CPU內存中的存放方式(假設從地址0x4000開始存放)爲:
內存地址
0x4000
0x4001
存放內容
0x34
0x12
而在Big-endian模式CPU內存中的存放方式則爲:
內存地址
0x4000
0x4001
存放內容
0x12
0x34
32bit寬的數0x12345678在Little-endian模式CPU內存中的存放方式(假設從地址0x4000開始存放)爲:
內存地址
0x4000
0x4001
0x4002
0x4003
存放內容
0x78
0x56
0x34
0x12
而在Big-endian模式CPU內存中的存放方式則爲:
內存地址
0x4000
0x4001
0x4002
0x4003
存放內容
0x12
0x34
0x56
0x78
聯合體union的存放順序是所有成員都從低地址開始存放,面試者的解答利用該特性,輕鬆地獲得了CPU對內存採用Little-endian還是Big-endian模式讀寫。如果誰能當場給出這個解答,那簡直就是一個天才的程序員。
一、如何判斷CPU是大端還是小端?
明白大端和小端的區別,實現起來就非常簡單:
#include <stdio.h>
int main()
{
union ut{
short s;
char c[2];
}u;
if(sizeof(short) == 2)
{
u.s = 0x0102;
if(u.c[0] == 1 && u.c[1] == 2)
{
printf("big enidan\n");
}else if(u.c[0] == 2 && u.c[1] == 1)
{
printf("little endian.\n");
}
return 0;
}
}
二、大端與小端相互轉換
#include <stdio.h>
typdedef unsigned int u_32;
typedef unsigned short u_16;
#define BSWAP_16(x) \
(u16) ( ((((u16)(x)) & 0x00ff) << 8 ) | \
((((u16)(x)) & 0xff00) >> 8 ) \
)
#define BSWAP_32(x) \
(u32) ( (( ((u32)(x)) & 0xff000000 ) >> 24) | \
(( ((u32)(x)) & 0x00ff0000 ) >> 8 ) | \
(( ((u32)(x)) & 0x0000ff00 ) << 8 ) | \
(( ((u32)(x)) & 0x000000ff ) << 24) \
)
u16 bswap16(u16 x)
{
return (x & 0x00ff) << 8 |
(x & 0xff00) >> 8
;
}
u32 bswap32(u32 x)
{
return ( x & 0xff000000 ) >>24 |
( x & 0x00ff0000 ) >>8 |
( x & 0x0000ff00 ) <<8 |
( x & 0x000000ff ) << 24
;
}
int main(void)
{
//關鍵是要能對錯誤進行處理,給一個0x123490 照樣能得出 0x9034的值,而且, 佔內存要小的
printf("macro conversion:%#x\n",BSWAP_16(0x123490 ));//要能正確轉換
printf("macro conversion:%#x\n", BSWAP_32(0x1234567890)); //要能正確轉換
printf("-----------------\n");
printf("function conversion:%#x\n",bswap16(0x123490));
printf("function conversion:%#x\n", bswap32(0x1234567890));
return 0;
}
下面是一篇從網上摘錄的關於大端小端的帖子,講得比較經典的
Big-endian和little-endian(轉載)
Big-endian和little-endian是描述排列存儲在計算機內存裏的字節序列的術語。
Big-endian是一種大值的一端(序列中更典型值)存在前面(在最小的存儲地址)的順序。Little-endian是一種小值的一端(序列中較不典型的值)存儲在前的順序。比如,在Big-endian的電腦中,需要兩個字節把十六位數4F52當作4F52存在存儲器中(如果4F存在存儲地址1000中,比如說,52將存在1001中)。在little-endian系統裏,將被存爲524F(52存在存儲地址1000中,比如說,4F將存在1001中)。
IBM的370主機,多數基於RISC計算機,和Motorola的微處理器使用big-endian方法。TCP/IP也使用big-endian方法(因此big-endian方法也叫做網絡編碼)。對於人來說我們的語言都是從左到右的習慣方式。這看上去似乎被認爲是自然的存儲字符和數字方式-你同樣也希望以同樣的方式出現在你面前。我們總的許多人因此也會認爲big-endian是流行的存儲方式,正如我們平時所讀到的。另一個方面說,Intel處理器(CPUs)和DEC Alphas和至少一些在他們的平臺的其他程序都是little-endian的。
有個關於little-endian的爭議是如果你增加數字的值,你可能在左邊增加數字(高位非指數函數需要更多的數字)。因此,經常需要增加兩位數字並移動存儲器裏所有Big-endian順序的數字,把所有數向右移。使用little-endian的存儲器中不重要的字節可以存在它原來的位置,新的數可以存在它的右邊的高位地址裏。這就意味着有些計算機的運作可以變得簡單和快速。
語言編譯器比如說Java或FORTRAN必須明確他們開發的目標代碼使用的是什麼存儲方式。有必要的話可以使用轉換器可以用來轉換存儲順序。
注意在使用big-endian和little-endian字節順序中,一個位裏面都是使用big-endian。也就是說,在存儲的字節裏一個位串代表的給定數沒有big-endian和little-endian之分。比如說,有個十六位數4F放在一個給定的字節地質前面或者後面,在這個字節的這一位的裏的順序是:
01001111
從這一位的順序來看有可能是big-endian也可能是little-endian,但是CPUs和程序幾乎都是設計成big-endian位序的。儘管如此在數據的交換中它有可能其中的一種順序。
Eric Raymond評述說Internet域名地址和e-mail地址是little-endian的。比如我們的網址用big-endian來表述是com.whatis.www。
Big-endian和little-endian來自於Jonathan Swift的Gulliver's Travels ,Big-endian是那個大雞蛋是打破大頭的那個政治派別,而反對派Lilliputian King要求他的國民打雞蛋的時候用小的那一頭。
目前的存儲器,多以byte爲訪問的最小單元,當一個邏輯上的整理必須分割爲物理上的若干單元時就存在了先放誰後放誰的問題,於是endian的問題應運而生了。對於不同的存儲方法,就有Big-endian和Little-endian兩個描述。(這兩個術語來自於 Jonathan Swift 的《《格利佛遊記》其中交戰的兩個派別無法就應該從哪一端--小端還是大端--打開一個半熟的雞蛋達成一致。在那個時代,Swift是在諷刺英國和法國之間的持續衝突,Danny Cohen,一位網絡協議的早期開創者,第一次使用這兩個術語來指代字節順序,後來這個術語被廣泛接納了。)
存在“如果說"跟word或者說字長根本就沒關係",假設有一數據文件裏面有N多數順序排布,如果想以Little-Endian format 讀入內存某區域,那麼應該怎麼讀?怎麼排?”這樣的問題是由於對於endian的實質理解的偏差,endian指的是當物理上的最小單元比邏輯上的最小單元小時,邏輯到物理的單元排布關係。這裏的“有一數據文件裏面有N多數順序排布”,這個“有一數據”顯然不是邏輯上的最小單元,而其中的“N多數”的一個纔是邏輯最小單元,於是可應用樓主表格中的原則排列,而“N多數”之間的順序則是由這“N多數”的宿主決定的,比如是你寫的程序,這個順序由你決定。
剛纔談到了,endian指的是當物理上的最小單元比邏輯上的最小單元小時,邏輯到物理的單元排布關係。咱們接觸到的物理單元最小都是byte,在通信領域中,這裏往往是bit,不過原理也是類似的。
實踐可以給你更多的經驗,比如在一個嵌入式系統的通信協議中,從底層射頻驅動到上層的協議棧全部需要實現,那麼很可能遇到多個endian的問題,底層的bit序、協議層的byte序、應用層的byte序,這些都是不同的概念。
Big-Endian 和 Little-Endian 字節排序
字節排序 含義
Big-Endian 一個Word中的高位的Byte放在內存中這個Word區域的低地址處。
Little-Endian 一個Word中的低位的Byte放在內存中這個Word區域的低地址處。
必須注意的是:表中一個Word的長度是16位,一個Byte的長度是8位。如果一個數超過一個Word的長度,必須先按Word分成若干部分,然後每一部分(即每個Word內部)按Big-Endian或者Little-Endian的不同操作來處理字節。
一個例子:
如果我們將0x1234abcd寫入到以0x0000開始的內存中,則結果爲
big-endian little-endian
0x0000 0x12 0xcd
0x0001 0x34 0xab
0x0002 0xab 0x34
0x0003 0xcd 0x12
除了moto的68K系列和dec的sparc是big endian外,常見的cpu都是little endian。ARM同時支持 big和little,實際應用中通常使用little endian。
本文來自CSDN博客,轉載請標明出處:http://blog.csdn.net/flickedball/archive/2009/04/21/4096991.aspx
2.
char str[] = “Hello” ;
char *p = str ;
int n = 10;
請計算
sizeof (str ) = 6 (2分)
sizeof ( p ) = 4 (2分)
sizeof ( n ) = 4 (2分)
void Func ( char str[100])
{
請計算
sizeof( str ) = 4 (2分)
}
void *p = malloc( 100 );
請計算
sizeof ( p ) = 4 (2分)
3、在C++程序中調用被 C編譯器編譯後的函數,爲什麼要加 extern “C”? (5分)
答:C++語言支持函數重載,C語言不支持函數重載。函數被C++編譯後在庫中的名字與C語言的不同。假設某個函數的原型爲: void foo(int x, int y);
該函數被C編譯器編譯後在庫中的名字爲_foo,而C++編譯器則會產生像_foo_int_int之類的名字。
C++提供了C連接交換指定符號extern“C”來解決名字匹配問題。
4.有關內存的思考題
void GetMemory(char *p)
{
p = (char *)malloc(100);
}
void Test(void)
{
char *str = NULL;
GetMemory(str);
strcpy(str, "hello world");
printf(str);
}
請問運行Test函數會有什麼樣的結果?
答:程序崩潰。
因爲GetMemory並不能傳遞動態內存,
Test函數中的 str一直都是 NULL。
strcpy(str, "hello world");將使程序崩潰。
char *GetMemory(void)
{
char p[] = "hello world";
return p;
}
void Test(void)
{
char *str = NULL;
str = GetMemory();
printf(str);
}
請問運行Test函數會有什麼樣的結果?
答:可能是亂碼。
因爲GetMemory返回的是指向“棧內存”的指針,該指針的地址不是 NULL,但其原現的內容已經被清除,新內容不可知。
void GetMemory2(char **p, int num)
{
*p = (char *)malloc(num);
}
void Test(void)
{
char *str = NULL;
GetMemory(&str, 100);
strcpy(str, "hello");
printf(str);
}
請問運行Test函數會有什麼樣的結果?
答:
(1)能夠輸出hello
(2)內存泄漏
void Test(void)
{
char *str = (char *) malloc(100);
strcpy(str, “hello”);
free(str);
if(str != NULL)
{
strcpy(str, “world”);
printf(str);
}
}
請問運行Test函數會有什麼樣的結果?
答:篡改動態內存區的內容,後果難以預料,非常危險。
因爲free(str);之後,str成爲野指針,
if(str != NULL)語句不起作用