Linking .h files with .c with #ifdef header guards
Asked Answered
B

4

14

im having trouble linking .h and .c files, i've also read some threads regarding this problem and all of them is a bit vague and still i can't fully grasp the concept of it, and im having a lot of linking problems, Say i have b.c and b.h which i will use in a.c, and im confused whether to include b.h both a.c and b.c cuz b.c itself needs to know the structure defined in b.h, i have some function which has its prototype in b.h and is defined in b.c which also use the structure in b.h, im am not including b.h in b.c cuz as what i know b.h is more like an interface to a.c which will use the functions in b.c... Here a more clear example

b.h file

typedef struct{
int x, y;
}myStruct;

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

b.c file

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

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

a.c file

#include "b.h"

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

Executed the command in cygwin: gcc b.c a.c -g

Now the confusing part, i have a linking error wherein when b.c is compiled it can't detect the structure and the prototypes in b.h. Cuz all i know is that b.h is used to link b.c from a.c but when both .c is compiled it seems that b.c can't find its strucutre and prototypes,

Why didn't i include b.h in b.c? Answer: Cuz as what i know, b.h is already included in a.c and when i include it again in b.c, i'll be doing double inclusions <--- thats what i learn so far and i know there is #ifdef but it seems it won't work, maybe i still don't know how to use it, if you know please feel free to discuss this.

If you have any idea as to how to go about this feel free to tell me some.

there is a #ifdef directive but i can't seem to have any idea how to do this.

NOTE: ASSUME THAT ALL ABOVE CODES IS SYNTACTICALLY CORRECT if there are any misspelled word please ignore, i'm only after the inclusions between .h and .c

Brentbrenton answered 12/1, 2013 at 3:47 Comment(0)
S
34

You do indeed need to #include b.h in b.c. Each file is compiled separately before the linker takes over, so it doesn't matter that you have included b.h in a.c, because b.c is compiled by itself and has no idea about the contents of b.h unless you include it.

Here's an example of a #include guard

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

When some_header_file.h is included anywhere, everything in between the #ifndef and the #endif will be ignored if SOME_HEADER_FILE_H has been defined, which will happen on the first time it is included in the compilation unit.

It is common practice to name the #define after the name of the file, to ensure uniqueness within your project. I like to prefix it with the name of my project or namespace as well, to reduce the risk of clashes with other code.

NOTE: The same header file CAN be included multiple times within your project even with the above include guard, it just can't be included twice within the same compilation unit. This is demonstrated as follows:

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

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

Now let's see what happens when we try to include the above two files. In a single compilation unit:

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

This generates a compiler error because test2 is not defined - it is ignored in header2.h because HEADER_H is already defined by the time that is included. Now if we include each header in separate compilation units:

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

It compiles fine and produces the expected output (1 and 2), even though we are including two files which both define HEADER_H.

Sacristy answered 12/1, 2013 at 3:54 Comment(9)
1 question, can it detect when SOME_HEADER_FILE is alreaady define in other .c file?Brentbrenton
#ifndef means if not defined and wherever you include the .h, it defines that symbol, so if you include the same .h again, the symbol is already defined.Widthwise
@lemoncodes: no, including b.h in a.c has no effect on b.c. When b.h is included into b.c for the first time, SOME_HEADER_FILE is defined within b.c only. Subsequent #ifdef and #ifndef statement for SOME_HEADER_FILE within b.c can then detect it within b.c.Roofer
ohh i get your point, hehe there are so many answer directing to the same point, so i'll just ask the same question, "hmmm what i meant is that your SOME_HEADER_FILE_His in a .h files right?, so when that .h files is included in, say a.c, and again included in b.c, can it defect B_H_INCLUDED is laready defined?"Brentbrenton
@Brentbrenton I have edited my answer to explain Remy's point further (actually I was in the middle of typing it before I noticed his comment).Sacristy
ohh you mean to say when header1.h is inlcuded in a.cpp HEADER_H only exist on that complition and when i include header2.h, which has another HEADER_H, into b.cpp,that HEADER_H that is in header1.h doesn't exist anymore?Brentbrenton
@Brentbrenton The purpose of the include guard, is to prevent the same file being included twice within the same compilation unit. E.g. a.cpp includes a.h, which includes b.h, which includes a.hSacristy
ok ok i get it, but i'm curios,how does ifdef prevent double inclusion when both HEADER_H only exist on its own compilation? when you include header1.h in two different .c files? say a.c and b.c?Brentbrenton
let us continue this discussion in chatBrentbrenton
H
3

