一道C語言訪存題目的引申(From林健的BLOG)



畢業生求職的時節,非畢業生接觸到各種面試、筆試題目的機率也會相應地增加。下面請看一道經典的 C 語言指針訪存題目,稍有些經驗的朋友應該很快可以看出這個題目考查的是字節序、內存佈局等知識點。然後在大腦中略排列一下,就能夠給出答案(2000000)。

  1. #include < stdio.h >
  2.  
  3. int   main ()
  4. {
  5.     int   a [ 5 ] = { 1 , 2 , 3 , 4 , 5 } ;
  6.     int * pa = ( int )( & a ) + 1 ;
  7.     printf ( " %x / n " , * pa ) ;
  8.     return   0 ;
  9. }

  不過,這個答案是否絕對正確,還要看題目所處的上下文了。如果題目明確說是在常見的 32 位 x86 平臺上運行,那就無可厚非;但如果沒有指明機器架構,那就要小心一點了,也許命題者真想考查一下求職者對非 x86 平臺的瞭解程度呢。如果考慮機器架構,這個題目應當如何作答呢?粗想一下,我們需要考慮的是字長、字節序和對齊(alignment)訪問規則。不過真要 做實驗看看,會發現這裏面還是有一些花樣的。如果沒有實際經驗,只憑教條加推測,很可能想不到其它平臺上的一些細節之處。
  我們換用一段信息量更豐富的程序來進行後續的實驗。在不同的平臺上,均使用未加特殊參數的 gcc 來編譯這段程序——

  1. #include < stdio.h >
  2.  
  3. int   main ()
  4. {
  5.     int   x ;
  6.     int   a [ 5 ] = { 0x11121314 , 0x21222324 , 0x31323334 , 0x41424344 , 0x51525354 } ;
  7.     for   ( x = 0 ; x < 20 ; x ++ ) {
  8.         printf ( " %02x " , * ( char * )(( int )( & a ) + x )) ;
  9.     }
  10.     printf ( " / n " ) ;
  11.     for   ( x = 0 ; x < 8 ; x ++ ) {
  12.         printf ( " %08x " , * ( int * )(( int )( & a ) + x )) ;
  13.     }
  14.     printf ( " / n " ) ;
  15.     return   0 ;
  16. }

  在 32 位 x86 下的結果不需要多解釋。

  1. uname -a
  2. Linux ubuntu 2.6.31-14-generic #48-Ubuntu SMP Fri Oct 16 14:04:26 UTC 2009 i686 GNU/Linux
  3. ./a.out
  4. 14 13 12 11 24 23 22 21 34 33 32 31 44 43 42 41 54 53 52 51
  5. 11121314 24111213 23241112 22232411 21222324 34212223 33342122 32333421

  而在 64 位的 x86_64 下,由於 8 字節的指針被截斷到了 4 字節的整型長度,故會引發段錯誤。同樣的情況出現在 64 位的 Alpha 機器下。解決辦法自然是把運算地址時的 int 修改成 long 或某種顯式的 64 位類型。修改後的結果應該與 32 位 x86 一致。

  1. uname -a
  2. Linux ubuntu 2.6.24-22-generic #1 SMP Mon Nov 24 19:35:06 UTC 2008 x86_64 GNU/Linux
  3. ./a.out
  4. Segmentation fault
  1. uname -a
  2. NetBSD sdf 2.1.0_STABLE NetBSD 2.1.0_STABLE (sdf) #0: Fri Mar 30 02:24:32 UTC 2007  root@ol:/var/sys/arch/alpha/compile/sdf alpha
  3. ./a.out
  4. Memory fault (core dumped)

  有趣的是在 XScale(Intel 實現的 ARMv5)下,雖然同屬 little-endian,但非對齊取數時出現了在字內按字節循環的移位的結果。查查 ARM 的官方文檔 ,這確實是 ARMv5 的特性;而在 ARMv6 以後,非對齊訪問則是完全支持的。

  1. uname -a
  2. Linux zaurus 2.4.18-rmk7-pxa3-embedix #1 Sat, 06 Aug 2005 12:22:55 +0000 armv5tel unknown
  3. ./a.out
  4. 14 13 12 11 24 23 22 21 34 33 32 31 44 43 42 41 54 53 52 51
  5. 11121314 14111213 13141112 12131411 21222324 24212223 23242122 22232421

  接下來看看 PowerPC,它是 big-endian 的代表,允許 32 位以內的非對齊訪問,結果是容易理解的。有關 PowerPC 非對齊訪問的一些細節可以參考這篇文章

  1. uname -a
  2. AIX aix 3 5 00C97AC04C00 powerpc unknown AIX
  3. ./a.out
  4. 11 12 13 14 21 22 23 24 31 32 33 34 41 42 43 44 51 52 53 54
  5. 11121314 12131421 13142122 14212223 21222324 22232431 23243132 24313233

  同樣是 big-endium 的 SPARC 則不允許非對齊訪問。它會對非對齊訪問拋出 SIGBUS。

  1. uname -a
  2. SunOS t1000 5.10 Generic_118833-33 sun4v sparc SUNW,Sun-Fire-T1000 Solaris
  3. ./a.out
  4. 11 12 13 14 21 22 23 24 31 32 33 34 41 42 43 44 51 52 53 54
  5. Bus Error (core dumped)

  最後看看我們中科院計算所的龍芯(Loongson)2E,它是兼容 MIPS 架構的處理器。很多教科書告訴我們說通常的 MIPS 是不允許非對齊訪問的(部分 MIPS 實現提供了非對齊訪問指令,並申請了專利),但我們在龍芯下卻得到了和 x86 相同的、允許非對齊訪問的結果,這又是爲什麼呢?初步查到的原因是“(針對龍芯修改過的 Linux)內核裏確實有一個異常處理函數負責處理 lw 訪問非對齊地址引起的異常 ”。這也許是龍芯繞開 MIPS 專利的一種辦法?我會向龍芯團隊的同學求證一下,也希望熟悉 MIPS 或龍芯的朋友給我一個確切的答案。

  1. uname -a
  2. Linux Loongson-1 2.6.18.1lemote #1 Sat Jan 13 16:02:26 CST 2007 mips GNU/Linux
  3. ./a.out
  4. 14 13 12 11 24 23 22 21 34 33 32 31 44 43 42 41 54 53 52 51
  5. 11121314 24111213 23241112 22232411 21222324 34212223 33342122 32333421

  不過用心思考的朋友也許會發現上面一系列實驗存在的一個疏漏:沒有考慮編譯器的影響。一方面,編譯器可能對整型的字長有不同的規定(例如 Windows 下的某些編譯器即使在 32 位 x86 上也會把 int 定義爲 16 位);另一方面,編譯器可以對不支持非對齊訪問的處理器生成一定的指令序列、通過多次訪存來模擬非對齊訪問。我們看下面的例子:還是在 SPARC 平臺上,改用 Solaris 自帶的 Sun CC 來編譯實驗程序,這時就不會出現“Bus Error”,而會輸出和 PowerPC 一樣的結果。因爲 SunCC 默認會使用“-xmemalign ”參數來生成適當的訪存指令序列。

  1. uname -a
  2. SunOS t1000 5.10 Generic_118833-33 sun4v sparc SUNW,Sun-Fire-T1000 Solaris
  3. cc data.c
  4. ./a.out
  5. 11 12 13 14 21 22 23 24 31 32 33 34 41 42 43 44 51 52 53 54
  6. 11121314 12131421 13142122 14212223 21222324 22232431 23243132 24313233

  這樣看來,在不指定機器架構和編譯器等上下文的情況下,要正確且完美地回答一開始的那道題目還是需要一定知識積累的。答案省略,留給大家自己求 解。在面試、筆試諸如 Sun SPARC、IBM PowerPC、中科院計算所微處理器中心等部門或者做 ARM 等嵌入式開發的公司時,最好先了解清楚它們的產品常識。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章