打印
[C语言]

浅说符号(变量)的声明和定义

[复制链接]
1978|15
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
xiaoyuan_ly|  楼主 | 2013-10-8 22:38 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
   符号(变量)的声明和定义,从学生时代起让我搞的很头疼,一知半解的混过来了。 如果真的要区分的话,我的理解是:第一次的声明和定义,以及以后的声明和定义来区分。 声明是什么,其实声明是符号(变量)在链接时的体现,所以虽然在编译阶段也有关系,但主要的还是为了在链接阶段去用的。而定义呢,是生成了对应的程序指令。正因为有生成了机器指令,那么这个定义是实实在在的使用了。而声明只是逻辑上说明了我有这个符号产生了,编译器记录下了这个符号,但并没有产生程序机器指令。有时候,我们会把声明和定义统一的说成声明也可,这时,就要区分好。接下来,第一次的声明是编译器记录从无到有的这个符号的过程,而编译器又只是对单个文件进行编译。也就是说我们的编译后的目标文件,就可能存在符号或变量同名重叠的问题了。如果你不闻不问,那么在链接阶段就会报错了,符号或变量重定义。这种情况就会出现在外部链接的全部变量和符号上,那么就需要声明的声明,我的理解:声明的声明就是引用,用修饰符extern;  extern char i; 就是一个声明的声明,代表了这个符号已在其他地方被声明过了,再次的声明只是为了能使用它。在链接阶段,链接器会将声明的声明归到最初第一次声明的位置上。所以就有了这个规则:定义只有一次,声明可以多重出现;或者说声明只有一次,而声明的声明可以多重出现。同理,对函数也一样,函数的定义产生了机器指令,而函数的第一次声明只是告诉编译器有这个符号存在,函数声明的声明就是为了告诉链接器,我和第一个函数声明定义的东西是同一个东西。 那么那个static 修饰符呢?! 只是将其的链接特性,从外部链接向内部链接转变。

相关帖子

沙发
xiaoyuan_ly|  楼主 | 2013-10-8 22:48 | 只看该作者
说的过程也是总结的过程。。。。。。。

使用特权

评论回复
板凳
yespider| | 2013-10-9 09:55 | 只看该作者
只是将其的链接特性,从外部链接向内部链接转变。

大部分说得很切合实际,搞懂了Extern的用法和含义,但这最后一句不太明白

使用特权

评论回复
地板
wsadadws| | 2013-10-9 11:25 | 只看该作者
说的很明白,谢谢

使用特权

评论回复
5
annwa| | 2013-10-9 13:09 | 只看该作者
嗯,应该是这样的了!

使用特权

评论回复
6
xiaoyuan_ly|  楼主 | 2013-10-9 15:12 | 只看该作者
yespider 发表于 2013-10-9 09:55
大部分说得很切合实际,搞懂了Extern的用法和含义,但这最后一句不太明白 ...

这句和声明和定义没有关系,只是想到static 这个修饰符,顺便说上去的。比如: void test(void) 和 static void test(void); 第一个是test符号是全局和外链接特性的,第二个加static后,是全局和内链接特性的;再说下,这时候的全局是变成了文件内的全局,内链接的意思是不能被其他的目标文件声明引用了,都被限制在当前文件内部了。

使用特权

评论回复
7
yespider| | 2013-10-9 15:46 | 只看该作者
xiaoyuan_ly 发表于 2013-10-9 15:12
这句和声明和定义没有关系,只是想到static 这个修饰符,顺便说上去的。比如: void test(void) 和 stati ...

问个题外的问题:
在C中说,⑴一般的函数在被调用后分配给他的内存(变量地址)就销毁了,⑵那嵌入式C的函数调用后也销毁吗?
回到帖子中。
static关键词的作用就是使变量/函数不销毁,起⑴的反作用的吧..因为内链接特性把test只能在文件内调用了,如果另一个文件用extern void test(void)还能解放test()吗?
可以这样理解吗:
一般情况下,头文件相包含了,就可以任意调用另一文件内的函数,却无法调用另一文件中的变量。所以用extern来解放变量,用static来禁锢函数。

