C/C++ header files and methods to avoid redefinition caused by header file inclusion

C header files

A header file is a file with the extension  .h  that contains C function declarations and macro definitions, and is referenced and shared by multiple source files. There are two types of header files: header files written by the programmer and header files that come with the compiler.

To use a header file in a program, you need to use the C preprocessing directive  #include  to reference it. We have seen  the stdio.h  header file earlier, which is the header file that comes with the compiler.

Referencing a header file is equivalent to copying the content of the header file, but we don't copy the content of the header file directly in the source file, because doing so is error-prone, especially when the program is composed of multiple source files.

A simple practice in C or C++ programs, it is recommended to write all constants, macros, system global variables and function prototypes in header files, and refer to these header files whenever needed.

Syntax for referencing header files

 User and system header files can be referenced using the preprocessing directive  #include . It has the following two forms:

#include <file>

This form is used to refer to system header files. It searches the standard listing of system directories for a file named file. When compiling source code, you can prepend directories to this list with the -I option.

#include "file"

This form is used to refer to user header files. It searches the directory containing the current file for a file named file. When compiling source code, you can prepend directories to this list with the -I option.

Actions for referencing header files

The #include  directive instructs the C preprocessor to browse the specified file as input. The output of the preprocessor consists of the output that has been generated, the output generated by the referenced file, and  the text output after the #include  directive. For example, if you have a header file header.h like this:

char *test (void);

and a main program  program.c that uses header files, as follows:

int x;
#include "header.h"

int main (void)
{
   puts (test ());
}

The compiler will see the following code information:

int x;
char *test (void);

int main (void)
{
   puts (test ());
}

Only reference the header file once

If a header file is referenced twice, the compiler will process the contents of the header file twice, which will generate an error. To prevent this, the standard practice is to put the entire contents of the file in a conditional compilation statement, like this:

#ifndef HEADER_FILE
#define HEADER_FILE

the entire header file file

#endif

This construct is what is commonly referred to as a wrapper  #ifndef . When the header file is referenced again, the condition is false because HEADER_FILE is defined. At this point, the preprocessor skips the entire contents of the file and the compiler ignores it.

conditional reference

Sometimes it is necessary to select one of several different header files to reference into a program. For example, you need to specify configuration parameters that are used on different operating systems. You can do this with a series of conditions, as follows:

#if SYSTEM_1
   # include "system_1.h"
#elif SYSTEM_2
   # include "system_2.h"
#elif SYSTEM_3
   ...
#endif

But if there are many header files, it is not appropriate to do so, the preprocessor uses macros to define the name of the header file. This is called a conditional reference . Instead of taking the name of the header file as  a direct argument to #include  , you just need to use the macro name instead:

1  #define SYSTEM_H "system_1.h"
2  ...
3  #include SYSTEM_H

SYSTEM_H is expanded and the preprocessor looks for system_1.h as  the #include  was originally written. SYSTEM_H can be defined by your Makefile with the -D option.

In a period of programming, redefinition problems are often encountered. Generally, the relationship between #include and repeated inclusion occurs when the header.h file is included. If you are lucky, you can easily find the problem. If you are unlucky, you have to list the inclusion relationships in all the header files .h, and check one by one what went wrong. Recently discovered that if you follow the principle of " don't include header file .h in header file .h anymore ", you can fundamentally avoid this problem. Although this will make the required header file .h must be included in the code file .c or .cpp, and also note that there may be order problems when including, but this is more than looking for where to redefine It is much simpler and makes the containment relationship clearer.

All the files in the original project have been modified according to the above principles, and no adverse effects have been found so far, which is not bad.

In addition, you can also add #pragma once to the precompiled area to prevent redefinition,

Note the usage of #ifndef... #define... #end if and extern

What is the difference between the usage of #ifndef and #pragma once?

The sample code is as follows:

 method one:

 #ifndef  __SOMEFILE_H__

#define   __SOMEFILE_H__

 ... ... // declaration, definition statement

#endif

   

Method two:

#pragma once

 ... ... // declaration, definition statement

