3

So I'm still getting used to modular programming, and want to make sure I'm adhering to best practices. If I have the two module header files below, will the the headers #included by each file (for example "mpi.h") be included multiple times? Is there a proper way to account for this?

Also, my module headers typically look like these examples, so any other criticism/pointers would be helpful.

/* foo.h */
#ifndef FOO_H
#define FOO_H

#include <stdlib.h>
#include "mpi.h"

void foo();

#endif

and

/* bar.h */
#ifndef BAR_H
#define BAR_H

#include <stdlib.h>
#include "mpi.h"

void bar();

#endif

And use the sample program:

/* ExampleClient.c */
#include <stdlib.h>
#include <stdio.h>
#include "mpi.h"
#include "foo.h"
#include "bar.h"

void main(int argc, char *argv[]) {
    foo();
    MPI_Func();
    bar();
    exit(0)
}
user1209326
  • 795
  • 3
  • 8
  • 17
  • 1
    You seem to already be aware of header guards. Can you clarify your question? – Mysticial Jul 18 '12 at 17:27
  • You already use guards. What is the matter? – jn1kk Jul 18 '12 at 17:27
  • 2
    libraries are not included when you include a header. headers are included. libraries are linked against. – William Pursell Jul 18 '12 at 17:27
  • I'm aware of header guards, but confused about their functionality I guess. Would the header guard stop "mpi.h" from being included multiple times, or the entire foo.h? I assumed the latter. Also, thanks for the tip @WilliamPursell – user1209326 Jul 18 '12 at 17:30
  • see also http://stackoverflow.com/a/6424524/1025391 (I think this mostly applies to C as well) – moooeeeep Jul 18 '12 at 17:33
  • You are missing a semicolon on `exit(0)`, and the call isn't necessary: See http://stackoverflow.com/a/461461. Use `return 0;` instead. – Kevin Vermeer Jul 18 '12 at 18:33

4 Answers4

5

What do you mean by 'include'? The preprocessor statement #include file copies the contents of file and replaces the statement with these contents. This happens no matter

If by 'include' you mean "the statements and symbols in these files will be parsed multiple times causing warnings and errors", then no, the include guards will prevent that.

If by 'include' you mean "some part of compiler will read some part of these files", then yes, they'll be included multiple times. The preprocessor will read the second inclusion of the file and replace it with a blank line because of the include guards, which incurs a tiny overhead (the file is already in memory). Modern compilers (GCC, not sure about others) will probably be optimized to avoid this, however, and note that the file has include guards on the first pass and simply discard future inclusions, removing the overhead - Don't worry about speed here, clarity and modularity are more important. Compilation is a time-consuming process, for sure, but #include is the least of your worries.

To better understand include guards, consider the following code sample:

#ifndef INCLUDE_GUARD
#define INCLUDE_GUARD
// Define to 1 in first block
#define GUARDED 1
#endif

#ifndef INCLUDE_GUARD
#define INCLUDE_GUARD
// Redefine to 2 in second block
#define GUARDED 2
#endif

After (the first pass of) preprocessing, what will GUARDED be defined to? The preprocessor statement #ifndef or its equivalent, #if !defined() will return false if their argument is indeed defined. Therefore, we can conclude that the second #ifndef will return false, so only the first definition of GUARDED will remain after the first pass of the preprocessor. Any instance of GUARDED remaining in the program will be replaced by 1 on the next pass.

In your example, you've got something slightly (but not much) more complicated. Expanding all the #include statements in ExampleClient.c will result in the following source: (Note: I indented it, but that's not normal style for headers and the preprocessor won't do it. I just wanted to make it more readable)

/* ExampleClient.c */
//#include <stdlib.h>
  #ifndef STDLIB_H
    #define STDLIB_H
    int abs (int number); //etc.
  #endif

//#include <stdio.h>
  #ifndef STDLIB_H
    #define STDLIB_H
    #define NULL 0 //etc.
  #endif

//#include "mpi.h"
  #ifndef MPI_H
    #define MPI_H
    void MPI_Func(void);
  #endif

//#include "foo.h"
  #ifndef FOO_H
    #define FOO_H
    //#include <stdlib.h>
      #ifndef STDLIB_H
        #define STDLIB_H
        int abs (int number); //etc.
      #endif
    //#include "mpi.h"
      #ifndef MPI_H
        #define MPI_H
        void MPI_Func(void);
      #endif
    void foo(void);
  #endif


