c extern 问题

在一个源文件中定义 int a[5]={1,2,3,4,5}; int *b=a;
在另外一个文件中写一下代码 extern int *a; extern int b[];
                                                      printf("%d",a);
打印出的值不是一个地址 ,而是a[0]的值
一般我们使用extern的时候像下面这样肯定就没问题
int a
extern int a

现在是
int a[]
extern int *a
会不会编译器就理解为你引用的是int *a[]这个玩意,所以就会导致现在的结果
求大侠解释
对于所有的非局部变量, 如全局变量, 都需要链接的时候重定位,原因很简单, 在c 语言转换成代码的时候, 对于这个全局变量不知道要去访问那个地址,一个简单的例子如下
int abcd[10];
int main()
{
    int a =0;
    a = abcd[0];
}
对于这样的c 代码, 里面a = abcd[0] 要访问全局变量,就是内存中的一个地方,在编译成d.o的时候, 无法得知abcd 的地址, 因为这时候不知道其他文件中还有多少全局变量,函数,一下是编译出来的d.o 的反汇编代码
0:   55                      push   %ebp
   1:   89 e5                   mov    %esp,%ebp
   3:   83 ec 10                sub    $0x10,%esp
   6:   c7 45 fc 00 00 00 00    movl   $0x0,-0x4(%ebp)
   d:   a1 00 00 00 00          mov    0x0,%eax
                        e: R_386_32     abcd
  12:   89 45 fc                mov    %eax,-0x4(%ebp)
  15:   c9                      leave
  16:   c3                      ret
在d 行, 访问了内存地址为0 的地方, 还有一个标记, 这里是类型为 R_386_32的重定位,在链接之后, 确定了有多少全局变量以后会把这里的地址修正过来,
08048394 <main>:
8048394:       55                      push   %ebp
8048395:       89 e5                   mov    %esp,%ebp
8048397:       83 ec 10                sub    $0x10,%esp
804839a:       c7 45 fc 00 00 00 00    movl   $0x0,-0x4(%ebp)
80483a1:       a1 40 96 04 08          mov    0x8049640,%eax
80483a6:       89 45 fc                mov    %eax,-0x4(%ebp)
80483a9:       c9                      leave
80483aa:       c3                      ret
80483ab:       90                      nop
看上面的8048a31 和前面对比 所有代码都一样,就是这里不一样, 他在访问8049640 这个内存地址,可以猜想 这里就是abcd 数组的地址, 可以验证一样, 用readelf 可以查看一个执行文件的符号表
这里只是一种一部分
58: 0804960c     0 NOTYPE  GLOBAL DEFAULT  ABS __bss_start
    59: 08049668     0 NOTYPE  GLOBAL DEFAULT  ABS _end
    60: 08049640    40 OBJECT  GLOBAL DEFAULT   25 abcd
    61: 0804960c     0 NOTYPE  GLOBAL DEFAULT  ABS _edata
    62: 08048415     0 FUNC    GLOBAL HIDDEN    13 __i686.get_pc_thunk.bx
    63: 08048394    23 FUNC    GLOBAL DEFAULT   13 main
看 abcd 的地址就是8049640, 在上面讲了这么多,
在一个文件里面声明一个数组,另外一个文件声明指针, 在链接的时候 会为数组安排地址,
但是
extern int *a
printf("%d",a);

这样的代码 因为a 是一个变量, 需要a 的值, 就留下一个重定位
00000000 <main>:
   0:   55                      push   %ebp
   1:   89 e5                   mov    %esp,%ebp
   3:   83 e4 f0                and    $0xfffffff0,%esp
   6:   83 ec 10                sub    $0x10,%esp
   9:   a1 00 00 00 00          mov    0x0,%eax
                        a: R_386_32     a
在链接完之后会把a 的地址让这里放, 所以就会读到a 数组的第一个元素,
正确的写法应该是extern int a[];
因为声明的是一个数组
00000000 <main>:
   0:   55                      push   %ebp
   1:   89 e5                   mov    %esp,%ebp
   3:   83 e4 f0                and    $0xfffffff0,%esp
   6:   83 ec 10                sub    $0x10,%esp
   9:   c7 44 24 04 00 00 00    movl   $0x0,0x4(%esp)
  10:   00
                        d: R_386_32     a
a 的含义是数组的地址, 你需要的也是a 的地址, 所以这里第9 行的 汇编代码也是需要重定位的, 不同的是前面有个美元符号
就是立即数的意思, 不是访问内存, 在重定位之后 直接把这个地址传给printf 了,
这就是2个的不同点,
c 语言的一句话, 类型决定operation
a[] = a [0] = a ,应该就是这样了,注意是地址哈

posted on 2010-12-16 18:36  MorningChen  阅读(281)  评论(0编辑  收藏  举报

导航