2013-04-24 90 views
4

未對齊INT指針我有以下的測試程序:GCC認爲在ARM

#include <string.h> 
int q(int *p) { 
    int x; 
    memcpy(&x,p,sizeof(int)); 
    x+=12; 
    memcpy(p,&x,sizeof(int)); 
    return p[0]; 
} 

當我編譯此使用GCC 4.7.2臂-Linux的gnueabihf,編譯器懷疑的指針訪問可以是未對齊的,註釋的加載和存儲在裝配輸出,例如:

ldr  r0, [r0, #0] @ unaligned 

如果我-mno-unaligned-access編譯,編譯器不會發出的所有直接加載和存儲,但調用庫memcpy來代替。但事實上,這種情況下的指針永遠不應該是不對齊的。這是gcc中的一個忽略,還是我誤解了?

+0

請張貼的全拆裝編譯後的代碼。 – 2013-04-24 15:21:31

+1

它使用int q(int * restrict p){p [0] + = 12;返回p [0];}'。 'mov r3,r0','ldr r0,[r0]','add r0,r0,#12','str r0,[r3]','blx'。你爲什麼要編碼?誠然,它應該是一致的。正如其他人所指出的那樣,你用'memcpy()'來混淆它。至少,'x = p [0];'似乎是半理智的。你爲什麼使用'memcpy()'作爲一項任務?下一個人會出現並聲明'* p'是一個字節交換版本。也許更大的問題會有所幫助? – 2013-04-24 16:34:08

+0

使用'memcpy'的要點與嚴格別名規則兼容。給定的'int *'指針可能來自指向另一種類型的指針。 – 2013-04-25 07:50:07

回答

1

如果你的linux內核版本在2.6.28之前。 GCC會拋出這個Warning-munaligned-access支持未對齊地址上的訪問存儲器。這要求這些系統的內核啓用這種訪問。另外,不支持未對齊的訪問,所有代碼必須使用-mno-unaligned-access進行編譯。上游Linux內核版本自動且無條件地支持由GCC發出的未對齊訪問,因爲此選項自版本2.6.28開始有效。

+0

你的意思是哪個'錯誤'? – 2013-04-24 16:14:58

+0

我猜他的意思是「警告」而不是「錯誤」。請注意,對於不支持未對齊的加載/存儲的處理器,2.6.28+內核將具有「對齊陷阱」,如果對齊,這將是1000個指令與1-3個循環。 – 2013-04-24 16:41:50

2

我覺得gcc確實是由int*迷惑被轉換爲在呼叫void*memcpy並假設最壞的這樣一個指針。它可能試圖查看底層指針是否正確對齊。你有沒有嘗試更高的優化水平?這可能是在更高的水平gcc變得更聰明。

也有可能gcc不保證其所有代碼中的指針對齊int,但這是不明智的,不太可能。

編譯器允許承擔的int*p因爲子句6.2.3.2廣告7的正確對準:

一個指向對象類型可被轉換成一個指針到一個不同的對象類型。如果 生成的指針未正確對齊68)的引用類型,則行爲爲 未定義。

注68)是關於正確對準的傳遞性。

+1

我用'-O2'和'-O3'編譯,所以它可能並不缺乏優化。問題可能是gcc沒有機器來推斷內建函數的指針對齊。 – 2013-04-25 07:52:19

2

在C編譯器中比在加載和存儲int值時更好的優化了一些東西,這些值在設計時是機器的自然尺寸。

寫功能

int q(int *p) { 
    return *p += 12; 
} 

這避免了兩個調用庫例程你,否則指望優化內聯,減少到一個簡單的加載和存儲,並表示修改整數的意圖-value參數就地並返回結果。使用memcpy來分配整數來模糊意圖。

如果這個問題是由於將一個較大的問題減少到最小的混淆例子的結果,那麼我的實現可能無法直接幫助。但即使p的類型是some_complex_struct *而不是int *,建議仍然適用。賦值運算符工作。在有意義的地方使用它優先於memcpy

1

這是我想出了一個解決方案,實現了幾個數據字段訪問的替代品:

// #define USE_MEMCPY 
// #define USE_PACKED 
#ifdef __cplusplus 
template <typename T> void SET(T *__attribute__((may_alias)) p, T val) { 
    *p=val; 
} 
template <typename T> T GET(T *__attribute__((may_alias)) p) { 
    return *p; 
} 
#else 
#ifdef USE_MEMCPY 
#include <string.h> 
#define _SET(p,val,line) \ 
    ({ typeof(val) _temp_##line = (val); \ 
     memcpy((void*)(p),(void*)&_temp_##line,sizeof(_temp_##line)); }) 
#define _GET(p,line) \ 
    ({ typeof(*(p)) _temp_##line; \ 
     memcpy((void*)&_temp_##line,(void*)(p),sizeof(_temp_##line)); \ 
     _temp_##line; }) 

#define SET(p,val) _SET(p,val,__LINE__) 
#define GET(p) _GET(p,__LINE__) 
#else /* no memcpy */ 
#ifdef USE_PACKED 
#define SET(p,val) (((struct { typeof(val) x __attribute__((packed)); } __attribute__((may_alias))*)p)->x=(val)) 
#define GET(p) (((struct { typeof(*p) x __attribute__((packed)); } __attribute__((may_alias))*)p)->x) 
#else 
#define SET(p,val) (*((typeof(val) __attribute__((may_alias))*)p)=(val)) 
#define GET(p) (*((typeof(*p) __attribute__((may_alias))*)p)) 
#endif 
#endif 
#endif 

然後,我可以寫這樣的功能:

int q(int *p) { 
    SET(p,GET(p)+12); 
    return p[0]; 
}