linux gcc编译extern变量 用gdb调试的问题

我在编程的时候遇到一个问题,希望和大家讨论一下

代码如下:
::::::::::::::
test.c
::::::::::::::
#include <stdio.h>

//extern char ip[20];
extern char *ip;
int main()
{
strcpy(ip,"192.168.0.102");
printf("%s/n",ip);
return 0;
}
::::::::::::::
a.c
::::::::::::::
char ip[20];
int a()
{
return 0;
}

编译:
$gcc -g test.c a.c
执行
$./a.out
Segmentation fault (core dumped)
$nm a.out|grep ip
08049660 B ip
调试发现,test.c中strcpy中的参数ip 是空指针,因此出现堆栈错误。
当把test.c中的extern char *ip改成extern char ip[20]
然后编译之后,程序运行没有问题
$nm a.out|grep ip
08049660 B ip

因此,再ip说明成外部指针的时候,ip指向的地址并非a.c中定义的ip字符数组
的地址,而通过gdb调试,地址空间08049660仍然是a.c中定义的字符数组,
那么test.c中的ip是在什么地方定义的呢?为什么连接程序没有将test.c中的
extern char *ip指向a.c中的char ip[20]呢? 

//

guoyichao
(addict)
02-09-20 14:33
[精华] Re: 关于全局变量的一个现象 新 [re: BoatTracker] 

老问题了,编译器对数组和指针的处理是不同的,对数组是直接对其地址的值进行操作,对指针是引用地址,先申明指针,最后却是数组那么编译器产生的代码就会出错,反之也一样。

文章选项: 打印

wandys版主
(addict)
02-09-20 14:34
[精华] Re: Please read the [FAQ] of this board 新 [re: BoatTracker] 

Title--"为何char *str会引起core dump?"


--
Copyleft(C) 2002 wandys
All Wrongs Reserved

文章选项: 打印

BoatTracker
(member)
02-09-20 15:06
[精华] Re: Please read the [FAQ] of this board 新 [re: wandys] 

我问的问题是,当在test.c中声明char *ip的时候,
程序执行之后,ip是空指针,而不是指向a.c中ip指向的地址,
这是什么原因造成的?

文章选项: 打印

kgon
(member)
02-09-20 15:42
[精华] Re: 关于全局变量的一个现象 新 [re: BoatTracker] 

char *ip;
strcpy(ip,“123213“);
汇编代码是:
pushl ip
call strcpy


改成
char ip[20];
strcpy(&ip,"123232"),;
汇编代码是:
pushl $ip
call strcpy
看来就是编译器对待指针和数组不同,谢谢提醒!

文章选项: 打印

kgon
(member)
02-09-20 15:45
[精华] Re: Please read the [FAQ] of this board 新 [re: BoatTracker] 

从上面的汇编代码可以看到,当ip是指针的时候,把ip指向的值压入堆栈,而是数组的时候,把ip的值压到堆栈

文章选项: 打印

coldguy
(member)
02-09-20 16:03
[精华] Re: Please read the [FAQ] of this board 新 [re: kgon] 

你写的者两端是等价的吗?我看未必吧。前面的ip和后面的&ip不是一回事。

To Be or Not to Be?

文章选项: 打印

kgon
(member)
02-09-20 16:16
[精华] Re: Please read the [FAQ] of this board 新 [re: coldguy] 

我什么时候说等价了,我列出来是为了说明区别!

文章选项: 打印

coldguy
(member)
02-09-20 16:19
[精华] 那有什么可比的?(内空) 新 [re: kgon] 



To Be or Not to Be?

文章选项: 打印

kgon
(member)
02-09-20 16:33
[精华] Re: 那有什么可比的?(内空) 新 [re: coldguy] 

为什么不能比?难道别人说是编译器处理的不同你就赶忙把指针变成数组啦?至少也该知道有什么不同吧

文章选项: 打印

wandys版主
(addict)
02-09-20 17:14
[精华] Re: note the difference between pointer and array. 新 [re: BoatTracker] 

sorry, 原来没看清你的意思.
这个的确与FAQ中的core dump不同, 关键在于数组和指针的区别.

数组直接在栈中分配内存, 如char a[20]; a的值就是数组在栈中起始的地址. a*本身*是个常量, a*本身*不占内存空间.
而指针本身是个变量, 它*本身*就需要占用内存空间.
如char *p; 它本身在栈中要占内存空间, 系统需要访问内存&p来得到p的值.

反应到汇编上就是
$foo vs. foo
leal vs. movl



