如何在函数文件和项目文件中正确包含自己的库

时间:2017-11-23 06:07:44

标签: c libraries kernighan-and-ritchie

我试图做K& R Exercise 8-3时遇到困难,练习的目的是重写stdio.h的某些功能,例如fopenfclose,{ {1}}和fillbuf 这是我的源文件的组织方式:

flushbuf:包含类型和宏定义,以及适用于库的某些函数的声明。该文件的所有内容都包含在stdio.h #ifndef行之间,如下所示:

#endif

#ifndef STDIO_H #define STDIO_H /* content of stdio.h */ #endif :我的每个函数都有myfunction.c个文件,每个文件都有.c行来加载所有需要的类型定义。

#include "stdio.h":我有代码来测试我的功能,main.c也有main.c行。

我的问题如下:当我尝试使用#include "stdio.h"编译所有文件时,我遇到了错误:

gcc

在我的每个函数文件中都包含multiple definition of `_iob' ,(stdio.h是我在_iob中定义的变量)...为什么会发生这种情况?我虽然stdio.h行专门用于防止此类错误。

更一般地说:

  1. 您如何制作自己的头文件和库/函数文件并在项目中使用它们?
  2. 有没有办法让链接器通过包含头文件来确定我的函数的位置,就像它对标准函数一样?

2 个答案:

答案 0 :(得分:2)

请注意library及其header files之间的区别。

库是二进制machine code的(集合)(带有一些额外的元数据,例如链接器的relocation指令。)

例如,在我的Linux系统上,动态库通常是共享对象(例如/usr/lib/x86_64-linux-gnu/libgmp.so),尝试像#include "libgmp.so" //wrong这样的预处理器指令是完全没有意义的。

但是图书馆有一些API。该API由一些文档和一些头文件给出,例如, gmp.h您应该#include "gmp.h"使用它的任何C代码(您的C翻译单元)。

  

myfunction.c:我的每个函数都有一个.c文件

每个功能一个文件通常很差。您通常可以对相关功能进行分组例如,在您的情况下,您可能希望在同一个myfopen翻译单元中定义myfclosemyopenclose.c函数(即使您不必),因为这两个函数功能密切相关。根据经验,我更喜欢拥有一行或几千行的源文件(但这确实是一种品味问题,有些人喜欢有许多小文件)。

请记住,编译器真正看到的是预处理的代码形式。请考虑让您的编译器生成该表单(例如,从foo.c您可以在我的Linux桌面上使用foo.i获取其预处理的表单gcc -C -E -Wall foo.c > foo.i并查看它。请尝试使用自己的文件(例如myopenclose.c,如果有的话)。

如果你有许多小文件,编译器可能在每个文件中包含相同的标题,并且这些包含的声明每次都会被编译。顺便说一句,请注意gcc只是一个驱动程序。与-v标志一起使用。您将看到它正在运行cc1(适当的C编译器),as(汇编程序),ld(链接程序)等。

  

我遇到了错误:

     

`_iob'

的多重定义      

在包含stdio.h的每个函数文件中,(_iob是我在stdio.h中定义的变量。)

您可能extern _iob声明<{em> stdio.h全局变量,定义全局_iob只有一个实现文件(如果它是相关的,可能是myopenclose.c)。

不要混淆定义声明(变量,函数,类型等)。花一些时间阅读C11标准n1570。这些词在那里定义。根据经验,声明应该放在标题.h文件,实现.c文件中的定义(变量和函数)中(当然,细节要复杂得多,你经常但并不总是定义类型和头文件中的struct

我强烈建议使用一些Linux发行版(它非常适合开发人员和学生)以及学习某些现有免费软件的源代码C standard library (如musl-libc,其代码非常易读)。更一般地说,研究现有free software项目的源代码(例如,在github上)。他们会激励你。

  

有没有办法让链接器通过包含头文件来找出我的函数的位置,就像它对标准函数一样?

这显示了很多混乱(上面的问题没有任何意义)。详细了解compilerscc1计划 - 由gcc启动 - 正在将.c文件转换为某些object file .o)和{{ {3}}(您的ld,通常由gcc启动,它聚集了几个目标文件,处理其中的重定位,并生成linkers库或可执行文件。 ELF(例如#include指令)在编译时由cc1完成。链接器无法查看任何头文件(它只处理目标文件或库)。

答案 1 :(得分:2)

如果重写某些系统声明和函数,同时包括系统声明,则可能会发生一些冲突。

头文件(.h)包含代码(通常只是声明),你描述的机制(#ifndef STDIO_H)是为了防止同一头文件的多个包含 - 主要是因为另一个已包含的文件(头文件)已加载也可能包括它。这导致了与你一样的碰撞。

在C中,你可以,例如

  • 制作一个新的头文件,其中包含您自己的声明+不与您发生冲突的stdio声明

  • 使用stdio声明,只编写使用相同结构,定义,枚举等的新函数...作为stdio

  • 重写必要的声明和代码,使 不再包含系统标题

  • 在您的标头文件和代码中使用其他命名约定,例如my_iob

在你的情况下,最后两个可能是最好的,因为你仍然有来自头文件的一些冲突。

例如,您的代码可能不包含stdio.h,但您包含的另一个头文件可能间接地执行此操作...