标头保护程序是否仅限于各自库的范围?

时间:2019-02-28 19:07:30

标签: c struct header

我继承了一些代码*,该代码在头文件(a_A.h)中声明并定义了一个结构。此结构位于包含层次结构树的顶级文件中,该树象征性地如下所示:

 file: t_T.h (#includes "c_C.h") //defines a struct
 file: c_C.h (#includes "h_H.h")
 file: h_H.h (#includes "a_C.h")
 file: a_C.h (#includes <stdio.h>) 

每个标头都有适当的标头保护,当看成平面文件集合时,它们似乎是非递归的。但是,文件c_C.h和a_C.h驻留在同一库中。虽然h_H.h驻留在另一个库中。从库的角度来看,这象征性地显示为:

t_T.h (includes a file from Lib_C) 
Lib_C: (includes a file from Lib_H)
Lib_H (includes a file from Lib_C)

是递归的,并且可能是我编译代码时重新定义问题的可能原因(链接器抱怨文件a_C.h中的结构已重新定义)。

1)我是否正确识别了问题?

2)如果是,为什么?我猜想库中的链接对象看起来对链接器来说是平坦的(即它们失去了层次结构上下文)。如果猜对了一点,那么:

3)我是否应该考虑将标头防护限制为各自库的范围?

下面是问题窗口中的错误声明:

symbol "ov5642_1280x960_RAW" redefined: first defined in "./TakePhoto.obj"; redefined in "./ArduCam/ov5642_Config.obj"  

./ TakePhoto中的标题:

#ifndef TAKEPHOTO_H
#define TAKEPHOTO_H
#include "ov5642_Config.h"
#include "HAL_ArduCAM.h"
...
#endif /* TAKEPHOTO_H_ */

./ ArduCAM / ov5642_Config.h中的标题:

#ifndef ARDUCAM_OV5642_CONFIG_H_
#define ARDUCAM_OV5642_CONFIG_H_
#include "HAL_ArduCAM.h"
#include "ov5642_Sensor_Values.h"
....
#endif /* ARDUCAM_OV5642_CONFIG_H_ */

HAL_ArduCAM中的标题

#ifndef  HAL_ArduCAM_h
#define  HAL_ArduCAM_h
#include <stdint.h>
#include "driverlib.h"
....
#endif /* HAL_ArduCAM_h */

ov5642_Sensor_Values.h具有以下内容

#ifndef ARDUCAM_OV5642_SENSOR_VALUES_H_
#define ARDUCAM_OV5642_SENSOR_VALUES_H_
#include <stdint.h>
const struct sensor_reg ov5642_1280x960_RAW[] =
{
     {0x3103,0x93},
     {0x3008,0x02},
     {0x3017,0x7f},
 .....

#endif /* ARDUCAM_OV5642_SENSOR_VALUES_H_ */

似乎OV5642_Sensor_Values的内容被复制了两次,一次是为TakePhoto,一次是为ovV5642_Config,尽管它们有标题保护。我最初的想法是存在递归依赖关系,但没有找到它。

好的,我在下面做了一个例子。在此示例中,有五个文件,三个文件(bar.h,foo.h,foo.c驻留在库中),其他两个文件(foo1.h,foo1.c)没有。请注意,foo.h包括bar.h,而foo1包括foo.h和bar.h。

我猜想当预处理器复制到foo.h中时,不会保留bar.h的保护头。因此,当foo1包含bar.h和foo.h时,将出现符号重新定义。因此,回答我自己的问题,不,这不是图书馆问题。不保留标题保护器似乎是造成我问题的可能原因。

它们粘贴在下面。

/*
 * bar.h
 *
 */

#ifndef ARDUCAM_BAR_H_
#define ARDUCAM_BAR_H_
#include <stdint.h>

typedef struct regStruct {
     uint16_t reg;
     uint8_t val;
 } regStruct;

 const struct regStruct regArray[] =
  {
      {0x3103,0x03},
      {0x3104,0x03},
      {0x3008,0x82},
      {0xffff,0xff},
  };
 const struct regStruct tinyArray[] =
   {
       {0x3106,0x03},
       {0x3003,0x82},
       {0xffff,0xff},
   };

#endif /* ARDUCAM_BAR_H_ */

/*
 * foo.h
 *
 *
 */

#ifndef ARDUCAM_FOO_H_
#define ARDUCAM_FOO_H_

#include <stdint.h>
#include <stdio.h>
#include "bar.h" //including this file causes redefinition


typedef struct Init_Parameters {
    //! Select sensor resolution
    //! options.
    //! \n Valid values are:
    //! - \b big
    //! - \b small
    //! - \b tiny
    uint8_t size;

} Init_Parameters;


uint8_t Sensor_Init(Init_Parameters *param);

typedef enum {
    small=0,
    big,
    tiny
} select_size;

#endif /* ARDUCAM_FOO_H_ */

/*
 * foo.c
 *
 *
 */

#include "foo.h"

uint8_t Sensor_Init(Init_Parameters *param)
{
    switch(param->size)
    {
    case big:
        break;
    case  small:
        break;
    case  tiny:
        break;
    }
    return 0x01;
}

/*
 * foo1.h
 *
 *  Created on: Feb 28, 2019
 *      Author: jnadi
 */

#ifndef FOO1_H_
#define FOO1_H_

#include "foo.h"
#include "bar.h"

#endif /* FOO1_H_ */

/*
 * foo1.c
 *
 *  Created on: Feb 28, 2019
 *      Author: jnadi
 */

#include "foo1.h"

void Camera_Init(){

    Init_Parameters setParams; //create instance
    setParams.size=big;
    Sensor_Init(&setParams);

}

4 个答案:

答案 0 :(得分:0)

头文件的物理位置仅通过包含文件搜索路径影响C源代码的编译。标头必须位于要搜索的目录之一中,如果存在多个同名目录,则搜索路径顺序将确定使用哪个。给定的头文件和库之间的关联(如果有的话)对于编译器是未知的,除非通过搜索路径间接进行,否则它不会影响编译。

您的主张

  

链接器抱怨文件a_C.h中的结构已重新定义

(加重)仅在“结构”中表示结构类型的 object 时才有意义。如果在多个翻译单元中定义了具有外部链接的变量,则链接器可能会抱怨,如果标头包含该变量的定义(而不只是声明),则很可能会发生这种情况。

如果问题是重新定义了 type 结构,则将由编译器(而不是链接程序)诊断该结构,这将与您的结论相矛盾

  

每个标头都有相应的标头保护装置

。正确的标题防护正是防止此类重新定义问题的原因。

  

1)我是否正确识别了问题?

当然没有具体说明。与不同库相关联的标头之间在两个方向上都存在标头依赖性,这反映了不良的设计,但并不会固有地导致编译或链接失败。

  

2)如果是这样[...]