回到你的问题, 你将ip定义为数组(以数组的明义为它分配了内存), 而又在main.c中声明它为指针.
那么strcpy(ip, "XXXX")时, 系统要到&ip去取ip的值, 它的值就是ip[0](等于0). 所以产生core dump.


--
Copyleft(C) 2002 wandys
All Wrongs Reserved

文章选项: 打印

kgon
(member)
02-09-20 17:17
[精华] Re: note the difference between pointer and array. 新 [re: wandys] 

但当指针申明为extern的时候也不占内存啊.
我觉得这只是编译器的行为而已,跟c的语法无关.

文章选项: 打印

wandys版主
(addict)
02-09-20 17:27
[精华] Re: 是的, extern不占内存, but 新 [re: kgon] 

内存是在char ip[20]的时候就分配好了,
extern又将它声明为指针.
所有有关ip的操作会按指针的方式来操作.

数组: ip == &ip
指针: ip == *(&ip) != &ip


这是C本身的东西, 编译器当然要完全遵循C的语法(定义).
数组和指针对编译器*必须*是不同的.

--
Copyleft(C) 2002 wandys
All Wrongs Reserved

文章选项: 打印

coldguy
(member)
02-09-20 17:55
[精华] Re: 是的, extern不占内存, but 新 [re: wandys] 

还是斑竹说的对,我就不明白
char ip[20];
strcpy(&ip,"123232");
是个什么意思,
看起来既无实际意义,恐怕连编译都通不过。

To Be or Not to Be?

文章选项: 打印

ddd
(enthusiast)
02-09-20 18:22
[精华] Re: note the difference between pointer and array. 新 [re: kgon] 

这确实应该是个编译器的bug,和
char a[20];
char* b=a;
情况又不一样,碰到这种情况应该直接做error处理。禁止链接通过。
要么就分配一个空间指向数组首地址。

文章选项: 打印

kgon
(member)
02-09-20 18:26
[精华] Re: 是的, extern不占内存, but 新 [re: wandys] 

斑竹的说法我完全同意。但语法书上虽然那样说,但我觉得还是不能讲清楚指针和数组的区别(至少本例我觉得就不是语法书讲的清楚的),他们的本质区别我觉得要看目标文件的symbol table和连接原理才能了解

以上是个人理解的方式而已!

文章选项: 打印

kgon
(member)
02-09-20 18:27
[精华] Re: note the difference between pointer and array. 新 [re: ddd] 

我不认为是bug,我只是觉得他们的区别不是语法书讲的清楚的

文章选项: 打印

sdjiangwei
(Pooh-Bah)
02-09-20 18:34
[精华] Re: note the difference between pointer and array. 新 [re: ddd] 

同意

日知其所亡

文章选项: 打印

wandys版主
(addict)
02-09-20 19:24
[精华] Re: Not the bug of the compiler 新 [re: ddd] 

这绝对不是编译器的bug, 如果它不这样才叫bug.


关键是代码有问题, 先将ip定义成数组, 又将它声明为指针, 当然会引起错误. 编译器没错.


>char a[20];
>char *b=a;
>情况又不一样

当然不一样. 这种情况, 系统会为b分配内存, 地址&b的内容就是a.
指针也是变量, 不论它是int foo, 还是int *foo, 要得到foo的值, 都要先访问内存&foo.


先定义char a[20], 然后再将a声明为 char *a 和
char a[20]; char *b=a 是不同的.

--
Copyleft(C) 2002 wandys
All Wrongs Reserved

文章选项: 打印

bx_bird
(enthusiast)
02-09-20 19:28
[精华] Re: note the difference between pointer and array. 新 [re: sdjiangwei] 

斑竹说的对 编译器没问题
或许这样看比较好理解:
test.c
::::::::::::::
#include <stdio.h>

//extern char ip[20];
extern char ip;
int main()
{
strcpy((char*)ip,"192.168.0.102");
printf("%s/n",ip);
return 0;
}


--------笨又不是我的錯

文章选项: 打印

sdjiangwei
(Pooh-Bah)
02-09-20 19:30
[精华] 你误会了 新 [re: wandys] 

ddd和你说的不冲突。

编译器应该在连接的时候出错,而不是编译的时候。


日知其所亡

文章选项: 打印

wandys版主
(addict)
02-09-20 19:40
[精华] Re: agree, we can't get everything from textbook. 新 [re: kgon] 

指针是C最强大, 同时也是最confusing的地方.

指针和数组的区别(或者编程中的其它方面)不是从某一个程序就能完全弄清的.
textbook(基础理论)必须, practice(动手实践)必须, thinking(用心思考)也是必须的.

