我在尝试执行 Exercise 8-3
时遇到了困难K&R,练习的目标是重写stdio.h
的一些函数例如fopen
, fclose
, 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
中定义的变量)...为什么会这样?我虽然#ifndef
行是为了专门防止此类错误。
更一般地说:
- 您将如何制作自己的头文件和库/函数文件并在您的项目中使用它们?
- 有没有办法让链接器通过包含头文件来确定我的函数的位置,就像它对标准函数所做的一样?
最佳答案
请注意 library 之间的区别及其 header files .
库是二进制文件(的集合)machine code (带有一些额外的元数据,例如链接器的 relocation 指令)。
例如,在我的 Linux 系统上,动态库通常是共享对象(例如 /usr/lib/x86_64-linux-gnu/libgmp.so
),尝试一些完全没有意义预处理器指令,如 #include "libgmp.so"//wrong
。
但是图书馆有一些API .该 API 由一些文档和一些头文件提供,例如gmp.h
并且您应该在使用它的任何 C 代码(您的 C 翻译单元)中#include "gmp.h"
。
myfunction.c: I have a .c file per function
每个函数都有一个文件通常很糟糕。您通常可以对相关功能进行分组。例如,在你的情况下,你可能想在同一个 myopenclose.c
翻译单元中定义你的 myfopen
和 myfclose
函数(即使你不必)因为这两个功能密切相关。根据经验,我更喜欢每个源文件只有一行或几千行(但这确实是个人喜好问题,有些人喜欢有很多小文件)。
请记住,编译器真正看到的是代码的预处理形式。考虑要求您的编译器生成该形式(例如,从 foo.c
您可以使用 gcc -C -E -Wall foo 获得其预处理形式
在我的 Linux 桌面上)并查看它。在你自己的文件上尝试一下(例如你的 foo.i
。 c > foo.imyopenclose.c
如果你有的话)。
如果您有许多小文件,编译器可能会在每个文件中包含相同的 header ,并且每次都会编译这些包含的声明。顺便说一句,请注意 gcc
只是一个驱动程序。将它与 -v
标志一起使用。您会看到它正在运行 cc1
(正确的 C 编译器)、as
(汇编器)、ld
(链接器)等.
I run to the error:
multiple definition of `_iob'
on every one of my function files where my stdio.h is included, (
_iob
is a variable I only defined inside my stdio.h).
您可能应该在stdio.h
中声明 extern
您的 _iob
全局变量并定义 全局 _iob
仅在您的库的一个实现文件(可能是 myopenclose.c
,如果相关的话)中。
不要混淆定义和声明(变量、函数、类型等)。花一些时间阅读 C11标准n1570 .这些词在那里定义。根据经验,声明应该放在头文件 .h
中,定义(变量和函数)放在实现 .c
文件中(当然细节要复杂得多,您经常但不总是在头文件中定义类型和 struct
。
我强烈建议使用一些 Linux 发行版(它对开发人员和学生非常友好)并研究一些现有免费软件的源代码 C standard library (比如 musl-libc ,其代码可读性很强)。更一般地,研究现有的源代码 free software项目(例如 github )。他们会激励你。
Is there a way to make the linker figure out the position of my functions just by including the header file, the same way it does for standard functions ?
这说明了很多困惑(上面的问题没有任何意义)。阅读更多关于 compilers 的信息(您的 cc1
程序 - 由 gcc
启动 - 正在将 .c
文件翻译成一些 object file .o
) 和关于 linkers (您的 ld
,通常由 gcc
启动,正在聚合多个目标文件,处理其中的重定位,并生成一个 ELF 库或一个可执行文件)。 preprocessing (例如 #include
指令)在编译时由 cc1
完成。链接器看不到任何头文件(它只处理目标文件或库)。
关于c - 如何在函数文件和项目文件中正确包含自己的库,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47448659/