标题: Extern与指针的指针变量
threehair
荣誉斑竹
Rank: 14Rank: 14Rank: 14Rank: 14


UID 27
精华 78
积分 3034
帖子 5716
活跃指数 0
LU金币 2093 个
LU金条 0 个
阅读权限 200
注册 2003-9-17
 
发表于 2003-10-14 02:08  资料  个人空间  短消息  加为好友 
发帖者:alert7 讨论区:Unix Hacking推荐区
标题:[tips] extern与指针的指针变量
发信站:安全焦点(2003年9月8日13时02分48秒)
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 <stdio.h>
void * table[]={"0","1","2","3",NULL};

int test(void )
{

return 0;
}

[root@redhat73 root]# cat t.c
#include <stdio.h>
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 <main>:
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 <stdio.h>
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 <main>:
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的申明就需要注意了,
这时候指针的指针变量和指针数组就不能通用了。





╭⌒╮ ╭⌒╮╭⌒╮
╱◥███◣╭╭ ⌒╮
︱田︱田   田|
关门,上锁,钥匙已生锈。
世事静方见,人情淡始长!
顶部
99大话王 (gggg)
荣誉斑竹
Rank: 14Rank: 14Rank: 14Rank: 14
占③为王



LU爱心使者  
UID 260
精华 20
积分 925
帖子 1734
活跃指数 56
LU金币 4461 个
LU金条 4147 个
阅读权限 200
注册 2003-9-30
来自 未来世界
 
发表于 2003-10-14 13:09  资料  个人空间  短消息  加为好友 
[root@redhat73 root]# ./t
table 8049518
&table[1] 8048507 <-----这里显然不是我们想要的
table[1] (null) <-----这里更是出错了

没什么道理,研究研究去 rolleyes.gif





鸟枪换炮 换马甲鸟
顶部
无双
荣誉斑竹
Rank: 14Rank: 14Rank: 14Rank: 14
天才猪



UID 4
精华 84
积分 5863
帖子 11390
活跃指数 0
LU金币 4248 个
LU金条 0 个
阅读权限 200
注册 2003-9-16
来自 杭州
 
发表于 2003-10-14 13:42  资料  个人空间  主页 短消息  加为好友 
&table[1]
与table[1] 当然是不同的

另外因为是指针的指针
而且是void类型的

所以&table[1] = table + sizeof(void)





不要问我结果 我只研究过程与思路
无双客栈
顶部
99大话王 (gggg)
荣誉斑竹
Rank: 14Rank: 14Rank: 14Rank: 14
占③为王



LU爱心使者  
UID 260
精华 20
积分 925
帖子 1734
活跃指数 56
LU金币 4461 个
LU金条 4147 个
阅读权限 200
注册 2003-9-30
来自 未来世界
 
发表于 2003-10-15 00:56  资料  个人空间  短消息  加为好友 
QUOTE(无双 @ 2003-10-14 13:42:04)
&table[1]
与table[1] 当然是不同的

另外因为是指针的指针
而且是void类型的

所以&table[1] = table + sizeof(void)

sizeof(void)
???
这也能,没用过 sleep.gif





鸟枪换炮 换马甲鸟
顶部
threehair
荣誉斑竹
Rank: 14Rank: 14Rank: 14Rank: 14


UID 27
精华 78
积分 3034
帖子 5716
活跃指数 0
LU金币 2093 个
LU金条 0 个
阅读权限 200
注册 2003-9-17
 
发表于 2003-10-15 09:46  资料  个人空间  短消息  加为好友 
sizeof操作符不能用于函数类型,不完全类型或位字段。不完全类型指具有未知存储大小的数据类型,如未知存储大小的数组类型、未知内容的结构或联合类型、void类型.
但是由于这里是void类型指针,所以
&table[1] = table + sizeof(void *)





╭⌒╮ ╭⌒╮╭⌒╮
╱◥███◣╭╭ ⌒╮
︱田︱田   田|
关门,上锁,钥匙已生锈。
世事静方见,人情淡始长!
顶部
无双
荣誉斑竹
Rank: 14Rank: 14Rank: 14Rank: 14
天才猪