You need to include b.h in all files that uses the structures that are defined in b.h. So you need to put a #include <b.h> in both files. To avoid that b.h is loaded several times, you need the directives #ifdef. In your case:

b.h

#ifndef B_H
#define B_H

typedef struct{
    int x, y;
}myStruct;

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

#endif

and b.c:

#include "b.h"

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

void funct2(myStruct y)
{
     //do something
} 
Hairston answered 12/1, 2013 at 3:56 Comment(6)
1 question sir, when b.h is included in a.c, does b.c detect that B_H is already defined?Brentbrenton
Yeap. The directive #define B_H defines that the variable is defined. So, when a #ifndef search for B_H, it will return false and skip all the code until #endif. It is important that the variable B_H is defined in only one .h file.Hairston
@WilliamSeitiMizuta: your "Yeap" is wrong. When b.h is included in a.c, b.c cannot detect that. B.h has to be included in b.c so B_H can be defined within b.c.Roofer
i thing @WilliamSeitiMizuta got my points im not talking about b.h im talking about B_H that is in b.hBrentbrenton
@Brentbrenton Remy is correct. If you include b.h in b.c, B_H has not been defined yet for that compilation unit, even if you've already included it in a.c. I have updated my answer to explain this further.Sacristy
I think I get what they say. The b.h is read once per compilation, ie, if you put a #include "b.c" in a.c file, the b.h is read once when you compile a.c. But, if you compile both .c files, the b.h is read for each compilation.Hairston
W
1

Proper coding would have you include b.h in b.c.

Here is a header guard that should work:

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

Put your declarations where the comment is, and include everywhere you need to.

EDIT The way I understand it, is that gcc compiles b.c first, because a.c depends on b.c. But when it compiles b.c first, b.h has not yet been included.

Widthwise answered 12/1, 2013 at 3:53 Comment(11)
the way i look at it when i compiled, it is separately compiled and linked after, so maybe it doesn't matter what the sequence is?Brentbrenton
But... the header file needs to be included in the compile step, which happens before the link step.Widthwise
Yes, all that matters is the current compilation unit (i.e. you don't want to be including the same file twice within the same unit).Sacristy
ohhh so if that is the case, can other .c file detect the, Say in your example, B_H_INCLUDED if that is already existing?Brentbrenton
Not sure exactly what you're asking in your comment, I think the answer is yes... I am not a C expert (just been doing it for 2+ years!).Widthwise
hmmm what i meant is that your B_H_INCLUDED is in a .h files right?, so when that .h files is included in, say a.c, and again included in b.c, can it defect B_H_INCLUDED is laready defined?Brentbrenton
Here is something that may help you understand... gcc runs a program, cpp, that is the preprocessor. One of the things it does, is more-or-less copy the header files into the .c files.Widthwise
as what i understand B_H_INCLUDED acts like a flag right? if so, can that flag exist if .h is included in multiple .c file?Brentbrenton
@lemoncodes: no. The contents of the .h file are copied into the contents of the .c file by the preprocessor before the compiler then processes the merged code. The "flag" will only exist on a per-file basis in the preprocessed .c files. Multiple .c files are compiled independent of each other.Roofer
i see so how does it detect that B_H_INCLUDED is already defined when that .h, where B_H_INCLUDED is coded, is included in two .c files?Brentbrenton
@Brentbrenton Isn't "multiple inclusion" when one header file gets included more than once in the same .c file? Each .c file may need a copy of the header file in it.Widthwise
R
0

You need to #include b.h in b.c. It is not just an interface for a.c, b.c needs to know the same definitions for its own code as well. Your reason for not including b.h in b.c is wrong. Each .c file is compiled separately from every other .c file. When the compiler is done with a.c, it starts over fresh with b.c. It does not matter that a.c included b.h, because b.c has no concept that a.c even exists. The purpose of a header guard is to prevent a .h file from being processed repeat times if it is included multiple times while compiling a given .c file. Without the guard, declarations would get compiled multiple times, causing errors about multiple declarations of existing symbols.

Roofer answered 12/1, 2013 at 4:0 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.