如何在函数文件和项目文件中正确包含自己的库
How to correctly include own libraries in function files and project files
我在尝试做 K&R 的 Exercise 8-3
时卡住了,练习的目标是重写 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/function 文件并在您的项目中使用它们?
- 有没有办法让链接器通过包含头文件来确定我的函数的位置,就像它对标准函数所做的一样?
请注意 library and its header files.
之间的区别
库是指向链接器的(collection 个)二进制 machine code (with some additional meta-data, e.g. relocation 指令。
例如,在我的 Linux 系统上,动态库通常共享 objects(例如 /usr/lib/x86_64-linux-gnu/libgmp.so
)并且尝试一些像 #include "libgmp.so" //wrong
.
但是图书馆有一些 API。 API 由一些文档和一些 header 文件给出,例如gmp.h
并且你应该在任何使用它的 C 代码(你的 C 翻译单元)中 #include "gmp.h"
。
myfunction.c: I have a .c file per function
每个函数有一个文件通常很糟糕。您通常可以对相关功能进行分组。例如,在您的情况下,您可能希望在同一个 myopenclose.c
翻译单元中定义 myfopen
和 myfclose
函数(即使您不必这样做),因为这两个函数是密切相关。根据经验,我更喜欢每个源文件只有一行或几千行(但这确实是个人喜好问题,有些人喜欢有很多小文件)。
请记住,编译器真正看到的是代码的预处理形式。考虑要求您的编译器生成该形式(例如,从 foo.c
中,您可以在我的 Linux 桌面上使用 gcc -C -E -Wall foo.c > foo.i
获得其预处理形式 foo.i
)并查看它。在你自己的文件上尝试一下(例如你的 myopenclose.c
如果你有的话)。
如果您有许多小文件,编译器可能会在每个文件中包含相同的 header,并且每次都会编译这些包含的声明。顺便说一句,注意 gcc
只是一个 driver 程序。将它与 -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).
你可能应该声明 extern
你的 _iob
全局变量在你的stdio.h
和定义 全局 _iob
仅在您的库的一个实现文件中(可能 myopenclose.c
,如果相关的话)。
不要混淆定义和声明(变量、函数、类型等)。花一些时间阅读 C11 standard n1570。这些词在那里定义。根据经验,声明应该进入 header .h
文件,定义(变量和函数)在实现 .c
文件中(当然细节要复杂得多,你经常但不始终在 header 文件中定义类型和 struct
。
我强烈建议使用一些 Linux 发行版(它对开发人员和学生非常友好)并且 研究 一些 现有的源代码 免费软件 C standard library (like musl-libc, whose code is quite readable). More generally, study the source code of existing free software projects (e.g. on 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 (your cc1
program -started by gcc
- is translating a .c
file into some object file .o
) and about linkers (your ld
, generally started by gcc
, is agglomerating several object files, processing relocations inside them, and producing an ELF library or an executable). The preprocessing 的内容(例如 #include
指令)是由 cc1
在编译时完成的。链接器看不到任何 header 文件(它只处理 object 文件或库)。
如果您重写一些系统声明和函数,同时包含系统声明,您可能会遇到一些冲突。
头文件 (.h) 包含代码(通常只有声明),您描述的机制 (#ifndef STDIO_H
) 是为了防止多次包含同一头文件 - 主要是因为另一个包含文件(头)已经加载可能还包括它。这导致了与您相同的碰撞。
例如,在 C 中,您可以
制作一个新的头文件,其中包含您自己的声明 + 不与您的冲突的 stdio 声明
使用 stdio 声明,并且只编写使用与 stdio
相同的结构、定义、枚举等的新函数
重写必要的声明和代码,让您不再包含系统头文件
在头文件和代码中使用其他命名约定,例如 my_iob
。
最后两个可能对你的情况来说是最好的,因为你仍然有一些来自头文件的冲突。
例如,您的代码可能不包含 stdio.h
,但您包含的另一个头文件可能会间接包含...
我在尝试做 K&R 的 Exercise 8-3
时卡住了,练习的目标是重写 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/function 文件并在您的项目中使用它们?
- 有没有办法让链接器通过包含头文件来确定我的函数的位置,就像它对标准函数所做的一样?
请注意 library and its header files.
之间的区别库是指向链接器的(collection 个)二进制 machine code (with some additional meta-data, e.g. relocation 指令。
例如,在我的 Linux 系统上,动态库通常共享 objects(例如 /usr/lib/x86_64-linux-gnu/libgmp.so
)并且尝试一些像 #include "libgmp.so" //wrong
.
但是图书馆有一些 API。 API 由一些文档和一些 header 文件给出,例如gmp.h
并且你应该在任何使用它的 C 代码(你的 C 翻译单元)中 #include "gmp.h"
。
myfunction.c: I have a .c file per function
每个函数有一个文件通常很糟糕。您通常可以对相关功能进行分组。例如,在您的情况下,您可能希望在同一个 myopenclose.c
翻译单元中定义 myfopen
和 myfclose
函数(即使您不必这样做),因为这两个函数是密切相关。根据经验,我更喜欢每个源文件只有一行或几千行(但这确实是个人喜好问题,有些人喜欢有很多小文件)。
请记住,编译器真正看到的是代码的预处理形式。考虑要求您的编译器生成该形式(例如,从 foo.c
中,您可以在我的 Linux 桌面上使用 gcc -C -E -Wall foo.c > foo.i
获得其预处理形式 foo.i
)并查看它。在你自己的文件上尝试一下(例如你的 myopenclose.c
如果你有的话)。
如果您有许多小文件,编译器可能会在每个文件中包含相同的 header,并且每次都会编译这些包含的声明。顺便说一句,注意 gcc
只是一个 driver 程序。将它与 -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).
你可能应该声明 extern
你的 _iob
全局变量在你的stdio.h
和定义 全局 _iob
仅在您的库的一个实现文件中(可能 myopenclose.c
,如果相关的话)。
不要混淆定义和声明(变量、函数、类型等)。花一些时间阅读 C11 standard n1570。这些词在那里定义。根据经验,声明应该进入 header .h
文件,定义(变量和函数)在实现 .c
文件中(当然细节要复杂得多,你经常但不始终在 header 文件中定义类型和 struct
。
我强烈建议使用一些 Linux 发行版(它对开发人员和学生非常友好)并且 研究 一些 现有的源代码 免费软件 C standard library (like musl-libc, whose code is quite readable). More generally, study the source code of existing free software projects (e.g. on 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 (your cc1
program -started by gcc
- is translating a .c
file into some object file .o
) and about linkers (your ld
, generally started by gcc
, is agglomerating several object files, processing relocations inside them, and producing an ELF library or an executable). The preprocessing 的内容(例如 #include
指令)是由 cc1
在编译时完成的。链接器看不到任何 header 文件(它只处理 object 文件或库)。
如果您重写一些系统声明和函数,同时包含系统声明,您可能会遇到一些冲突。
头文件 (.h) 包含代码(通常只有声明),您描述的机制 (#ifndef STDIO_H
) 是为了防止多次包含同一头文件 - 主要是因为另一个包含文件(头)已经加载可能还包括它。这导致了与您相同的碰撞。
例如,在 C 中,您可以
制作一个新的头文件,其中包含您自己的声明 + 不与您的冲突的 stdio 声明
使用 stdio 声明,并且只编写使用与 stdio
相同的结构、定义、枚举等的新函数
重写必要的声明和代码,让您不再包含系统头文件
在头文件和代码中使用其他命名约定,例如
my_iob
。
最后两个可能对你的情况来说是最好的,因为你仍然有一些来自头文件的冲突。
例如,您的代码可能不包含 stdio.h
,但您包含的另一个头文件可能会间接包含...