UID 4
精华 84
积分 5863
帖子 11390
活跃指数 0
LU金币 4248 个
LU金条 0 个
阅读权限 200
注册 2003-9-16
来自 杭州
 
发表于 2003-10-15 13:23  资料  个人空间  主页 短消息  加为好友 
谢谢三毛

我这个没有认真研究 angry.gif





不要问我结果 我只研究过程与思路
无双客栈
顶部
[广告] 记录自己的思想火花,留住每日的技术积累,尽在拥有属于自己独立域名的博客。
threehair
荣誉斑竹
Rank: 14Rank: 14Rank: 14Rank: 14


UID 27
精华 78
积分 3034
帖子 5716
活跃指数 0
LU金币 2093 个
LU金条 0 个
阅读权限 200
注册 2003-9-17
 
发表于 2003-10-15 15:49  资料  个人空间  短消息  加为好友 
不客气 huh.gif





╭⌒╮ ╭⌒╮╭⌒╮
╱◥███◣╭╭ ⌒╮
︱田︱田   田|
关门,上锁,钥匙已生锈。
世事静方见,人情淡始长!
顶部
[广告] 记录自己的思想火花,留住每日的技术积累,尽在拥有属于自己独立域名的博客。
li2002
LU新生
Rank: 1



UID 405
精华 0
积分 11
帖子 21
活跃指数 0
LU金币 2006 个
LU金条 0 个
阅读权限 10
注册 2003-10-10
 
发表于 2003-10-28 15:44  资料  个人空间  短消息  加为好友 
真的汗啊,平时这样混用的情况很多,好像没碰到这样的情况,怎么会编译出的效果不同呢??理论上应该一样的,想不通!!
还有平时声明main()时标准的方法应该用
main(int argc,char **argv);
还是
main(int argc,char *argv[]);
平时这两种声明都没遇到这样的问题。
无双能不能解答一下:这二者到底有什么区别?还是我们的理论知识不对??还是仅限于extern的情况?? wub.gif

顶部
[广告] 记录自己的思想火花,留住每日的技术积累,尽在拥有属于自己独立域名的博客。
无双
荣誉斑竹
Rank: 14Rank: 14Rank: 14Rank: 14
天才猪



UID 4
精华 84
积分 5863
帖子 11390
活跃指数 0
LU金币 4248 个
LU金条 0 个
阅读权限 200
注册 2003-9-16
来自 杭州
 
发表于 2003-10-29 09:16  资料  个人空间  主页 短消息  加为好友 
使用[]时同时也说明了指针++时应该前进的步数

如果是指针的指针 那么通过extern声明时就出现步数不对

CODE

#include <stdio.h>
int * table_tttt[]={"0","1","2","3",NULL};

int test(void )
{
printf("table %x\n",&table_tttt);
printf("&table[1] %x\n",&table_tttt[1]);

return 0;
}

CODE


#include <stdio.h>
int main(int argc, char *argv[])
{
extern int **table_tttt;
extern int test();
int t;
printf("table %x\n",&table_tttt);
printf("&table[1] %x\n",&table_tttt[1]);
test();
printf("table[1] %s\n",table_tttt[1]);

return 0;
}


比较上面两次打印出来的结果
CODE

table 424a30
&table[1] 422058
table 424a30
&table[1] 424a34

结果发现在main中
差是29D8
而test中是4 ,指针是int类型,所以4是正确的 而29D8显然不对
我想这可能就是出错原因了吧

另外gcc下没有试过
我在vc下测试的 GCC测试结果出来了再说明一下





不要问我结果 我只研究过程与思路
无双客栈
顶部
[广告] 记录自己的思想火花,留住每日的技术积累,尽在拥有属于自己独立域名的博客。
 



当前时区 GMT+8, 现在时间是 2008-12-5 08:29
乐悠LoveUnix论坛-京ICP备05005823号

Thanks to Discuz!  © 2001-2007    Power by LoveUnix.net
Processed in 0.055632 second(s), 6 queries , Gzip enabled

清除 Cookies - 联系我们 - 乐悠LoveUnix - Archiver