首页 > 解决方案 > 标头保护是否仅限于其各自库的范围?

问题描述

我继承了一些代码*,它在头文件 (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);

}

标签: cstructheader

解决方案


标头保护仅防止.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.cfoo.cstdio.h

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

导致这个问题的错误并不是ov5642_Sensor_Values.h's header guards 无效。错误是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包含初始化定义的新文件:

#include "ov5642_Sensor_Values.h"

extern const struct sensor_reg ov5642_1280x960_RAW[] =
{
    {0x3103,0x93},
    {0x3008,0x02},
    {0x3017,0x7f},
    .....
};

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


推荐阅读