What are the characteristics of the two?

(1)#ifndef

  The way of #ifndef is supported by the C/C++ language standard. It not only ensures that the same file is not included multiple times, but also ensures that two files (or code snippets) with the exact same content are not accidentally included at the same time.

  Of course, the disadvantage is that if the macro names in different header files accidentally "collide", it may lead to you seeing that the header file clearly exists, but the compiler insists that the declaration cannot be found - this situation is sometimes very annoying depressed.

  Since the compiler needs to open the header file every time to determine whether there are duplicate definitions, ifndef will make the compilation time relatively long when compiling large projects, so some compilers gradually begin to support the #pragma once method.

(2)#pragma once

  #pragma once is generally guaranteed by the compiler: the same file will not be included more than once. Note that the "same file" mentioned here refers to one physical file, not two files with the same content.

  You cannot make a pragma once statement for a piece of code in a header file, only for files.

  The advantage is that you no longer have to worry about macro name conflicts, and of course there will be no strange problems caused by macro name conflicts. The compilation speed of large projects has also been improved a little.

  The corresponding disadvantage is that if there are multiple copies of a header file, this method cannot guarantee that they will not be included repeatedly. Of course, compared to the "declaration not found" problem caused by conflicting macro names, such duplicate inclusions are easier to spot and fix.

  Also, this way does not support cross-platform!

What is the connection between the two?

 The #pragma once approach came after #ifndef, so many people probably haven't even heard of it. It seems that #ifndef is more respected at the moment. Because #ifndef is supported by the C/C++ language standard and is not subject to any restrictions by the compiler;

 The #pragma once method is not supported by some older compilers, and some compilers that support it plan to remove it, so its compatibility may not be good enough.

 Generally speaking, when programmers hear such words, they will choose the #ifndef method. In order to try to make their code "live" longer, they usually prefer to reduce some compilation performance. This is the personality of the programmer. Of course, this is off-topic. La.

 Also saw a usage that puts the two together:

     #pragma once

   #ifndef __SOMEFILE_H__

   #define __SOMEFILE_H__

   ... ... // declaration, definition statement

   #endif

Summarize:

        It seems like it wants to have the best of both worlds. However, as long as #ifndef is used, there is a danger of macro name conflict, and it is inevitable that compilers that do not support #pragma once will report errors, so mixing the two methods does not seem to bring more benefits, but it will make some unfamiliar people. Confused.

        Which method to choose should be determined according to the specific situation after understanding the two methods. Either way is acceptable in my opinion, as long as there is a reasonable convention to avoid the drawbacks. And this is no longer the responsibility of the standard or the compiler. It should be done by the programmers themselves or by a small-scale development specification.

To avoid the same file being included multiple times

1 #ifndef method
2 #pragma once method

There isn't much difference between the two in terms of compilers that support them, but there are still some subtle differences between the two.
    method one:

    #ifndef __SOMEFILE_H__
    #define __SOMEFILE_H__
    ... ... // some declaration statements
    #endif

    Method two:

    #pragma once
    ... ... // some declarations


    The #ifndef method relies on the macro name not colliding, which not only ensures that the same file will not be included multiple times, but also ensures that two files with the same content will not be accidentally included at the same time. Of course, the disadvantage is that if the macro names of different header files accidentally "collide", it may lead to the fact that the header file clearly exists, but the compiler insists that the declaration cannot be found.

    #pragma once is guaranteed by the compiler: the same file will not be included multiple times. Note that the "same file" mentioned here refers to one physical file, not two files with the same content. The advantage is that you don't have to think too hard about a macro name, and of course there will be no weird problems caused by macro name collisions. The corresponding disadvantage is that if there are multiple copies of a header file, this method cannot guarantee that they will not be included repeatedly. Of course, compared to the "cannot find declaration" problem caused by the collision of macro names, duplicate includes are easier to find and fix.

   The first method is supported by the language, so the portability is good, and the second method can avoid name conflicts

Guess you like

Origin blog.csdn.net/weixin_55305220/article/details/121998373