首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >如何在函数文件和项目文件中正确地包含自己的库

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

Stack Overflow用户
提问于 2017-11-23 06:07:44
回答 2查看 297关注 0票数 1

我被困在做K&R的Exercise 8-3时,这个练习的目标是重写stdio.h的一些函数,比如fopenfclosefillbufflushbuf,这里是我的源文件的组织方式:

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

代码语言:javascript
运行
复制
#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编译所有文件时,我运行到错误:

代码语言:javascript
运行
复制
multiple definition of `_iob'

在包含stdio.h的每个函数文件中,(_iob是只在stdio.h中定义的变量)...why会发生这种情况吗?我认为#ifndef行是专门防止此类错误的。

更一般的

  1. 如何创建自己的头文件和库/函数文件,并在项目中使用它们?
  2. 有没有一种方法可以让链接器通过包含头文件来确定我的函数的位置,就像它对标准函数所做的那样?
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2017-11-23 06:15:42

请注意图书馆与其头文件之间的区别。

库是二进制机器代码的集合(包含一些额外的元数据,例如搬迁 )。

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

但是一个图书馆有一些API接口。该API由一些文档和一些头文件(例如gmp.h )提供,您应该在使用它的任何C代码(您的C转换单元)中使用#include "gmp.h"

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

每个函数都有一个文件,这通常是很糟糕的。您通常可以对相关功能进行分组。例如,在您的示例中,您可能希望在同一个myfopen转换单元中定义您的myopenclose.cmyfclose函数(即使您不必这样做),因为这两个函数是密切相关的。根据经验,我更喜欢有一行或几千行的源文件(但这确实是一个品味问题,有些人喜欢有很多小文件)。

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

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

我遇到了一个错误: \_iob' on every one of my function files where my stdio.h is included, (_iob`的多个定义是我仅在stdio.h中定义的变量。

您可能应该在您的extern中声明_iob全局变量,并在库的一个实现文件中定义一个全局_iob (如果相关的话,可能是myopenclose.c )。

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

我强烈建议使用一些Linux发行版(它对开发人员和学生非常友好),并研究一些现有自由软件C标准库的源代码(比如musl-libc,它的代码很容易读)。更一般地,研究现有自由软件项目的源代码(例如关于github)。他们会激励你的。

有没有一种方法可以让链接器通过包含头文件来确定我的函数的位置,就像它对标准函数所做的那样?

这表明了很多混乱(上面的问题没有任何意义)。了解更多关于编译器 ( cc1程序-started by gcc-正在将.c文件转换为某些目标文件 .o)和链接剂 (通常由gcc启动,正在聚集多个对象文件,处理其中的重定位,并生成小精灵库或可执行文件)的更多信息。预处理 (例如#include指令)是在编译时由cc1完成的。链接器看不到任何头文件(它只处理对象文件或库)。

票数 2
EN

Stack Overflow用户

发布于 2017-11-23 06:25:35

如果您重写了一些系统声明和函数,同时还包括了系统声明,那么您可能会遇到一些冲突。

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

例如,在C语言中,你可以

  • 创建一个新的头文件,其中包含您自己的声明+不与您的声明冲突的stdio声明
  • 使用stdio声明,并且只编写使用相同结构、定义、枚举等的新函数。斯迪奥
  • 重写必要的声明和代码,使您不再包含系统标头。
  • 在头文件和代码中使用另一个命名约定,比如my_iob

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

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

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/47448659

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档