2013-12-20 27 views
0

我想了解multiple-include optimization如何与gcc一起使用。 最近,我一直在读那有包括标准的头文件警卫像这样多包含优化

#ifndef _STDIO_H_ 
#include <stdio.h> 
#endif 

大量的代码,我试图找出是否这个结构有什么好处。

下面是我写的一个例子,以更好地理解这一点。

那么header1.h

#ifndef _HDR_H_ 
#define _HDR_H_ 

#define A (32) 

#endif 

header2.h

#ifndef _HDR_H_ 
#define _HDR_H_ 

#define A (64) 

#endif 

hdr.c

#include <stdio.h> 
#include "header1.h" 
#include "header2.h" 

int main() 
{ 
    printf("%d\n", A); 
    return 0; 
} 

注意两个header1.hheader2.h使用相同的包括后卫。如预期的那样,该程序输出在header1.h中定义的值A;由于它使用相同的包含保护,header2.h被跳过。

这里就是我想了解

  • 在什么时候,当解析header2.h不预处理跳过这个文件?我的理解是,它在第​​1行的#if指令之后立即跳过此文件,即它不必等待匹配的#endif。它是否正确?
  • 我可以在上面的例子中添加什么来演示这是如何工作的?

编辑:谢谢大家的答案。现在开始变得更有意义。后续问题。链接到这篇文章的第一行中的页面有如下文字

预处理通知这样的头文件,因此,如果头文件 出现在随后的#include指令和FOO的定义,那么它 被忽略,并且它不会预处理或者甚至重新打开该文件第二个 时间。这被称为多重包含优化。

如果我的理解正确,这意味着任何头文件只读一次,即使它包含多次给定的编译过程。所以,在应用程序代码或头文件中增加的守护程序不会带来任何好处。

+0

通常我做我的侍卫包括以文件代入的大写的名字。与_这样的一个包括守卫不会在另一个文件中禁用第二个。不过,由于大多数编译器支持#pragma,一旦我在这些日子里使用包含守卫的代码。 – drescherjm

+0

以下划线开头的名称后跟一个大写字母('_HDR_H_')和包含两个连续下划线的名称将保留给实现(编译器及其头文件)。不要使用它们。 –

回答

2

在解析header2.h的时候,预处理程序跳过了这个文件?

该文件未被跳过。

我的理解是它会在第1行的#if指令之后立即跳过此文件,即它不必等待匹配的#endif。它是否正确?

是和否。一些编译器在解析第一个头文件时识别哨兵宏,如果它在第二个文件中发现它,它将立即停止解析。其他编译器会再次解析头文件(寻找匹配的#endif)。

我可以在上面的例子中添加什么来演示这是如何工作的?

添加打印信息内部和哨兵宏观

#ifdef _HEADER_INCLUDED 
#define _HEADER_INCLUDED 
... 
#pragma message ("inside sentry in " __FILE__ "\n") 
#endif //#ifdef _HEADER_INCLUDED 

#pragma message ("outside sentry in " __FILE__ "\n") 

相关材料外:

  • 可以使用#pragma once代替哨兵宏。由于很少的文件被解析,编译速度更快。不用担心宏名称冲突。
  • 您可以将包含if检查包裹到哨兵宏中,以便不再次加载头文件。这通常用于多次包含多个标头的库标头。在丑陋的代码为代价可以显著加快编译:

    #ifndef __LIST_H_

    #include "list.h"

    #endif

+1

根据https://gcc.gnu.org/onlinedocs/cppinternals/Guard-Macros.html,在'#endif'之后添加内容将完全禁用优化,从而无法观察优化如何工作。 – FRIdSUN

2

预处理器不会跳过header2.h。它将始终包含它,并且在展开时它将忽略#ifndef块中的内容。

在你的例子中,A将是32,因为herader2.h中的#define永远不会到达。如果达到了,你会得到某种“宏定义错误”,因为你有多个“A”的#define。为了解决这个问题,你需要##undef A.

现在大多数编译器都支持#pragma once指令,以免你必须在头文件中编写include guard。

4

在解析header2.h的时候,预处理器会跳过这个文件吗?

由于@Sean说:header2.h永远不会被跳过,但ifndef ... endif之间的内容会在这种情况下被忽略。


可我加什么上面的例子来说明它如何工作?

header2.h添加的东西(例如,#define B 123)后#endif。现在尝试在main中访问它。它将被访问。

现在,尝试在之前添加,#endif。你会看到,它不能在`main中访问。

+0

谢谢!我确实确认它按照你的解释工作。 – dinesh

+0

根据https://gcc.gnu.org/onlinedocs/cppinternals/Guard-Macros.html,在'#endif'之后添加内容将完全禁用该优化,从而无法观察优化如何工作。 – FRIdSUN

2

预处理器开始阻止在错误#if[[n]def]后面的所有输入以进入后续编译器步骤。

然而,预处理器不会继续读取输入,以跟踪所有这些条件编译的嵌套深度 - 指令。

当它找到匹配的#endif时,它开始阻塞输入的位置,它只是停止阻塞。

0

如果我理解这个正确的,这意味着任何头文件被读对于给定的编译过程,即使它包含多次,也只需要一次。所以,在应用程序代码或头文件中增加的守护程序不会带来任何好处。

没有gcc编译器不仅会优化文件,它知道是安全的遵守规则:

  1. 必须有控制#如果 - #ENDIF对外界没有任何标记,但空白和注释被允许。
  2. 控制指令对之外不得有任何指令,但允许使用null指令(只包含单个'#'和可能为空白的行)。
  3. 开幕指令的形式必须

    #ifndef FOO 
    

    #if !defined FOO  [equivalently, #if !defined(FOO)]