【问题标题】:Linking .h files with .c with #ifdef header guards使用 #ifdef 标头保护将 .h 文件与 .c 链接
【发布时间】:2012-12-26 17:39:00
【问题描述】:

我在链接 .h 和 .c 文件时遇到了问题,我也阅读了一些关于这个问题的线程,所有这些都有点模糊,我仍然不能完全理解它的概念,而且我有很多关于链接问题,说我有 bc 和 bh,我将在 ac 中使用,我很困惑是否同时包含 bh ac 和 bc 因为 bc 本身需要知道 bh 中定义的结构,我有一些在 bh 中具有原型并在 bc 中定义的函数,它也使用 bh 中的结构,我不包括 bc 中的 bh 因为我知道 bh 更像是 ac 的接口,它将使用 bc 中的函数。这里一个更清楚的例子

b.h 文件

typedef struct{
int x, y;
}myStruct;

void funct1(myStruct);
void funct2(myStruct);

b.c 文件

void funct1(myStruct x)
{
    //do something
}

void funct2(myStruct y)
{
     //do something
} 

a.c 文件

#include "b.h"

int main()
{
myStruct x;
  funct1(x);
  funct2(y);
return 0;
}

在cygwin中执行命令:gcc b.c a.c -g

现在令人困惑的部分是,我有一个链接错误,其中在编译 b.c 时,它无法检测到 b.h 中的结构和原型。因为我只知道 b.h 用于从 a.c 链接 b.c 但是当两个 .c 都被编译时,似乎 b.​​c 找不到它的结构和原型,

为什么我没有在 b.c 中包含 b.h? 答案:因为据我所知,bh 已经包含在 ac 中,当我再次将它包含在 bc 中时,我将进行双重包含

如果您对如何进行此操作有任何想法,请随时告诉我。

有一个#ifdef 指令,但我似乎不知道如何执行此操作。

注意:假设以上所有代码在语法上都是正确的,如果有任何拼写错误的单词请忽略,我只是在 .h 和 .c 之间的包含之后

