2011-04-05 99 views
2

C編譯器能否假定兩個不同的外部全局變量不能被別名到相同的地址?具有相同地址的C extern?

在我的情況,我有這樣的情況:

 
extern int array_of_int[], array_end; 

void some_func(void) 
{ 
    int *t; 
    for (t = &array_of_int[0]; t != &array_end; t++) 
    { 
    ... 

生成的二進制與優化編譯在進入循環之前沒有測試

t != &array_end
條件。編譯器的優化是循環必須至少執行一次,因爲 t一開始不能立即等於 &array_end

當然我們發現這很難。顯然,一些帶鏈接器部分的彙編器hackery導致兩個extern是相同地址的情況。

感謝您的任何建議!

+0

啊哈,只有_now_我明白了。代碼試圖依賴包含array_of_int [] ...的對象文件的數據段中的數據佈局,這是_is_髒。我發佈了規範版本... – sehe 2011-04-05 22:43:01

回答

6

總之,是的,它可以自由地做出這個假設。 extern變量沒有什麼特別之處。兩個變量可能不是別人的別名。 (如果答案有任何不同,請考慮隨後發生的混亂,extern int a, b可能會互相混淆,這會使得使用這些變量的任何代碼的語義完全瘋狂!)

實際上,您正在依靠未定義行爲在這裏,句號。以這種方式比較無關變量的地址是無效的。

+0

是的,這是未定義的行爲。這是相當微妙的發現,所以[我發佈了'固定'版本(http://stackoverflow.com/questions/5559281/c-externs-that-alias-the-same-address/5559351#5559351),這是很可能是@srking打算的內容 – sehe 2011-04-05 22:40:04

+0

不等式比較不是未定義的,而是落在數組的末尾(因爲'array_end'不能具有與其任何元素相同的地址)。如果循環體中的某些內容確保它在這種情況發生之前總是中斷,那麼此代碼將是合法且可移植的。 – 2011-04-05 22:47:35

+0

@Steve:你是對的。我一直在誤解標準,直到現在! – 2011-04-05 22:55:18

0

我覺得這裏是在另一個編譯單元的固定碼

#include <stdio.h> 

extern int array_of_int[]; 
extern int *array_end; 


int main() 
{ 
    int *t; 
    for (t = &array_of_int[0]; t != array_end; t++) 
    { 
     printf("%i\n", *t); 
    } 
    return 0; 
} 

int array_of_int[] = { }; // { 1,2,3,4 }; 
int *array_end = array_of_int + (sizeof(array_of_int)/sizeof(array_of_int[0])); 

它編譯成這個(-O3,GCC 4.4.5的i686)

080483f0 <main>: 
80483f0:  55      push %ebp 
80483f1:  89 e5     mov %esp,%ebp 
80483f3:  83 e4 f0    and $0xfffffff0,%esp 
80483f6:  53      push %ebx 
80483f7:  83 ec 1c    sub $0x1c,%esp 
80483fa:  81 3d 24 a0 04 08 14 cmpl $0x804a014,0x804a024 
8048401:  a0 04 08 
8048404:  74 2f     je  8048435 <main+0x45> 
8048406:  bb 14 a0 04 08   mov $0x804a014,%ebx 
804840b:  90      nop 
804840c:  8d 74 26 00    lea 0x0(%esi,%eiz,1),%esi 
8048410:  8b 03     mov (%ebx),%eax 
8048412:  83 c3 04    add $0x4,%ebx 
8048415:  c7 44 24 04 00 85 04 movl $0x8048500,0x4(%esp) 
804841c:  08 
804841d:  c7 04 24 01 00 00 00 movl $0x1,(%esp) 
8048424:  89 44 24 08    mov %eax,0x8(%esp) 
8048428:  e8 d7 fe ff ff   call 8048304 <[email protected]> 
804842d:  39 1d 24 a0 04 08  cmp %ebx,0x804a024 
8048433:  75 db     jne 8048410 <main+0x20> 
8048435:  83 c4 1c    add $0x1c,%esp 
8048438:  31 c0     xor %eax,%eax 
804843a:  5b      pop %ebx 
804843b:  89 ec     mov %ebp,%esp 
804843d:  5d      pop %ebp 
804843e:  c3      ret  
804843f:  90      nop 
+0

沒錯,但這不是OP正在描述/詢問的內容。 – 2011-04-05 22:38:17

+0

點了,在循環條件發生了另一個奇怪的事情,我張貼我的修復代碼 – sehe 2011-04-05 22:38:48

1

C99在6.2.2「標識符的長度」中說:

在不同 作用域或在多於一次 可以使由一個過程來指代相同的 對象或功能的相同範圍聲明的標識符稱爲 聯動。 (腳註21)

...

腳註21:有不同的標識符之間沒有聯繫 。

因此,不幸的是,這種有點常見的彙編語言技巧(我用過......)沒有很好的定義。你最好讓你的彙編模塊定義array_end作爲asm代碼加載數組結尾地址的實際指針。這樣C代碼可以很好地定義,因爲array_end指針將是一個單獨的對象。

+0

我不知道業主將如何有興趣修復此代碼,但我至少可以讓他們一些鈍的力量創傷與腳註21.乾杯。 – srking 2011-04-06 00:13:14

+0

優秀的解釋;總是很高興看到他們的胳膊下的規格的軍隊:) – sehe 2011-04-06 00:13:33

0

它的情況下,如果我們在ARM代碼中做到這一點很簡單 - 我們有一個屬性,它..

#include <stdio.h> 
int oldname = 1; 
extern int newname __attribute__((alias("oldname"))); // declaration 
void foo(void) 
{ 
    printf("newname = %d\n", newname); // prints 1 
} 

只有EXTERN就夠了這裏。 將其導入其他文件 - 其無縫。用於彙編文件的 - 您可以使用IMPORT命令,並且您有別名。 :)

相關問題