简体   繁体   中英

are header guards limited to the scope of their respective library?

I've inherited some code* which declares and defines a struct in a header file (a_A.h). This struct is in the top file of an include hierarchy tree which symbolically looks like:

 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>) 

Each header has the appropriate header guards and appear to be non-recursive when looked at as a flat collection of files. However, files c_C.h and a_C.h reside in the same library. While h_H.h resides in a different library. From a library perspective this symbolically appears as:

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)

which is recursive and is the likely cause of redefinition problems when I compile the code (the linker complains that the struct in file a_C.h is redefined).

1) Did I correctly identify the issue?

2) If so, why? I am guessing that linked objects in a library appear flat to the linker (ie they've lost their hierarchy context). And if guessed somewhat correctly then:

3) Should I consider header guards to be limited to the scope of their respective library?

Below is the error statement from the problems window:

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

Header in ./TakePhoto:

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

Header in ./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_ */

Header in HAL_ArduCAM

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

ov5642_Sensor_Values.h has the following

#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_ */

It seems that the contents of OV5642_Sensor_Values is copied twice, once for TakePhoto and once again for ovV5642_Config despite their header guards. My original thought was that there was a recursive dependencies but didn't find it.

Ok, I've made an example pasted below. There are five files in this example, three files (bar.h, foo.h, foo.c reside in a library), the other two files (foo1.h, foo1.c) do not. Notice that foo.h includes bar.h and that foo1 includes both foo.h and bar.h.

I am conjecturing that the guard headers of bar.h are not preserved when the pre-processor copies into foo.h. Thus when foo1 includes bar.h and foo.h the symbol redefinition arises. So to answer my own question, no, it is not a library issue. Not preserving the header guard seems the likely cause of my problem.

They are pasted below.

 /* * 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); } 

The physical locations of header files affect C source compilation only via the include-file search path. Headers have to be in one of the directories that are searched, and if there is more than one with the same name, then the search path order determines which is used. The association, if any, between a given header and a library is not known to the compiler, and it does not affect compilation, unless indirectly via the search path.

Your claim that

the linker complains that the struct in file a_C.h is redefined

(emphasis added) makes sense only if by "the struct" you mean an object of a structure type. The linker may complain if a variable with external linkage is defined in more than one translation unit, which will likely happen if a header contains a definition of that variable (as opposed to merely a declaration).

If instead the issue is that the structure type is redefined, then that would be diagnosed by the compiler, not the linker, and it would tend to contradict your conclusion that

Each header has the appropriate header guards

. Proper header guards are exactly what would prevent such a type redefinition issue.

1) Did I correctly identify the issue?

Not with any specificity, for sure. That there are header dependencies in both directions between headers associated with different libraries reflects poor design, but it would not inherently cause compilation or linking to fail.

2) If so [...]

N/A

3) Should I consider header guards to be limited to the scope of their respective library?

No. Header guards are relevant at the compilation stage only, and C compilation knows nothing about libraries. All header files processed by the compiler in the course of translating a single translation unit are on equal footing. In fact, the main risk in this area is in the opposite direction: a collision of header guards.


That answers the question as posed. As for what the true nature of your build issue may be, you have not given us enough information to do more than speculate. My own speculations are already conveyed above.

Using the five files posted above and commenting out an #includes bar.h in foo.h, I believe I found the answer to my problem.

The simple answer is that header guards are not preserved once it is included into the header of another file.

When bar.h is included into another header file, its header guards are superseded by the header guards of its new host (foo.h). Thus symbol redefinition issues arise when foo1.h includes both bar.h and foo.h

Header guards only prevent a .h file from including its contents twice or more within one top-level translation unit. They are to deal with cases like this, where two or more headers need to include the same set of common definitions:

// 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

But each translation unit starts out with a clean macro environment, so if you include a header file in two different top-level translation units, that header's declarations are made visible (once) to each. And that's what you want. (Think about standard library headers. You wouldn't want stdio.h not to declare printf in bar.c just because there existed foo.c in the same project that also included stdio.h .)

Now, your problem is that ov5642_Sensor_Values.h defines a data object (not a type), ov5642_1280x960_RAW , and this header is included into two different top-level translation units ( .c source files). Each translation unit produces an object file containing a definition of ov5642_1280x960_RAW , and you get a multiple definition error from the linker when you try to combine them.

The bug causing this problem is not that ov5642_Sensor_Values.h 's header guards are ineffective. The bug is that ov5642_Sensor_Values.h should not be making any global definitions. Header files should only declare things (with rare exceptions that you shouldn't worry about until you encounter them).

To fix the bug, change ov5642_Sensor_Values.h to declare ov5642_1280x960_RAW but not define it, like this:

#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

and create a new file named ov5642_Sensor_Values.c that contains the initialized definition:

#include "ov5642_Sensor_Values.h"

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

and add that file to your link.

thank you everyone especially John, Zwoi. I was up against a deadline (this was inherited code) but was able to calm myself down enough to figure out what John was saying. I moved the struct definition into ac file and used an extern pointer in the header which is similar to zwoi's declaration. I am also relieved that what I did matched zwoi's example (thank you so much!).

extern const struct sensor_reg * ov5642_1280x960_RAW;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM