1

I wrote a minimal code to test the working of guard macros in C. I read that they prevent the header file to be read again by the compiler if it has done already. Here is my header file:

#ifndef TEST_H
#define TEST_H
#include<stdio.h>

int num=12;
#endif  

Here is my main function:

#include"headers.h"

int main()
{
    p2();
    return 0;
}

Here is the p2 function called in main():

#include"headers.h"

void p2()
{
    printf("p2 running\n");
}
  1. While compilation it is giving me error of redefinition of num. Should macro TEST_H not prevent multiple definitions error of num here ?
  2. Also if I replace int num=12; with int num; , without any other modification, It does not show any error. Should int num; must not be a definition of num(as it will be initialized to 0) and compiler should again show same error of multiple definitions of num?
Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214
atul_pant
  • 99
  • 8
  • have you tried putting a space between `#include` and filename? – Raildex Jan 29 '20 at 10:52
  • 4
    The guards only help against multiple inclusions in the same compile. If you build two separate files and both use the same same .h, you'll end up with a variable in each (except if they are tentative definitions, mentioned above). That's fine, unless you later link them together. That's when you get the error you describe. Use `extern int num;` in the .h and `int num=12;` in *one* of the .c – ikegami Jan 29 '20 at 10:53
  • (Sorry, typing with one hand atm, so not going to post a proper Answer) – ikegami Jan 29 '20 at 10:57
  • @Raildex: There is no need. A `"` or `<` character will end the `include` preprocessing token. – Eric Postpischil Jan 29 '20 at 13:30

2 Answers2

3

Let me answer to each of your questions:

  1. The macro TEST_H prevents any multiple inclusion of the content of headers.h file in a translation unit, i.e. in a C source file. In your case, it works: you have only one definition of num in each C file. The raised error probably comes from the linker, which finds two definitions of the same variable in the linked code.

  2. If you replace int num=12; with int num;, then it become a tentative definition as commented by @RobertoCaboni: to summarize, you authorize the compiler/linker to consider this instruction either as a definition (the first time it is encountered) or a declaration (the next times it is encountered). So you do not have any more error with multiple definitions. The initialization to 0 will depend on your linker configuration and/or your source code.

Laurent H.
  • 6,316
  • 1
  • 18
  • 40
  • 1
    The behavior of declaring `int num;` in multiple translation units is not defined by the C standard. Although it is initially a tentative definition, it is, per C 2018 6.9.2 2, equivalent to a regular definition, `int num = 0;`, **in each translation unit it appears in**. This results in multiple definitions, which violates 6.9 5. Although the behavior is not defined by the C standard, it is defined by various Unix tools. However, to make the program strictly conforming C in this aspect, only `extern int num;` should appear in the header, and `int num;` should be inserted in one source file. – Eric Postpischil Jan 29 '20 at 13:36
  • I fully agree with you. – Laurent H. Jan 29 '20 at 17:02
  • @ Laurent H So are the header files like stdio.h included only once in the above code? – atul_pant Jan 29 '20 at 17:39
  • Well it is included in each C file, but only once in each C file, which is the goal of the guard macros. – Laurent H. Jan 30 '20 at 07:53
0

Should macro TEST_H not prevent multiple definitions error of num here ?

No. Each c file or translation unit compiled separately and header guard avoids multiple definitions within transaction unit.

In preprocessing stage of compilation all header files declared in a transaction unit are copied,macro's are expanded and many more things are done. refer for more details.

Understand how header guard is working,

//test.h
#ifndef __TEST_H__
#define __TEST_H__
    int num=12;
#endif

If Multiple includes of same headers,

//main.c 

#include "test.h"
#include "test.h"

int main(){

}

Translates as below during per-processing stage of compilation and conditional compilation part of code will be removed.

#ifndef __TEST_H__
#define __TEST_H__
    int num=12;
#endif

//as __TEST_H__ is already defined subsequent inclusion of test.h has no effect
#ifndef __TEST_H__  
#define __TEST_H__
    int num=12;
#endif

int main(){
    
}

And Hence compiler will not detect any conflict with multiple definition of num.

Linking takes one or more object files or libraries as input and combines them to produce a single (usually executable) file. In doing so, it resolves references to external symbols, assigns final addresses to procedures/functions and variables, and revises code and data to reflect new addresses (a process called relocation). Which detects multiple instance of the same variable and throws link time error for multiple definition.

Should int num; must not be a definition of num(as it will be initialized to 0) and compiler should again show same error of multiple definitions of num?

There has to be exactly one definition of any functions or initialized global variables, but the definition of an uninitialized global variable can be treated as a tentative definition. C then allows (or at least does not forbid) different source files to have tentative definitions for the same object. (source)

TruthSeeker
  • 1,539
  • 11
  • 24