guoyichao (addict) 02-09-20 14:33
|
|
老问题了,编译器对数组和指针的处理是不同的,对数组是直接对其地址的值进行操作,对指针是引用地址,先申明指针,最后却是数组那么编译器产生的代码就会出错,反之也一样。 文章选项: |
wandys![版主](http://www.linuxforum.net/forum/images/mod.gif) (addict) 02-09-20 14:34
|
|
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
|
|
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
|
|
从上面的汇编代码可以看到,当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
|
|
To Be or Not to Be? 文章选项: |
kgon (member) 02-09-20 16:33
|
|
为什么不能比?难道别人说是编译器处理的不同你就赶忙把指针变成数组啦?至少也该知道有什么不同吧 文章选项: |
wandys![版主](http://www.linuxforum.net/forum/images/mod.gif) (addict) 02-09-20 17:14
|
|
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![版主](http://www.linuxforum.net/forum/images/mod.gif) (addict) 02-09-20 17:27
|
|
内存是在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
|
|
还是斑竹说的对,我就不明白 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
|
|
斑竹的说法我完全同意。但语法书上虽然那样说,但我觉得还是不能讲清楚指针和数组的区别(至少本例我觉得就不是语法书讲的清楚的),他们的本质区别我觉得要看目标文件的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![版主](http://www.linuxforum.net/forum/images/mod.gif) (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
|
|
ddd和你说的不冲突。
编译器应该在连接的时候出错,而不是编译的时候。
日知其所亡
文章选项: |
wandys![版主](http://www.linuxforum.net/forum/images/mod.gif) (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
|
|
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![版主](http://www.linuxforum.net/forum/images/mod.gif) (addict) 02-09-21 10:54
|
|
在一个文件中定义为数组, 在另一个文件中又声明为指针, 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![版主](http://www.linuxforum.net/forum/images/mod.gif) (addict) 02-09-21 16:41
|
|
这里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
|
|
如果在一个文件里这样声明:
extent int a;
在另一个文件里声明:
char a;
连接的时候会出现什么问题?
日知其所亡
文章选项: |
wandys![版主](http://www.linuxforum.net/forum/images/mod.gif) (addict) 02-09-21 18:18
|
|
"指教"可不敢当, 大家一起讨论就好. :-)
这对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
|
|
对于gcc确实这样(据说联接器是ld),但vc不是这样,对于extern int aa;另外一个文件里char aa;联接错误。 所以只能说是gcc/ld就是这样的一个行为(联接的时候不检查类型,名字一样就连,全是变量还好理解,可以认为是节省一个指针转换的过程,一个常量一个变量恐怕就有问题了),这个行为不代表所有的联接器,但这个行为应该来说非常不合理。 文章选项: |
wandys![版主](http://www.linuxforum.net/forum/images/mod.gif) (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
|
|
对于全局变量,一般使用方法是 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
|
|
厉害,透彻。 |