--
Copyleft(C) 2002 wandys
All Wrongs Reserved

文章选项: 打印

uchinaboy
(old hand)
02-09-20 21:51
[精华] Re: 关于全局变量的一个现象 新 [re: BoatTracker] 

ip="192.168.0.102";

我是浮躁的

文章选项: 打印

grip2
(journeyman)
02-09-21 09:38
[精华] Re: agree, we can't get everything from textbook. 新 [re: wandys] 

同意,应该是编码的问题,与编译器无关,C是灵活的,所以需要使用者有更好的驾驭能力。

THE TRUTH IS OUT THERE

文章选项: 打印

wandys版主
(addict)
02-09-21 10:54
[精华] Re: IMO, it's either not the bug of the linker 新 [re: sdjiangwei] 

在一个文件中定义为数组, 在另一个文件中又声明为指针, compiler是"看"不出来的, 但这是linker的责任么?

linker主要的报错是有没有"undifined symbol",和"defined symbol"有没有被重新定义. 类型检查是linker要做的么?


这里的问题是, 类型检查是compiler的事, 而compiler又"看"不到, then what to do?

that's easy.

1. 你要确定代码无误(等于没说??)
2. 具体解决办法: 将全局变量的声明写入一个header, 在所有用到(包括定义)这个变量的c文件中都include这个header,
这样如果类型不一致, _compiler_会发现.


--
Copyleft(C) 2002 wandys
All Wrongs Reserved

文章选项: 打印

sdjiangwei
(Pooh-Bah)
02-09-21 15:34
[精华] Re: IMO, it's either not the bug of the linker 新 [re: wandys] 

我认为。此时应报“"undifined symbol”

日知其所亡

文章选项: 打印

wandys版主
(addict)
02-09-21 16:41
[精华] Re: about linker's global symbols 新 [re: sdjiangwei] 

这里ip已经在char ip[20]时"定义"了,
(注:C语言中对全局变量的"定义"与linker对global symbol的"defnition"理解不同, 详见下)
因此不会出现"undefined reference".
(sorry, 上贴中将"undefined reference"说成了"undefined symbol")


[Copied from info ld]
------------------------------------------------------------


There are three kinds of global symbols, illustrated here by C examples:

`int i = 1;'
A definition, which goes in the initialized data section of
the output file.

`extern int i;'
An undefined reference, which does not allocate space. There
must be either a definition or a common symbol for the
variable somewhere.