不适用

  

3)我是否应考虑将标头保护区限制在其范围内   各自的图书馆?

不。头保护仅在编译阶段相关,并且C编译对库一无所知。编译器在翻译单个翻译单元的过程中处理的所有头文件均处于平等地位。实际上,这方面的主要风险是相反的:头球后卫的碰撞。


这回答了提出的问题。至于您的构建问题的真正本质,您没有给我们足够的信息,只能做推测。我自己的推测已经在上面传达了。

答案 1 :(得分:0)

使用上面发布的五个文件,并在foo.h中注释掉 #includes bar.h ,我相信我找到了解决问题的方法。

简单的答案是,一旦文件头保护包含在另一个文件的文件头中,就不会保留。

当bar.h包含在另一个头文件中时,其头保护符将被其新主机(foo.h)的头保护符所取代。因此,当foo1.h同时包含bar.h和foo.h时,就会出现符号重新定义问题

答案 2 :(得分:0)

标题防护仅阻止.h文件在一个顶级翻译单元中包含其内容两次或更多次。他们将处理这样的情况,其中两个或多个标头需要包含相同的一组通用定义:

// A.h
struct A { int x, y, z; };

// B.h
#include "A.h"
struct B { struct A aye; float f, g; };

// C.h
#include "A.h"
struct C { struct A aye; long l, m; };

// main.c
#include "B.h"
#include "C.h" // error, redefinition of struct A

但是每个翻译单元都从一个干净的宏环境开始,因此,如果您在两个不同的顶级翻译单元中包含头文件,则每个头对该头的声明都是可见的。这就是您想要的。 (考虑标准库头。您不希望stdio.h不在printf中声明bar.c只是因为在同一项目中也存在foo.c,其中也包含{{1 }}。

现在,您的问题是stdio.h定义了数据对象(不是类型)ov5642_Sensor_Values.h,并且此标头包含在两个不同的顶级翻译单元中(ov5642_1280x960_RAW个源文件)。每个翻译单元都会生成一个包含.c定义的目标文件,当您尝试将它们组合时,会从链接器中收到多个定义错误。

导致此问题的错误不是ov5642_1280x960_RAW的标题防护无效。错误在于ov5642_Sensor_Values.h不应进行任何全局定义。头文件只能声明(很少的例外,除非遇到它们,否则您不必担心)。

要修复该错误,请更改ov5642_Sensor_Values.h来声明ov5642_Sensor_Values.h而不定义它,如下所示:

ov5642_1280x960_RAW

并创建一个名为#ifndef ARDUCAM_OV5642_SENSOR_VALUES_H_ #define ARDUCAM_OV5642_SENSOR_VALUES_H_ #include <stdint.h> #include "sensor_reg.h" extern const struct sensor_reg ov5642_1280x960_RAW[]; #endif 的新文件,其中包含初始化的定义:

ov5642_Sensor_Values.c

并将该文件添加到您的链接中。

答案 3 :(得分:0)

谢谢大家,尤其是John Zwoi。我有一个截止日期(这是继承的代码),但是能够使自己冷静下来,以弄清约翰在说什么。我将结构定义移到了c文件中,并在标头中使用了extern指针,该指针类似于zwoi的声明。我也很满意我与zwoi的例子相匹配的事情(非常感谢!)。

extern const struct sensor_reg * ov5642_1280x960_RAW;