C 当我使用这样的头文件时,如何停止包含冗余头文件?
因此,我仍然习惯于模块化编程,并希望确保遵循最佳实践。如果我有下面的两个模块头文件,每个文件(例如“mpi.h”)包含的头文件C 当我使用这样的头文件时,如何停止包含冗余头文件?,c,compiler-construction,C,Compiler Construction,因此,我仍然习惯于模块化编程,并希望确保遵循最佳实践。如果我有下面的两个模块头文件,每个文件(例如“mpi.h”)包含的头文件#是否会被多次包含?有没有合适的方法来解释这一点 此外,我的模块标题通常与这些示例类似,因此任何其他批评/指点都会有所帮助 /* foo.h */ #ifndef FOO_H #define FOO_H #include <stdlib.h> #include "mpi.h" void foo(); #endif /*foo.h*/ #伊夫德夫·福安
#是否会被多次包含?有没有合适的方法来解释这一点
此外,我的模块标题通常与这些示例类似,因此任何其他批评/指点都会有所帮助
/* foo.h */
#ifndef FOO_H
#define FOO_H
#include <stdlib.h>
#include "mpi.h"
void foo();
#endif
/*foo.h*/
#伊夫德夫·福安
#定义FOO_H
#包括
#包括“mpi.h”
void foo();
#恩迪夫
及
/*bar.h*/
#ifndef BAR_H
#定义条形图
#包括
#包括“mpi.h”
空心钢筋();
#恩迪夫
并使用示例程序:
/* 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)
}
/*ExampleClient.c*/
#包括
#包括
#包括“mpi.h”
#包括“foo.h”
#包括“bar.h”
void main(int argc,char*argv[]){
foo();
MPI_Func();
bar();
出口(0)
}
基本上是否定的,还有一点“是”。您的头文件将被多次“读取”,但在第二次或以后,预处理器将切断所有内容。这意味着它不会浪费编译器的时间,而且#在#中包含#ifdef
块只执行一次(每个头文件)
这是一个很好的做法。我本人也在#ifdef
s之前添加了以下行:
#pragma once
当特定编译器支持时,它保证文件实际上只读取一次。我认为这样会更好一点
总而言之:
像您正在使用的头保护防止编译器多次解释头内容,但可能会导致预处理器多次解析它(这不是一个大问题)
#pragma once
导致特定头文件只读取一次
当同时使用这两种语言时,如果编译器支持,#pragma once
应该有效;如果没有,将使用收割台防护装置。1)好:您有一个。“stdlib.h”、“mpi.h”和“void foo()”只有在第一次包含“foo.h”时才被编译器看到”
/*foo.h*/
#伊夫德夫·福安
#定义FOO_H
#包括
#包括“mpi.h”
void foo();
#恩迪夫
2) 坏:每次使用“foo.h”时,它都会包含“foo.h”的全部内容:
/* foo.h */
#include <stdlib.h>
#include "mpi.h"
void foo();
/*foo.h*/
#包括
#包括“mpi.h”
void foo();
3) 所谓#include”,我的意思是“每个编译单元一次”(即相同的.c源文件)
这主要是“保护”一个标头(foo.h)调用另一个标头(“bar.h”),该标头可能递归调用第一个标头
#包含foo.h的每个不同编译单元将始终获得“stdlib.h”、“mpi.h”和“void foo()”。关键是它们只会在同一个编译单元中出现一次,而不是多次
4) 这都是“编译时”。它与库(即“链接时间”)无关。是的,mpi.h
将被多次包含(就像stdlib.h
一样);如果mpi.h
包含类似于foo.h
和bar.h
的防护装置,则不会出现问题 你说的“包括”是什么意思?预处理器语句#include file
复制文件的内容
,并用这些内容替换该语句。不管怎样,这种情况都会发生
如果“include”的意思是“这些文件中的语句和符号将被多次解析,从而导致警告和错误”,那么“include”防护将防止出现这种情况
如果“包含”的意思是“编译器的某些部分将读取这些文件的某些部分”,那么是的,它们将被包含多次。预处理器将读取文件的第二个包含项,并将其替换为一个空行,因为包含保护会产生很小的开销(文件已经在内存中)。但是,现代编译器(GCC,不确定其他编译器)可能会进行优化以避免这种情况,请注意,该文件在第一次运行时就包含了防护,并简单地放弃了将来的包含,从而消除了开销—这里不必担心速度,清晰性和模块性更为重要。当然,编译是一个耗时的过程,但是#include
是您最不担心的
为了更好地理解包括守护程序,请考虑下面的代码示例:
#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
在(第一次)预处理之后,GUARDED
将被定义为什么?预处理器语句#ifndef
或其等效语句#if!defined()
将返回false
,如果它们的参数确实已定义。因此,我们可以得出结论,第二个#ifndef
将返回false,因此在预处理器第一次通过后,只有第一个受保护的定义将保留。在下一次通过时,程序中剩余的受保护的实例将被1替换
在您的示例中,您得到了稍微(但不太)复杂的东西。扩展ExampleClient.c中的所有#include
语句将产生以下源代码:(注意:我缩进了它,但这不是标题的正常样式,预处理器不会这样做。我只是想让它更具可读性)
关于您对其他批评/指点的请求,为什么您在所有标题中都包含stdlib.h和mpi.h?我知道这是一个简单的示例,但一般来说,头文件应该只包含声明其内容所需的文件。如果使用stdlib中的函数或在foo.c或bar.c中调用MPI_func(),但函数声明只是void foo(void)
,则不应在头函数中包含这些文件。例如,考虑以下模块:
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);
}
#include//定义类型大小
#包括“mpi.h”//声明函数mpi_func()
#包括“foo.h”//include self-so类型定义和函数声明
//在foo.h中,对foo.c中的所有函数都可用
/* 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
}
#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);
#ifndef FOO_H
#define FOO_H
void foo(void);
#endif
#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);
}