extern与指针的指针变量 by alert7 gcc的一点小经验,可能您已经知道,比较肤浅,但是不知道的可能会郁闷好几个小时。 一直以来,我们都认为char *argv[]与char **argv是等价的。而且这样 也一直没有出错。所以侥幸的认为char *argv[]与char **argv是等价的,原型 不一样,但至少是通用的。 但是,昨天的一个BUG调试了一天,郁闷绝望之余结果发现是定义指针的指针变量 惹的祸,按照原型,应该定义成指针数组。问题解决,现把经验写成本文,以使来者少 走一些弯路。 以下的例子是我抽象出来的,table被定义成指针数组,在实际现实中o.c可能是第三 方提供的软件,没有源代码,你可能草草的把table定义为extern void ** ; [root@redhat73 root]# cat o.c #include void * table[]={"0","1","2","3",NULL}; int test(void ) { return 0; } [root@redhat73 root]# cat t.c #include int main(int argc, char *argv[]) { extern void **table; int t; printf("table %x\n",&table); printf("&table[1] %x\n",&table[1]); printf("table[1] %s\n",table[1]); return 0; } [root@redhat73 root]# gcc -c t.c [root@redhat73 root]# gcc -o t t.o o.o [root@redhat73 root]# ./t table 8049518 &table[1] 8048507 <-----这里显然不是我们想要的 table[1] (null) <-----这里更是出错了 我们来看下汇编代码 08048400
: 8048400: 55 push %ebp 8048401: 89 e5 mov %esp,%ebp 8048403: 83 ec 08 sub $0x8,%esp 8048406: 83 ec 08 sub $0x8,%esp 8048409: 68 18 95 04 08 push $0x8049518 <------table地址 804840e: 68 d8 84 04 08 push $0x80484d8 8048413: e8 d8 fe ff ff call 80482f0 <_init+0x58> 8048418: 83 c4 10 add $0x10,%esp 804841b: 83 ec 08 sub $0x8,%esp 804841e: a1 18 95 04 08 mov 0x8049518,%eax <-----把0x8049518地址中的值放到eax中 8048423: 83 c0 04 add $0x4,%eax <-----再把这个值加4,这显然与我们的本意有出入 8048426: 50 push %eax 8048427: 68 e2 84 04 08 push $0x80484e2 804842c: e8 bf fe ff ff call 80482f0 <_init+0x58> 8048431: 83 c4 10 add $0x10,%esp 8048434: 83 ec 08 sub $0x8,%esp 8048437: a1 18 95 04 08 mov 0x8049518,%eax 804843c: 83 c0 04 add $0x4,%eax 804843f: ff 30 pushl (%eax) 8048441: 68 f0 84 04 08 push $0x80484f0 8048446: e8 a5 fe ff ff call 80482f0 <_init+0x58> 804844b: 83 c4 10 add $0x10,%esp 804844e: b8 00 00 00 00 mov $0x0,%eax 8048453: c9 leave 8048454: c3 ret 再来看下按照原型定义的extern void *table[]; #include int main(int argc, char *argv[]) { extern void *table[]; int t; printf("table %x\n",&table); printf("&table[1] %x\n",&table[1]); printf("table[1] %s\n",table[1]); return 0; } [root@redhat73 root]# gcc -c t.c [root@redhat73 root]# gcc -o t t.o o.o [root@redhat73 root]# ./t table 8049508 &table[1] 804950c table[1] 1 08048400
: 8048400: 55 push %ebp 8048401: 89 e5 mov %esp,%ebp 8048403: 83 ec 08 sub $0x8,%esp 8048406: 83 ec 08 sub $0x8,%esp 8048409: 68 08 95 04 08 push $0x8049508 804840e: 68 c8 84 04 08 push $0x80484c8 8048413: e8 d8 fe ff ff call 80482f0 <_init+0x58> 8048418: 83 c4 10 add $0x10,%esp 804841b: 83 ec 08 sub $0x8,%esp 804841e: 68 0c 95 04 08 push $0x804950c <--------这里就对了 8048423: 68 d2 84 04 08 push $0x80484d2 8048428: e8 c3 fe ff ff call 80482f0 <_init+0x58> 804842d: 83 c4 10 add $0x10,%esp 8048430: 83 ec 08 sub $0x8,%esp 8048433: ff 35 0c 95 04 08 pushl 0x804950c 8048439: 68 e0 84 04 08 push $0x80484e0 804843e: e8 ad fe ff ff call 80482f0 <_init+0x58> 8048443: 83 c4 10 add $0x10,%esp 8048446: b8 00 00 00 00 mov $0x0,%eax 804844b: c9 leave 804844c: c3 ret 所以,我们还是要严格按照函数的原型来定义。 经过一些实验,指针的指针变量经过extern的申明就需要注意了, 这时候指针的指针变量和指针数组就不能通用了。