设为首页 加入收藏

TOP

13.3.3 连接错误示例(2)
2013-10-12 06:51:12 来源: 作者: 【 】 浏览:78
Tags:13.3.3 连接 错误 示例

13.3.3  连接错误示例(2)

6.实现同名函数导致符号重定义错误

在main.c文件中,增加对hello函数的定义。

void hello(void)
{
printf("+++ main hello +++\n");
}

编译通过后,连接时会产生符号重定义的错误。实际上,由于在hello.c和main.c的目标文件的代码段中分别定义hello函数。连接器认为出现重定义。与上例的程序类似,即使没有对函数hello的调用,编译器也不允许在代码段中出现2个hello函数的符号,所以还是会产生符号重定义连接错误。

知识点:在多个文件中定义全局的同名函数和变量,连接时将发生符号重定义错误。

7.静态函数与其他文件中的函数重名,可以正常使用

将main.c文件更改成如下:

#include "hello.h"
int main(void) 
{
hello();
string[0] = 'a';
return 0;
}
static void hello(void)                        /* 静态的函数,内部使用 */
{
printf("+++ main hello +++\n");
}
程序主要的变化是增加了静态的hello函数,在这种情况下,是可以成功地进行编译连接和运行的,运行结果如下所示:
+++ main hello +++

从运行结果中可以看到,main中调用的hello函数是main.c文件中定义的static 的hello函数。

值得注意的是,在这种情况下,编译器在进行编译的时候,main函数写在静态的函数hello前面,因此可以通过编译。这是由于main.c文件中包含了hello.h文件,其中具有对hello()函数的声明。但是,当编译器编译到main.c之中的hello函数的时候,由于static头文件中声明函数原型不同,可能出现一个编译报警(warning)。

8.静态变量与其他文件中的变量重名,可以正常使用

在main.c中,增加静态(static)的读写数据段的字符数组string[]的定义。

char string[1024] = "main string";

在这种情况下,编译连接可以成功。当连接工作完成后,可执行程序的读写数据段将出现两个string[1024]数组,均占用的空间。一个是hello.c中定义的全局的数组,一个是main.c定义的文件内部使用的数组。在数据访问的过程中,语句string[0] = 'a'访问的将是main.c中定义的数组string[]。

知识点:如果全局变量和函数已定义,而在某个文件中另外定义静态的同名变量和函数,可以在文件内部使用同名的静态变量和函数。在使用的过程中,将优先使用文件内的变量和函数。

9.在头文件中定义已经初始化数据,可能产生问题

在程序中,将string[]的定义放入hello.h的头文件中:

#ifndef HELLO_H
#define HELLO_H
void hello(void);
char string[1024] = "";
#endif

同时,取消在hello.c中对string[]数组的定义。此时,由于hello.c和main.c同时包含了hello.h头文件,因此string在内存中有两份,连接的时候将产生符号重定义错误。

如果将头文件中string的定义改为静态的,这时不会产生连接错误,但是会在hello.c和main.c的目标文件中各产生一个string[1024]。最终可执行程序的读写数据段中也会有两个string。

如果在头文件中使用如下方式定义:

const char string_const[1024] = {"constant data"};

由于具有const属性,string_const是一个在只读数据段上的常量。这样有多个文件包含该头文件的时候,在连接过程中也会出现符号重定义的错误。连接器在这个问题上,对读写数据区和只读数据区的处理方式是类似的。

知识点:具有初始值的变量将被连接到读写数据区。在头文件中不应该定义有初始值的全局变量。同样,也不应该定义只读数据段的常量。否则,在头文件被多个文件包含的时候,将发生连接错误。

10.在头文件中定义未初始化数据段,可以正常使用

在程序中,hello.h的头文件中定义string[],但是没有初值:

#ifndef HELLO_H
#define HELLO_H
void hello(void);
char string[1024];
#endif

同时,取消在hello.c中对string[]数组的定义。在这种情况下,编译连接都是可以通过的,程序也可以正常运行。

知识点:无初始化的变量将被连接到未初始化数据段,在头文件中可以定义。当头文件被多个文件包含时,该未初始化段在运行时也将只有一份。

事实上,由于没有初值,string[]将不再是读写数据段上的变量,而是未初始化数据段上的变量。未初始化段上的变量并不会占用目标文件或者可执行文件中的空间,它们只是一些标识。由于不需要分配空间,编译器允许这种做法。未初始化数据段的变量在运行的时候才会产生,而且只会有一个。

同理,可以将string修改为static的未初始化变量:

#ifndef HELLO_H
#define HELLO_H
void hello(void);
static char string[1024];
#endif

在这种情况下,在编译的时候将会在两个目标文件中各自记录一个未初始化数据段,在运行时程序将在内存上开辟两个独立的1024字节的数据区。

比较以上的两个示例(9和10),总结出以下的结论:

首先,不应该在头文件中使用全局的读写数据变量,这样当两个文件同时引用这个头文件的时候,将会产生符号重定义连接错误。

其次,在头文件中也不应该使用静态的变量,无论它有没有初值(即在读写数据段或者未初始化数据段),这样虽然不会引起连接错误,但是在各个源文件中各自产生变量,不但占用更多的空间,而且在逻辑上是不对的,也违背头文件的使用原则。

最后,在头文件中使用全局的没有初始化的变量是可以的,它在程序运行的过程中,在内存中只会有一份,可以被包含该头文件的程序访问。

C语言程序设计的角度,不应该在头文件中定义变量或者函数。对于函数,在头文件中只是声明,需要在源文件中定义;对于变量,无论何种性质(只读数据段、可读写数据段、未初始化数据段),最好的方式是在C语言的源文件中定义,在头文件中使用extern声明。


】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到: 
上一篇3.2.2 程序中段的使用 下一篇13.3.3 连接错误示例(1)

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容: