use*_*326 3 c compiler-construction
所以我仍然习惯于模块化编程,并希望确保我坚持最佳实践.如果我有下面的两个模块头文件,是否会#included
多次包含每个文件的标题(例如"mpi.h")?有没有正确的方法来解释这个?
此外,我的模块标题通常看起来像这些示例,因此任何其他批评/指针都会有所帮助.
/* foo.h */
#ifndef FOO_H
#define FOO_H
#include <stdlib.h>
#include "mpi.h"
void foo();
#endif
Run Code Online (Sandbox Code Playgroud)
和
/* bar.h */
#ifndef BAR_H
#define BAR_H
#include <stdlib.h>
#include "mpi.h"
void bar();
#endif
Run Code Online (Sandbox Code Playgroud)
并使用示例程序:
/* 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)
}
Run Code Online (Sandbox Code Playgroud)
'包含'是什么意思?预处理程序语句#include file
复制内容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
Run Code Online (Sandbox Code Playgroud)
在(第一次通过)预处理之后,将GUARDED
定义什么?如果确实定义了它们的参数#ifndef
,那么预处理器语句或其等价物#if !defined()
将返回false
.因此,我们可以得出结论,第二个#ifndef
将返回false,因此只有第一个GUARDED定义将在预处理器的第一次传递后保留.GUARDED
程序中剩余的任何实例将在下一遍中替换为1.
在你的例子中,你有一些(但不是很多)更复杂的东西.扩展#include
ExampleClient.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
}
Run Code Online (Sandbox Code Playgroud)
完成该代码并记下执行各种定义的时间.结果是:
#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);
Run Code Online (Sandbox Code Playgroud)
关于你对其他批评/指针的请求,你为什么在所有标题中#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
Run Code Online (Sandbox Code Playgroud)
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);
}
Run Code Online (Sandbox Code Playgroud)
在这个例子中,执行foo()
需要来自stdlib和mpi的东西,但定义没有.如果foo()返回或需要一个size_t
值(在stdlib中为typedef'),则需要在.h文件中#include stdlib.
归档时间: |
|
查看次数: |
2367 次 |
最近记录: |