刚接触,这些关键词的作用一直没切实明白

使用特权

评论回复
8
李富贵| | 2013-10-9 15:53 | 只看该作者
yespider 发表于 2013-10-9 15:46
问个题外的问题:
在C中说,⑴一般的函数在被调用后分配给他的内存(变量地址)就销毁了,⑵那嵌入式C的 ...

static修饰全局变量的含义是本文件可见,别的c文件无法使用这个全局变量。
static修饰局部变量的含义是跟全局变量一样的存储方式,函数退出后变量的值不销毁,下次进入函数仍然保持上次调用时改变的值。

使用特权

评论回复
9
yespider| | 2013-10-9 16:03 | 只看该作者
李富贵 发表于 2013-10-9 15:53
static修饰全局变量的含义是本文件可见,别的c文件无法使用这个全局变量。
static修饰局部变量的含义是跟 ...

static居然还有变全局变量为局部变量的作用...学习了,有空验证下..:handshake

使用特权

评论回复
10
江枫渔火| | 2013-10-9 17:53 | 只看该作者
一般情况下,头文件相包含了,就可以任意调用另一文件内的函数,却无法调用另一文件中的变量。所以用extern来解放变量,用static来禁锢函数。


另一个文件中的变量也是可以引用的,前提是,这是个全局变量(是在源文件中定义的,且没有static修饰)。因为你没有区分声明和定义的概念,所以你不知道怎么用吧。

只要在你需要引用该全局变量的地方 用extern进行一下声明,就可以了。extern修饰变量或函数声明,就是告诉电脑:“这个类型、这个名字的变量或函数已经在别的地方定义好了,而且是全局公开的,你就直接引用吧 !莫客气”

进行了这样的操作后,就算你那个变量没有在别的地方定义,你当前的源文件也可以编译正常,因为电脑已经假定那些变量已经定义了。但最后进行链接的时候,如果没有定义,是会出错的。

链接的时候会去寻找那些你曾告诉它在别的地方已经有了的东西,如果找不到,就会产生严重后果,轻则机毁人亡,重则人毁机亡~

那些东西可以是别的源文件定义的符号,更可以是库文件中的符号

有时候可以认为 声明 就是 告诉周遭,有这么一个符号了(函数名或变量名),而定义 就是 将 这么一个符号 赋予 这么个东西(一块内存空间)了。
一个东西的名字可以被无限次的广播给所有人知道,让全世界人都知道它叫啥,让全宇宙都能找到它~(可以随便声明)
但一个名字,就不适合用在多个东西上了,不然不就乱套了~(但只可以定义一次,重复定义是要机毁人亡的~)

使用特权

评论回复
11
xiaoyuan_ly|  楼主 | 2013-10-9 17:55 | 只看该作者
本帖最后由 xiaoyuan_ly 于 2013-10-9 18:22 编辑
yespider 发表于 2013-10-9 15:46
问个题外的问题:
在C中说,⑴一般的函数在被调用后分配给他的内存(变量地址)就销毁了,⑵那嵌入式C的 ...

