2011-04-05 62 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命令,并且您有别名。 :)

相关问题