设为首页 加入收藏

TOP

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

13.3.3  连接错误示例(1)

连接过程中常见的错误是符号未找到(undefined reference)和符号重定义(redefinition)。由于在编译器在处理各个符号的时候,已经没有了各个C语言源文件的概念,只有目标文件。因此对于这种错误,连接器在报错的时候,只会给出错误的符号的名称,而不会像编译器报错一样给出错误程序的行号。

符号未定义的错误经常发生在符号已经声明,但是并没有具体的定义的情况下。在C语言中,符号可以是一个函数,也可以是一个全局变量。在程序的编译过程中,只要符号被声明,编译就可以通过,但是在连接的过程中符号必须具有具体的实现才可以成功连接。

例如:某一个源程序的文件的某一个地方调用了一个函数,如果这个函数具有声明,这时编译就可以通过。在连接的过程中,连接器将在各个代码段中寻找函数,如果函数没有在程序的任何一个位置中定义,那么就不会有函数符号,这时连接器将发生符号未定义的连接错误。请阅读如下程序:

extern void function(void);
int main(void)
{
/* ...... */
function ();
/* ...... */
return 0;
}

在以上的例子中函数function可以和其调用者main在同一个源文件中,也可以在其他的源文件被调用中,但是它必须被定义。

符号重定义错误与符号未定义错误类似,如果连接器在连接的时候,发现一个符号在不同的地方有多于一个定义,这时就会产生符号重定义错误。对于同一个符号,如果在多个源文件中出现多次,将会产生符号重定义错误。

知识点:在连接过程中,符号未定义和符号重定义是两种最基本的错误。

下面以一个包含3个文件的程序为例,说明在连接过程中的错误。这个程序的三个文件为hello.h、hello.c和main.c。

main.c文件如下所示:

#include "hello.h"
int main(void) 
{
hello();
string[0] = 'a';
return 0;
}
hello.h文件如下所示:
#ifndef HELLO_H
#define HELLO_H
void hello(void);
extern char string [];
#endif
hello.c文件如下所示:
#include <stdio.h>
#include "hello.h"

char string [1024] = "";
void hello(void)
{
printf("=== hello ===\n");
}

以上是一个可以正常运行的程序,编译器将对main.c和hello.c两个源文件分别进行编译。在main函数中,调用了函数hello,并访问了数据string,由于包含了hello.h头文件,hello和string具有声明,因此main.c可以被成功编译。hello.c中定义了一个读写数据段上的变量string和函数hello。在连接的过程中,目标文件main.o对string和hello符号进行访问,hello.o提供了这两个符号,因此可以连接成功。

1.由于无数据定义导致符号未定义错误

将hello.c中对data的定义去掉,这时编译器依然可以成功编译main.c和hello.c。但是,由于string[0] ='a'编译后产生的代码将对string访问,在连接的过程中找不到string这个数据,因此会产生符号未定义连接错误(找不到数据)。

2.由于无函数实现导致符号未定义错误

将hello.c中对hello函数的定义去掉,这时编译器还是可以成功编译main.c,而且有这个符号,因此也会产生目标文件hello.o。但是,在连接器处理函数调用的时候,需要跳转到hello符号,由于实际上并没生符号而报告未定义连接错误(找不到函数)。

知识点:在程序中使用只有声明而未定义的函数或数据,可以成功编译,但是连接时将发生符号未定义错误。

3.由于数据仅能在文件内部使用,导致符号未定义错误

将hello.c的数组string []更改为静态变量:

static char string [1024] = "";

此时编译依然是可以通过的,这时候由于string已经是一个静态数据,因此它不会出现在hello.c的目标文件的符号中。也就是说,增加static修饰后,目标文件和以前将略有不同。

连接器在处理的时候,虽然有string这个数据,但是它只有数据区而没有符号,因此依然会出现未定义符号错误。

4.函数具有声明可编译通过,但有连接错误

取消hello.c中对函数的声明,在main.c增加对该函数的声明:

void hello(void);
在hello.c中,将hello函数的定义改为静态的:
static void hello(void)
{
printf("=== hello ===\n");
}

这时,编译还是能通过,由于hello成为静态函数,只能在文件内部使用,不会产生外部的符号。因此,虽然在main中对该函数进行了声明,连接也会产生未定义符号错误。

函数在各个源文件中的声明不会对连接产生影响,因此,在本例中,只要hello函数不是静态的,连接就可以通过。

知识点:使用static的函数和变量不能给其他文件调用,否则会发生连接的符号未定义错误。

5.定义同名变量导致符号重定义错误

在hello.h文件中,取消对string的外部声明。在main函数中增加一个定义在读写数据段的字符数组string[],

char string[1024] = "main string";

编译通过后,在连接时将会产生符号重定义的连接错误。main.c和hello.c中各自有一个读写数据段的字符数组string[],虽然它们看似没有直接的关系,但连接器无法处理这种情况,依然会产生连接错误。

在这种情况下,即使没有对string的引用(即没有string[0] = 'a'),连接器依然无法处理两个同名的读写数据段全局变量,这时还是会报告符号重定义的连接错误。


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

评论

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