C header file

C header file

A header file is a file with an extension of .h, which contains C function declarations and macro definitions, and is shared by multiple source files. There are two types of header files: header files written by programmers and header files that come with compilers.

To use a header file in a program, it needs to be referenced using the C preprocessing directive #include. Earlier we have seen the stdio.h header file, 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 will not copy the content of the header file directly in the source file, because it is easy to make mistakes, especially when the program is composed of multiple source files.

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

Syntax for referencing header files

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

#include <file>

This form is used to refer to system header files. It searches a standard list 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 for a file named file in the directory containing the current file. When compiling source code, you can prepend directories to this list with the -I option.

Operations that reference header files

The #include directive instructs the C preprocessor to look for the specified file as input. The output of the preprocessor includes the generated output, 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 as follows:

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

The role of the header file

In the C language, each source file is a module, and the header file provides an interface for users who use the module. Interface refers to the method that a functional module exposes to other modules to access specific functions.

Use source files to implement the functions of the module, and use header files to expose the interface of the unit. Users only need to include the corresponding header file to use the interface exposed in the header file.

Linking each functional module in the program through the method included in the header file is beneficial to modular programming design:

1) Call the library function through the header file. In many occasions, the source code is inconvenient (or not allowed) to be released to the user, as long as the header file and binary library are provided to the user. Users only need to call the library functions according to the interface declaration in the header file, and don't need to care about how the interface is implemented. The compiler extracts the corresponding code from the library.

2) Header files can strengthen type safety checks. If an interface is implemented or used in a way that is inconsistent with the declaration in the header file, the compiler will point out an error. This simple rule can greatly reduce the programmer's burden of debugging and correcting errors.

In the preprocessing stage, the compiler copies the contents of the header file included in the source file to the include statement (#include). When the source file is compiled, it is compiled together with the content of the included header file to generate an object file (.obj).
If the included header file is very large, it will seriously slow down the compilation speed (use the -E option of GCC to obtain and view the final preprocessed file). Therefore, only the necessary header files should be included in the source file, and try not to include other header files in the header file.

Header File Organization Principles

The definition of variables and functions is implemented in the source file, and the link scope is specified. In the header file, write the definitions of global variables, function declarations, data types and macros that need to be used externally.

The following guidelines are recommended for organizing header file content:

  • The principle of header file division: type definitions and macro definitions should be separated from function declarations as much as possible, and they should be located in different header files. The inner function declaration header file is separated from the outer function declaration header file, and the inner type definition header file is separated from the outer type definition header file.

Note that sometimes the type and macro definition cannot be split into different files, for example, when the number of elements of an array member in a structure is represented by a constant macro. Therefore, only type macro definitions and function declarations are separated, and placed in *.th and *.fh files respectively (not mandatory).

  • Semantic hierarchy principle of header files: header files need to have semantic hierarchy. Type definitions of different semantic levels should not be placed in one header file, and function declarations of different levels should not be placed in one header file.

  • The principle of semantic relevance of header files: type definitions and function declarations appearing in the same header file should be semantically related and have internal logical relationships, and avoid putting irrelevant definitions and declarations in one header file.

  • The name of the header file should be the same as the source file that implements the function, namely module.c and module.h. But a source file does not have to include a header file of the same name.

  • Header files should not contain local data to reduce inter-module coupling.
    That is, only the types, macro definitions, variables, and function declarations used by the source file itself should not appear in the header file. Private variables and functions whose scope is limited to a single file should be declared static to prevent external calls. Placing private types in source files increases cohesion and reduces unnecessary formatting leakage.

  • Variables and functions are not allowed to be defined in the header file, only macros, types (typedef/struct/union/enum, etc.) and declarations of variables and functions.
    In special cases, the global variable of the basic type can be extern, and the source file can access the global variable by including the header file. However, the header file should not extern the global variable of the custom type (such as structure), otherwise it will force the source file that does not need to access the variable to include the header file [1] where the custom type is located.

  • A descriptive header file does not need to have a corresponding source file. Most of these header files contain a large number of conceptual macro definitions or enumeration type definitions, and do not contain any other type definitions and variable or function declarations. Such header files should also not include any other header files.

  • Use #pragma once or header guard (also known as include guard or macro guard) to avoid repeated inclusion of header files. #pragma once is a non-standard but widely supported trick by modern compilers that explicitly tells the preprocessor "don't double-include the current header file". The header guard simulates similar behavior through preprocessing commands:

insert image description here

Using #pragma once has two advantages over header guard:

① Faster. The compiler will not read the file marked #pragma once for the second time, but it will read the file using header guard several times (look for #endif);

②Easier. It is no longer necessary to name the header guard of each file, avoiding the "cannot find declaration" problem caused by duplicate macro names.

The disadvantages are:

#pragma once保证物理上的同一个文件不会被包含多次,无法对头文件中的一段代码作#pragma once声明。若某个头文件具有多份拷贝(内容相同的多个文件),pragma不能保证它们不被重复包含。当然,这种重复包含很容易被发现并修正。
  • When a C function is to be referenced in C++, extern "C" should be included in the header file where the function is located.

insert image description here
Variables and functions modified by extern "C" will be compiled and linked in C language, otherwise the compiler will not be able to find the C function definition, resulting in link failure.

  • There must be sufficient user-oriented comments in the header file to describe the content exposed by the interface from the application point of view.

Guess you like

Origin blog.csdn.net/QYgujingjing/article/details/129906652