【问题讨论】:

    标签: c multiple-inclusions


    【解决方案1】:

    您确实需要在b.c 中输入#include b.h。每个文件在链接器接管之前都是单独编译的,所以你在 a.c 中包含 b.h 并不重要,因为 b.c 是自己编译的,除非你包含它,否则不知道 b.h 的内容。

    这是#include 守卫的示例

    // some_header_file.h
    #ifndef SOME_HEADER_FILE_H
    #define SOME_HEADER_FILE_H
    // your code
    #endif
    

    当 some_header_file.h 包含在任何地方时,如果 SOME_HEADER_FILE_H 已定义,则 #ifndef#endif 之间的所有内容都将被忽略,这将在它第一次包含在编译单元中时发生。

    通常的做法是在文件名之后命名#define,以确保项目中的唯一性。我也喜欢在它前面加上我的项目或命名空间的名称,以减少与其他代码冲突的风险。

    注意:即使使用上述包含保护,同一个头文件也可以在您的项目中多次包含,只是不能在同一个编译单元中包含两次。这证明如下:

    // header1.h
    #ifndef HEADER_H
    #define HEADER_H
    int test1 = 1;
    #endif
    
    // header2.h
    #ifndef HEADER_H
    #define HEADER_H
    int test2 = 2;
    #endif
    

    现在让我们看看当我们尝试包含上述两个文件时会发生什么。在单个编译单元中:

    // a.cpp
    #include "header1.h"
    #include "header2.h"
    #include <iostream>
    int main()
    {
       std::cout << test1;
       std::cout << test2;
    };
    

    这会产生编译器错误,因为 test2 未定义 - 它在 header2.h 中被忽略,因为 HEADER_H 在包含的时间已经定义。现在,如果我们将每个头文件包含在单独的编译单元中:

    // a.cpp
    #include "header2.h"
    int getTest2()
    {
       return test2;
    };
    
    // b.cpp
    #include "header1.h"
    #include <iostream>
    int getTest2(); // forward declaration
    int main()
    {
       std::cout << test1;
       std::cout << getTest2();
    };
    

    即使我们包含两个都定义 HEADER_H 的文件,它也可以正常编译并产生预期的输出(1 和 2)。

    【讨论】:

    • 1个问题,它可以检测到SOME_HEADER_FILE何时已经在其他.c文件中定义了吗?
    • #ifndef 表示如果未定义,无论您在何处包含 .h,它都会定义该符号,因此如果您再次包含相同的 .h,则该符号已定义.
    • @lemoncodes: 不,在 a.c 中包含 b.h 对 b.c 没有影响。当 b.h 首次包含在 b.c 中时,SOME_HEADER_FILE 仅在 b.c 中定义。随后在 b.c 中 SOME_HEADER_FILE#ifdef#ifndef 语句可以在 b.c 内检测到它。
    • 哦,我明白你的意思了,呵呵,有很多答案指向同一点,所以我会问同样的问题,“嗯,我的意思是你的 SOME_HEADER_FILE_His 在 .h 文件中对吧?所以当 .h 文件被包含在 ac 中,然后又被包含在 bc 中时,它是否可以定义 B_H_INCLUDED 缺陷?"
    • @lemoncodes 我已经编辑了我的答案以进一步解释 Remy 的观点(实际上,在我注意到他的评论之前,我正在输入它)。
    【解决方案2】:

    您需要在使用b.h 中定义的结构的所有文件中包含b.h。所以你需要在两个文件中都放一个#include &lt;b.h&gt;。为避免多次加载b.h,您需要指令#ifdef。在你的情况下:

    b.h

    #ifndef B_H
    #define B_H
    
    typedef struct{
        int x, y;
    }myStruct;
    
    void funct1(myStruct);
    void funct2(myStruct);
    
    #endif
    

    和 b.c:

    #include "b.h"
    
    void funct1(myStruct x)
    {
        //do something
    }
    
    void funct2(myStruct y)
    {
         //do something
    } 
    

    【讨论】:

    • 1 个问题先生,当 b.h 包含在 a.c 中时,b.c 是否检测到 B_H 已经定义?
    • 是的。指令#define B_H 定义变量已定义。因此,当#ifndef 搜索 B_H 时,它将返回 false 并跳过所有代码,直到 #endif。重要的是变量 B_H 仅在一个 .h 文件中定义。
    • @WilliamSeitiMizuta:你的“是的”是错误的。当 b.h 包含在 a.c 中时,b.c 无法检测到。 B.h 必须包含在 b.c 中,因此 B_H 可以在 b.c 中定义
    • 我的事情@WilliamSeitiMizuta 得到了我的观点,我不是在谈论 b.h 我在谈论 b.h 中的 B_H
    • @lemoncodes Remy 是正确的。如果您在 b.c 中包含 b.h,则尚未为该编译单元定义 B_H,即使您已将其包含在 a.c 中也是如此。我已经更新了我的答案以进一步解释这一点。
    【解决方案3】:

    正确的编码会让你在 b.c. 中包含 b.h。

    这是一个应该可以工作的标头保护:

    #ifndef B_H_INCLUDED
    #define B_H_INCLUDED
    //header file
    #endif
    

    将您的声明放在注释所在的位置,并包括您需要的任何地方。

    编辑 我的理解是gcc首先编译b.c,因为a.c依赖于b.c。但是先编译b.c的时候,b.h还没有被包含

    【讨论】:

    • 我编译的时候看的样子,后面是单独编译和链接的,所以可能顺序是什么都没关系?
    • 但是...头文件需要包含在编译步骤中,这发生在链接步骤之前。
    • 是的,重要的是当前的编译单元(即你不想在同一个单元中包含同一个文件两次)。
    • ohhh 所以如果是这样的话,其他 .c 文件可以检测到,在你的例子中,如果 B_H_INCLUDED 已经存在?
    • 不确定您在评论中到底要问什么,我认为答案是肯定的......我不是 C 专家(只是做了 2 年多!)。
    【解决方案4】:

    你需要#include b.h in b.c.它不仅仅是 a.c 的接口,b.c 也需要知道其自己代码的相同定义。您在 b.c 中不包括 b.h 的原因是错误的。每个 .c 文件都与其他每个 .c 文件分开编译。当编译器用 a.c 完成时,它会用 b.c 重新开始。 a.c 包含 b.h 并不重要,因为 b.c 甚至没有 a.c 存在的概念。标头保护的目的是防止在编译给定 .c 文件时多次包含 .h 文件时重复处理该文件。如果没有守卫,声明将被编译多次,从而导致现有符号的多个声明出现错误。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-05-02
      • 2023-03-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-10-08
      相关资源
      最近更新 更多