我看了你的问题: 首先你没有正确的去理解源代码变成程序的这个过程,或者说你没有关注过;编译,链接和加载这一个过程。其次你没有仔细的去深究过符号(变量)在这三个阶段(编译、链接、加载)是怎么处理的。
或许我们不需要做的知道全部细节,但也应该知道他们的大概情况-因为我们是实用者而不是研究者;但也因为这样,为了更好的为我们服务,我们需要知道他的大概本质,这样有利于我们去掌控和把握。接下来回答你的问题。
    问题(一):函数和变量是物理存在的,并没有销毁之说,或者说没有被CPU去理睬而睡眠了更加的恰当。内存的变量也同样是物理的,对于变量来说都是一直存在的(当你调用这个变量的时候就会“显现”出来)。对于全局变量来说,没有“显现”的说法,因为它一直存在内存的固定地址上。而局部变量才有被“显现”的说法,其本质就是程序运行的代码和变量的局部性,这是在内存(堆中)共享内存的逻辑方法,理由是你将这些变量定义成了局部变量。也许用“覆盖相同的内存堆中位置”更能说明问题;也就是说这个局部变量的内存位置被编译器生产的所有局部变量所共用。-这才是本质。
    问题(二):不管是嵌入式C还是一般的其他C编译器,函数都是物理存在的。如果你一定要说成销毁,那就是CPU不去执行这个函数代码,就被认为是销毁了。其实,我想用睡眠更合适。
   "static关键词的作用就是使变量/函数不销毁,起⑴的反作用的吧..因为内链接特性把test只能在文件内调用了,如果另一个文件用extern void test(void)还能解放test()吗?” 这个的理解,是这样的,此test并非extern 修饰的test,这2个符号虽重名,但却是不同的符号。比如:西安路的10号和北京路的10号那样,10号是同名的,但却属于不同“类”西安路和北京路。那么对全局符号和局部符号来说,就是分属于不同的2个类。当然在同一个类中是不能重名的。extern void test(void);这个我的理解是声明的声明,你只是想在文件中引用这个函数,而函数的真身并不在本文件中,而是在其他文件中,同时,也告诉你的链接器到其他文件中去找这个函数的“真身”。(去看下我的浅说符号(变量)的声明和定义这个帖子了)。
    接着说你的问题:头文件的包含,在编译阶段是展开的,内嵌入你当前的文件中,所以本质上讲你仍有且只有一个文件,为什么呢? 因为C编译器只处理单个文件。在链接加载阶段,才会把多个目标文件组合起来。包含的时候,就是相当于在进行声明或定义。C编译器会在处理时,按照规则进行符号的相应归类处理。
至于你说的“用extern来解放变量,用static来禁锢函数” 这样的理解有点狭隘了。你说呢?!
    在仔细好好的看看想想,你应该会明白的。
   

使用特权

评论回复
12
xiaoyuan_ly|  楼主 | 2013-10-9 18:06 | 只看该作者
yespider 发表于 2013-10-9 16:03
static居然还有变全局变量为局部变量的作用...学习了,有空验证下..

...

    你在看下我在C语言栏中发的几个浅说吧,有问题再来说。我想我说的这些也是为了让我们更好的运用C语言和C编译器。也是这几年下来在我这里,自己被问的比较多的原理上的东西。希望对你有所帮助。

使用特权

评论回复
13
yespider| | 2013-10-9 20:21 | 只看该作者
xiaoyuan_ly 发表于 2013-10-9 18:06
你在看下我在C语言栏中发的几个浅说吧,有问题再来说。我想我说的这些也是为了让我们更好的运用C语言 ...

好,非常感谢LZ!信息量有点大,学习一下再来

使用特权

评论回复
14
xiaoyuan_ly|  楼主 | 2013-10-9 22:44 | 只看该作者
yespider 发表于 2013-10-9 20:21
好,非常感谢LZ!信息量有点大,学习一下再来

没关系了,共同学习了。。。。。

使用特权

评论回复
15
xiongxiaoshuang| | 2013-10-9 23:24 | 只看该作者
好吧,同是新手

使用特权

评论回复
16
i55| | 2013-10-9 23:43 | 只看该作者
赶脚楼猪喜欢把一些其实很容易就说清楚的概念搞得拗口生涩。其实《Pointer on C》这本书里面这些概念说的很清楚了,搞嵌入式如果只是学了课本里面的C语言,想要进阶的话,这本书是必看的,还有一本《C缺陷与陷阱》比这本简单一些。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

个人签名:追求卓越,成功将不期而至。

27

主题

284

帖子

1

粉丝