`int i;'
A common symbol. If there are only (one or more) common
symbols for a variable, it goes in the uninitialized data
area of the output file. The linker merges multiple common
symbols for the same variable into a single symbol. If they
are of different sizes, it picks the largest size. The
linker turns a common symbol into a declaration, if there is
a definition of the same variable.

-----------------------------------------------------------

1. 如果*只*有上面说的第二种symbol("undefined reference"), 那linker会报错"undefined reference".
2. 如果有多于一个的"definition"(上面说的第一种symbol), 那linker会报错"multiple definition of 'XXX'".
3. 对其它情况, it's all okay.


PS. 对compiler和linker的讨论已超出了原题;
IMHO, 做为应用层的编程, 除非你要学习具体的编译器和连接器的开发,
我觉得无需深入compiler和linker的how & why, 只知道它们的what就好了.
同样的, 除非是做kernel级的开发, 对一般的应用编程来讲, 我们没必要深入kernel的how & why.

对原题来说, compiler无错, linker无错, 错的是代码本身, 至于原因和解决办法, 我在前面做出了解释.

--
Copyleft(C) 2002 wandys
All Wrongs Reserved

文章选项: 打印

ddd
(enthusiast)
02-09-21 17:45
[精华] Re: about linker's global symbols 新 [re: wandys] 

数组指针在编译以后应该就是一个常数,联接的时候将一个常数和一个变量弄到一起不可理解,尽管这个常数和变量名字一样。

文章选项: 打印

sdjiangwei
(Pooh-Bah)
02-09-21 17:51
[精华] 请指教: 新 [re: wandys] 

如果在一个文件里这样声明:

extent int a;

在另一个文件里声明:

char a;

连接的时候会出现什么问题?

日知其所亡

文章选项: 打印

wandys版主
(addict)
02-09-21 18:18
[精华] Re: through linker smoothly 新 [re: sdjiangwei] 

"指教"可不敢当, 大家一起讨论就好. :-)

这对linker来说没什么错误.

原因,上面的两个贴子都提到了:
1. 类型检查和linker无关
2. linker的错误主要有"undefined reference"和"multiple definition".

就说个*具体*的例子吧: 只有两个文件

1)在一个文件中声明:
extern char a;
在另一个文件中声明:
extern char a; (或extern int a, 或extern floute a, etc.)

linker会报错: "undefined reference".

2)在一个文件中定义:
char a = 'x';
在另一个文件中又定义:
char a = 'x'; (或char a = 'y', 或int a = 1, etc.)
linker报错: "multiple definition of 'a'".

3)其它情况都能通过linker

--
Copyleft(C) 2002 wandys
All Wrongs Reserved

文章选项: 打印

ddd
(enthusiast)
02-09-21 21:06
[精华] Re: through linker smoothly 新 [re: wandys] 

对于gcc确实这样(据说联接器是ld),但vc不是这样,对于extern int aa;另外一个文件里char aa;联接错误。
所以只能说是gcc/ld就是这样的一个行为(联接的时候不检查类型,名字一样就连,全是变量还好理解,可以认为是节省一个指针转换的过程,一个常量一个变量恐怕就有问题了),这个行为不代表所有的联接器,但这个行为应该来说非常不合理。

文章选项: 打印

wandys版主
(addict)
02-09-21 23:49
[精华] Re: yes, i've been talking abou gcc/ld, but 新 [re: ddd] 

类型检查本不该linker的事; 别说linker了, 就是在assembler一级都不涉及类型检查.

类型检查是在compiler一级完成的, linker主要处理的是symbol.
linker不知道也没必要知道它所处理的symbol在编译时是int, char *, 还是int [].


Q1. 那M$下的VC为何会报错呢?

A1. 那是因为做为商业产品, VC从compiler, 到assembler, 到linker 都tie得很紧.

VC报错, that's okay.


Q2. 那UN*X/LINUX下的gcc/ld为何就不报错呢?

A1. 那是因为它本就不该报错. UN*X的philosophy: Write programs that do one thing and do it well.
从源码到可执行程序, 其间要经过很多步骤: preprocessor --> compiler --> assembler --> linker
不同的步骤用不同的工具来完成, 彼此协作, 但不越界.
如果你用gcc -v 选项你就会发现这些步骤的(linux/FreeBSD下一般用到了cpp, gcc, GAS和GNU ld)
但gcc不要求你必须用GAS或者GNU ld.

UN*X下不报错, that's all right.


Q3. 一会报错, 一会又不报错, 那我该怎么办?

A3. 规范你的代码. :-)
将全局变量的声明写入一个header, 在任何使用(包括定义)这个变量的c文件中都include这个header.
这样无论你用什么编译器, 如果类型不一, 那么在*编译*阶段就会报错的.

--
Copyleft(C) 2002 wandys
All Wrongs Reserved

文章选项: 打印

ddd
(enthusiast)
02-09-22 18:33
[精华] Re: yes, i've been talking abou gcc/ld, but 新 [re: wandys] 

汇编自然没什么类型检查了,全是字节、字之类的直接内存操作。
>>: Write programs that do one thing and do it well.
这个可能也是特点之一
就是这个可能导致了这种三不管、踢皮球的事情出现。
顺便说一句,vc中对char a[23]; extern char* a也不报错,即使用vc的这里也得小心点。

文章选项: 打印

sdjiangwei
(Pooh-Bah)
02-09-23 20:16
[精华] Re: yes, i've been talking abou gcc/ld, but 新 [re: wandys] 

对,联接器确实不应该进行类型检查,只有这样,才可能将不同语言编译的目标文件连接到一起.(因为不同的语言有不同的数据类型) (即连接器不是专为某种特定语言工作的)

日知其所亡

文章选项: 打印

cold_tang
(stranger)
04-09-07 14:29
[精华] 我对这个问题的理解 新 [re: guoyichao] 

对于全局变量,一般使用方法是
1)首先使用该全局变量的文件直接定义:如 int giTemp;
2)然后可能要使用该变量的文件用extern:即 extern int giTemp;
在上例中main()中定义的仅仅是一指针(数据段,长度跟系统相关),而
实际想使用其指向的区域,由于该区域还没有分配,导致core dump。

我想,如果在main中定义数组,而在子程序中用指针,并在子程序中使用
sprintf,可能旧没错误了!如下:
--main.c--
char p[20];
main() {
abc();
}
--abc.c--
extern char *p;
abc() {
sprintf(p,"10.167.18.1");
}

文章选项: 打印

lblaze
(stranger)
04-09-07 15:58
[精华] Re: 从内存所占空间看指针和数组的区别 新 [re: cold_tang] 

厉害,透彻。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值