1、如何解释声明和定义,它们有何区别?
C语言中,某一模块内部的数据对象只能有一个定义,可以有多个extern声明。
定义是一种特殊的声明,它负责创建一个对象并分配空间。
声明告诉编译器对象的类型和名字,对象的内存分配在别处进行,无需再定义就可直接使用。
它们的本质区别是:是否分配内存空间,定义需要分配空间,声明不需要分配空间。
2、为什么要明确区分声明和定义,他们有什么用处?
声明相当于普通的声明,它所说明的并非自身,而是其他地方创建的对象;
定义相当于特殊的声明,他为对象分配内存。
这里以多个文件的模块为例:文件A中定义了数据a,文件B中定义了数据b,如果文件B想用到数据a,则需要在B中对a进行声明才可以使用数据a,如果不声明数据a,则编译器会报错“数据a没有定义”。
内部原理就涉及到编译原理的知识,以linux为例,编译器对文件的转换过程为:预处理、编译、汇编、链接(详见文章:【1.2】系统漫游——“程序”被其他程序翻译成不同格式)。以文件A、B为例,当“汇编”完成后每个源文件(*.c)会生成对应目标文件(*.o),文件B中对数据a的引用并没有转换成具体地址,因为此时编译器不知道a到底在哪定义的,而是把这个“异常”存到“符号表”中,当进行“链接”时从各个目标文件中寻找a的定义,这里能从A中找到,则所有关于数据a的引用都转换成具体地址,如果没找到则报错“数据a没有定义”。
定义语句“int test = 0”,如果作为“外部变量”没有初始化,则运行时系统默认为0,因为它存在于bss段中。即便如此,我们仍不能指望编译器帮我们初始化,所以对外部变量无论何值都要手动初始化。如果作为“内部变量”没有初始化,则其值是随机的,因为存在于栈中,所以内部变量也要手动初始化,尤其是指针变量,否则后果很严重,老板很生气!
声明语句“ extern int test ”中不能进行初始化,这个声明就是对外部变量a的引用。这两个语句是配套的,可以在一个文件中,也可以在不同文件中,声明可以有多个嘛!
如果有多个相同的外部对象定义,则结果是未定义的,一般情况系统会报错,因为链接时会找到多个地址空间,系统会晕的。严格说来,同一个模块内外部变量只允许定义一次,严禁多次定义。
3、在一个大型系统中多人协同开发,如何保证不会出现命名冲突?
首先要有一套完整有效的变量命名规则,多人共同遵守和维护;
利用static关键字,static可以改变外部变量的链接属性,使外部变量的作用域在一个源文件内,对于其他源文件该变量不可见。如果static修饰函数名,则效果类似。
所以,如果外部变量和函数仅在本文件有效,则都应该加上static,最大限度避免命名冲突。
如果static修饰内部变量,则改变内部变量的生存期,内部变量在函数第一次运行时就分配好空间,当函数退出时该变量数据仍然保留,以备下次使用。
在C语言中,默认状态下函数和外部变量都是是全局可见的,而实际经验表明对象在大多数情况下应该是有限可见的,这就需要程序员手动添加static关键字来限制链接属性,防止命名冲突。在实际开发中,要养成设置对象有限可见性的习惯,提高软件质量。
4、使用对象时,要注意哪些与声明定义相关的内容?
对于每个对象包括函数和变量,在使用之前必须先进行声明;
声明的格式与实际定义格式要严格一致,否则错误状态未定义;
因为定义也是一种类型的声明,所以如果在使用之前进行了定义,相当于进行了声明。
对于严格一致,哪怕是指针和数组也不能混淆,下面代码是有问题的:
char test[] = "I am the best one"; //文件A中
extern char *test; //文件B中
第一个定义中指明test是一个字符数组的名称,test的类型是“字符数组”,而不是“字符指针”,尽管引用test值得到的是数组首元素的地址。
第二个声明中指明test是一个指针,本质是一个变量,它俩对存储空间的利用方式是不同的,一个是直接引用数据,一个是间接引用数据,最后的结果是不同的。
数组名类似于一个常量指针,数组名test本身的值是不能更改的,但是对于指针test的值可以随意修改。即便是某种巧合,这么做出现了正确的结果,但是用指针的方式就人为的隐藏掉了数组名称的常量特性,对于维护者也是一个陷阱。所以为了现在和将来,耶稣的归耶稣,上帝的归上帝吧,把他们分开。
5、头文件是否可以起到什么作用?
一个好的方法是:只在头文件中对外部对象进行统一声明。当其他模块要用到本模块的数据时,直接包含本模块的头文件就行;本模块也要包含自己的头文件,这么做是合法的,方便模块内部函数调用关系。
靠谱推荐:Free Pos,微信JOCIHEZ