0

There is a question which make me puzzle. I know it is not right to do that ,but I don't know why. And how does the #ifndef #define #endif work.How the compliers deal with the varialbles like the following "a" and "b" ;

The code is simple:(myh.h):

#ifndef P_H
#define P_H
struct P{
      int a;   
};
int b;
#endif

another file s.cpp

#include"myh.h"
P a1;

the main.cpp:

#include<iostream>
#include"myh.h"
using namespace std;

int main()
{
    P a2;
   return 0;

}  

The error is multiple definition of b; just as I know. I have two questions: 1.as some books said if you use #ifndef the compliers will not include it twice , then why the "b" seem be included twice .

  1. What is the differnce of "a" 、 "b"and "P". Why "a" and "P" have no question . I don't know wether I am right to consider "P" as a variable the same level with "b"? Or "P" is just a definition of type.

If It is the difference of local and global ,why is "P" right?

I am really puzzled. Pardern your time to help me. Thanks.

Fanl
  • 1,491
  • 2
  • 11
  • 15

4 Answers4

1

Header guards apply to each translation unit separately. You have at least two of them (s.cpp and main cpp). Hence the 'int b` is defined in these translation units.

Also notice, a preprocessor directive does not effect the compiler but the preprocessed text passed to the compiler.

Declare your variable as extern int b and have int b in (maybe) s.cpp;

Btw: The P's are actually different variables. As you say one is local in nain and the other is a global in s.cpp, only.

1

First, about the compilation:
You´ve to understand that the compiler will compile every cpp-file separately first
(before they go all together in a executable during the linking process)

If you have a file main.cpp and two times a #include <abc.h> in it,
you can prevent the second include to give errors with your #ifndef-usage.
If you have a main.cpp which includes a.h and b.h, but a.h includes b.h too,
#ifndef etc. will help too.

But: As cpp-files (together with included headers) are processed separately,
it won´t help if you include the same header file in different cpp files.

Ie. you may include headers as much as you want, but they shouldn´t contain variables.

About your second question: P is only a "description" what a "P" should be.
(every P has a int a. But at that time, there is no P.).
b is an actual int which can hold numbers...

amanuel2
  • 4,508
  • 4
  • 36
  • 67
deviantfan
  • 11,268
  • 3
  • 32
  • 49
1

This is a very common problem - it will work on some linkers (not compilers) but not others (as you have discovered).

The problem is that int b is declared in a header, which means it is declared as globally visible in every file that includes myh.h. The include guards will protect you from defining the same thing more than once in the same file but it will not protect you from defining it more than once in different files.

How to fix it

  • Don't do it. If int b does not need to be global, don't stick it in a header
  • In myh.h declare it as extern int b;. In main.cpp, declare it as int b; This is a common fix but it means that if you change the type of b, then it needs to be changed in 2 places
  • A bit more elaborate. In myh.h

    #ifdef main_c
    #define EXTERN
    #else
    #define EXTERN extern
    #endif
    EXTERN int b;
    

    In main.cpp

    #define main_c
    #include "myh.h"
    

To answer your second question, b is a global variable, P is a local variable.

cup
  • 7,589
  • 4
  • 19
  • 42
1

Here's something that will help you 'see' the problem:

First, the error is not compile time error but link time error. Try: Compile s.cpp:

g++ -c s.cpp

This should compile fine. Since s.cpp has #include "myh.h", definition of b would come in s.o

Compile main.cpp

g++ -c main.cpp

This too should compile fine. Since main.cpp has #include "myh.h", definition of b would come in main.o

Now try linking both the .o files. Easy way would be:

g++ s.o main.o

This should give you the error about multiple definition of b. Reason being the symbol b is present in both s.o and main.o. Linker gives this error.

To see what all symbols are present in a object file you can use nm command. Try:

nm s.o

Also try:

nm main.o

Both should contain a line:

00000000 B b

Now, why we don't get error for struct P or for int a inside it? Because they are not part of symbol table.

rockoder
  • 747
  • 11
  • 23