//#include "bar.h"
  #ifndef BAR_H
    #define BAR_H
    //#include <stdlib.h>
      #ifndef STDLIB_H
        #define STDLIB_H
        int abs (int number); //etc.
      #endif
    //#include "mpi.h"
      #ifndef MPI_H
        #define MPI_H
        void MPI_Func(void);
      #endif
    void bar(void);
#endif

void main(int argc, char *argv[]) {
    foo();
    MPI_Func();
    bar();
    exit(0); // Added missing semicolon
}

Go through that code and note when various definitions are performed. The result is:

#define STDLIB_H
int abs (int number); //etc.
#define STDLIB_H
#define NULL 0 //etc.
#define MPI_H
void MPI_Func(void);
#define FOO_H
void foo(void);
#define BAR_H
void bar(void);

With respect to your request for other criticism/pointers, why are you #including stdlib.h and mpi.h in all your headers? I understand that this is a stripped down example, but in general, header files should only include files necessary for the declaration of their contents. If you use a function from stdlib or call MPI_func() in foo.c or bar.c, but the function declarations are simply void foo(void), you shouldn't include these files in the header function. For example, consider the following module:

foo.h:

#ifndef FOO_H
#define FOO_H
void foo(void);
#endif

foo.c:

#include <stdlib.h>  // Defines type size_t
#include "mpi.h"     // Declares function MPI_func()

#include "foo.h"     // Include self so type definitions and function declarations
                     // in foo.h are available to all functions in foo.c

void foo(void);
  size_t length;
  char msg[] = "Message";

  MPI_func(msg, length);
}

In this example, the implementation of foo() requires stuff from stdlib and mpi, but the definition does not. If foo() returned or required a size_t value (typedef'ed in stdlib), you'd need to #include stdlib in the .h file.

Kevin Vermeer
  • 2,736
  • 2
  • 27
  • 38
1

Mostly no, with a bit 'yes'. Your header files will be 'read' more than once but at second and later time the preprocessor will cut off all the contents. This implies that it won't waste your compiler's time and also #includes inside your #ifdef blocks will be done only once (per header file).

It's a good practice. Myself, I also add the following line before #ifdefs:

#pragma once

When supported by the particular compiler, it guarantees that the file will actually be read only once. I think it's a little bit more optimal that way.

So, to sum up:

  1. header guards like you are using prevent compiler from interpreting the header contents more than once but possibly can cause the preprocessor to parse it more than once (which is not a big problem),
  2. #pragma once causes the particular header file to be read only once.

When using both, #pragma once should be in effect if supported by compiler; if not, header guards will apply.

Michał Górny
  • 18,713
  • 5
  • 53
  • 76
  • 1
    Modern compilers will optimize include guards and `#pragma once` equally. This includes GCC since the [2.x versions](http://gcc.gnu.org/onlinedocs/gcc-2.95.3/cpp_1.html#SEC8). See [#pragma once vs include guards?](http://stackoverflow.com/q/1143936) and [Is #pragma once a safe include guard?](http://stackoverflow.com/q/787533) for more details. – Kevin Vermeer Jul 18 '12 at 17:41
0

1) GOOD: you have an "include guard". "stdlib.h", "mpi.h" and "void foo()" are only seen by the compiler the first time you #include "foo.h"

/* foo.h */
#ifndef FOO_H
#define FOO_H

#include <stdlib.h>
#include "mpi.h"

void foo();

#endif

2) BAD: This will #include the entire contents of "foo.h" every time you use it:

/* foo.h */
#include <stdlib.h>
#include "mpi.h"

void foo();

3) By #include", I mean "once per compilation unit" (i.e. the same .c source file).

This mainly "protects" against a header (foo.h) calling another header ("bar.h) which might recursively call the first header.

Every different compilation unit that #includes foo.h will always get "stdlib.h", "mpi.h" and "void foo()". The point is that they'll be seen only once - not multiple times in the same compilation unit.

4) This is all "compile-time". It has nothing to do with libraries (which are "link time").

paulsm4
  • 114,292
  • 17
  • 138
  • 190
0

Yes, mpi.h will be included multiple times (as will stdlib.h); if mpi.h has include guards similar to foo.h and bar.h, then it won't be an issue.

John Bode
  • 119,563
  • 19
  • 122
  • 198