2012-07-06 55 views
0

我是C新手,特别是编写静态库,我从库中得到奇怪的行为。静态库不需要头文件?

我写了一个叫做cde的小静态库。我用gcc的不同部分编译成的.o-文件,然后我用AR来把它们放在一起到.A文件现在

,当我想测试我的图书馆我做了以下内容:

gcc test.c -L../bin -lcde -lelf

libcde.a是我的库,位于../bin。 libelf.a是我需要的库(我不知道如何将它直接放到我自己的库中)。

问题是我可以调用我的库的每个函数而不需要必须包含我的库的头文件。这怎么可能?在编译时这些文件不应该被链接所以编译器应该不知道什么功能都可用我的库里面...

当我运行它通过以下方式,

gcc -L../bin -lcde -lelf test.c

的文件test.c无法找到我的头文件中定义的任何函数,即使我已经包含了它。

我认为我在这里做了一些根本性的错误,但我真的找不到什么。

回答

7

这里有两个问题:为什么test.c没有头提供库中的例程声明?为什么链接在命令行中首先列出test.c,而不是命令行中最后列出的test.c?

我们无法提供完整答案,因为您没有显示源代码。正如其他人所表明的那样,C在提供隐式声明方面有一些余地,主要是出于历史原因。这些隐式声明可能与你的例程的实际定义不匹配,这会导致错误,所以你应该避免隐式声明。

第二个问题的答案是这样的。给出你显示的两条命令行之一,编译器编译test.c然后调用链接器。 (编译器也可以做其他事情,例如编译时不需要链接或链接以前编译的源代码中的对象模块。)当编译器调用链接器时,它会按照与您的顺序相对应的顺序传递链接器参数将它们传递给链接器。特别是,如果在“test.c”之前加上“-lcde”,那么编译器在运行链接器时将“-lcde”放在来自test.c,test.o的目标模块之前。

由于链接器的操作方式,这很重要。除此之外,链接器还有一个需要定义的符号列表。该列表最初是空的。链接器从左到右处理来自命令行的输入。当链接器在其命令行中看到像“test.o”这样的对象模块时,它将读取对象模块并对其进行处理。通常情况下,目标模块包含对其未定义的某些符号的引用,例如调用库例程。如果链接器已经具有来自先前文件的这些符号的定义,则它将引用连接到定义。如果它没有定义,它会将符号添加到链接器需要定义的符号列表中。

当链接器处理库文件时,它会检查库中的每个对象模块,以查看该对象模块是否定义了链接器所需定义列表上的符号。如果是这样,链接器读取该对象模块并将其添加(及其定义)到链接器正在构建的可执行文件。如果没有,链接器忽略该目标模块。

现在我们可以看到为什么“test.c -lcde”有效,但“-lcde test.c”没有。在前一种情况下,链接器列出test.o需要的所有内容,然后从cde库中获取这些内容。在后一种情况下,链接器看到cde库,但不需要任何东西,所以它不会从库中取任何东西。然后链接器读取test.c并添加到所需符号列表中。然后命令行结束,链接器没有更多的文件,但仍然没有定义的符号。所以它报告一个错误。

因此,一般来说,库应该在命令行中最后列出。

0

C++头文件也用于为要调用的函数提供原型。编译器使用原型来帮助您在调用外部函数时不会误将参数计数/类型。所以通过这一点你静态或动态库没有任何区别。

2

没有头文件,你不会有你的函数的原型。 这是不是一个错误,但是C编译器会假设你调用一个函数,编译器还没有见过其中的原型样机是

int functionname() 

空()是指像“任何参数” ,所以你传递给这个函数的任何参数,就是函数被调用的方式。

根据参数的实际类型和函数的返回值,这可能会正确地工作,或者它可能至少在某些情况下工作。

在链接阶段,当您生成可执行文件时,链接器只会链接到一个函数的名称中。如果您调用一个名为“foo”的函数,并且您的库也有一个名为“foo”的函数符号,那么这就是链接器选择的内容。

1

在C中,假定一个称为无原型函数具有其原型这样的:

int function(); 

这意味着函数接受任何数量的任何参数,返回一个int。 所以,当然,它的工作原理,但尝试传递你的函数在libcde中的参数不要期望,它会崩溃。

你可以把libelf。一个在你的库中(而不是从你的库中拷贝目标文件到你的库中),这很少是一个好主意:你依赖于本地系统配置(你必须找到库所在的位置)。