头文件被重复包含,嵌套包含的两层含义

http://www.cnblogs.com/bluestorm/archive/2011/11/04/2298126.html

说明:

写代码的时候头文件命名知道要加

#ifndef xxxx
#define xxxx

#endif

但是我把实现都放在了 .h文件中,然后出错了...所以头文件包含,只知其一不知其二,恩,就是所谓的

//a.h
#ifndef _A_H_
#define _A_H_
int max(int a, int b)
{
    return a > b ? a:b;
}

#endif

//b.h
#include "a.h"

//c.h
#include "a.h"

//main.cpp
#include "b.h"     //在这里会展开一个a.h中的max函数
#include "c.h"     //在这里会展开一个a.h中的max函数
//也就是说在main.cpp中展开了两个max函数 ,所以编译的时候肯定会报错

讲解:

#include文件的一个不利之处在于一个头文件可能会被多次包含,为了说明这种错误,考虑下面的代码:  //在一个文件中例如main.cpp中写了两次#include"x.h"

------main.cpp-------

#include "x.h"
#include "x.h"

显然,这里文件x.h被包含了两次,没有人会故意编写这样的代码。但是下面的代码:

------a.h------

#include "x.h"

------b.h------

#include "x.h"

-----main.cpp-----

#include "a.h"
#include "b.h"

看上去没什么问题。如果a.h和b.h都包含了一个头文件x.h。那么x.h在此也同样被包含了两次,只不过它的形式不是那么明显而已。

多重包含在绝大多数情况下出现在大型程序中,它往往需要使用很多头文件,因此要发现重复包含并不容易。要解决这个问题,我们可以使用条件编译。
#ifndef _HEADERNAME_H
#define _HEADERNAME_H

...//(头文件内容)

#endif

那么多重包含的危险就被消除了。当头文件第一次被包含时,它被正常处理,符号_HEADERNAME_H被定义为1。如果头文件被再次包含,通过条件编译,它的内容被忽略。符号_HEADERNAME_H按照被包含头文件的文件名进行取名,以避免由于其他头文件使用相同的符号而引起的冲突。

但是,你必须记住预处理器仍将整个头文件读入,即使这个头文件所有内容将被忽略。由于这种处理将托慢编译速度,所以如果可能,应该避免出现多重包含。

#ifndef只是防止了头文件被重复包含,但是无法防止变量被重复定义。

 1 # vi test.h
 2 -------------------------------
 3 #ifndef _TEST_H_
 4 #define _TEST_H_
 5
 6 char add1[] = "www.shellbox.cn/n";
 7 char add2[] = "www.scriptbox.cn/n";
 8 int i = 10;
 9 void test1();
10 void test2();
11
12 #endif

 1 # vi test.c
 2 -------------------------------
 3 #include <stdio.h>
 4 #include "test.h"
 5
 6 extern i;
 7 extern void test1();
 8 extern void test2();
 9
10 int main()
11 {
12    test1();
13    printf("ok/n");
14    test2();
15    printf("%d/n",i);
16    return 0;
17 }

 1 # vi test1.c
 2 -------------------------------
 3 #include <stdio.h>
 4 #include "test.h"
 5
 6 extern char add1[];
 7
 8 void test1()
 9 {
10    printf(add1);
11 }

 1 # vi test2.c
 2 -------------------------------
 3 #include <stdio.h>
 4 #include "test.h"
 5
 6 extern char add2[];
 7 extern i;
 8
 9 void test2()
10 {
11    printf(add2);
12    for (; i > 0; i--)
13        printf("%d-", i);
14 }

   

# Makefile
-------------------------------
test:    test.o test1.o test2.o
test1.o: test1.c
test2.o: test2.c
clean:
   rm test test.o test1.o test2.o

错误:
test-1.0编译后会出现"multiple definition of"错误。

错误分析:
由于工程中的每个.c文件都是独立的解释的即使头文件有
#ifndef _TEST_H_
#define _TEST_H_
....
#enfif
在其他文件中只要包含了global.h就会独立的解释,然后每个.c文件生成独立的标示符。在编译器链接时,就会将工程中所有的符号整合在一起,由于文件中有重名变量,于是就出现了重复定义的错误。

解决方法
在.c文件中声明变量,然后建一个头文件(.h文件)在所有的变量声明前加上extern,注意这里不要对变量进行初始化。然后在其他需要使用全局变量的.c文件中包含.h文件。编译器会为.c生成目标文件,然后链接时,如果该.c文件使用了全局变量,链接器就会链接到此.c文件 。

 1 # vi test.h
 2 -------------------------------
 3 #ifndef _TEST_H_
 4 #define _TEST_H_
 5
 6 extern i;
 7 extern char add1[];
 8 extern char add2[];
 9
10 void test1();
11 void test2();
12
13 #endif

 1 # vi test.c
 2 -------------------------------
 3 #include <stdio.h>
 4 #include "test.h"
 5
 6 int i = 10;
 7 char add1[] = "www.shellbox.cn/n";
 8 char add2[] = "www.scriptbox.cn/n";
 9 extern void test1();
10 extern void test2();
11
12 int main()
13 {
14    test1();
15    printf("ok/n");
16    test2();
17    printf("%d/n",i);
18    return 0;
19 }

1 # vi test1.c
2 -------------------------------
3 #include <stdio.h>
4 #include "test.h"
5
6 void test1()
7 {
8    printf(add1);
9 }

 1 # vi test2.c
 2 -------------------------------
 3 #include <stdio.h>
 4 #include "test.h"
 5
 6 void test2()
 7 {
 8    printf(add2);
 9    for (; i > 0; i--)
10        printf("%d-", i);
11
12 }

二、链接指示符:extern

如果希望调用其他程序设计语言(尤其是C)写的函数,那么,调用函数时必须告诉编译器使用不同的要求.例如,当这样的函数被调用时,函数名或参数排列的顺序可能不同,无论是C++函数调用它,还是用其他语言写的函数调用它.
    程序员用链接指示符(linkage directive)告诉编译器,该函数是用其他的程序设计语言编写的.

链接指示符有两种形式:
    单一语句(single statement)形式
    复合语句(compound statement)形式

当复合语句链接指示符的括号中包含有#include时,在头文件中的函数声明都被假定是用链接指示符的程序设计语言所写的.

链接指示符不能出现在函数体中.

 1 vi externC.cpp
 2 -------------------------------------
 3 #include <iostream>
 4 extern "C" double sqrt(double);
 5 int main()
 6 {
 7     using std::cout;
 8     using std::endl;
 9     double result = sqrt(25);
10     cout << "result = " << result << endl;
11     return 0;
12 }

编译:    g++ externC.cpp

如果我们希望C++函数能够为C程序所用,我们也可以使用extern "C"链接指示符来使C++函数为C程序可用.

作为一种面向对象的语言,C++支持函数重载,而过程式语言C则不支持。函数被C++编译后在symbol库中的名字与C语言的不同。

例如,假设某个函数的原型为:void foo(int x, int y); 
C编译器编译后在symbol库中的名字为_foo

C++编译器则会产生像_foo_int_int之类的名字_foo_int_int这样的名字包含了函数名和函数参数数量及类型信息,C++就是靠这种机制来实现函数重载的。

为了实现C和C++的混合编程,C++提供了C链接交换指定符号extern "C"来解决名字匹配问题函数声明前加上extern "C"后,则编译器就会按照C语言的方式将该函数编译为_foo,这样C语言中就可以调用C++的函数了

1 cppExample.h
2 -----------------------------------
3 #ifndef CPP_EXAMPLE_H
4 #define CPP_EXAMPLE_H
5 //被extern "C"限定的函数或变量首先是extern类型的;  //extern是C/C++语言中表明函数和全局变量作用范围(可见性)的关键字  //该关键字告诉编译器,其声明的函数和变量可以在本模块或其它模块中使用.
6 //被extern "C"修饰的变量和函数是按照C语言方式编译和连接的;
7 extern "C" int add(int x, int y);
8 //extern int add(int x, int y);
9 #endif

1 cppExample.cpp
2 -----------------------------------
3 #include "cppExample.h"
4
5 int add( int x, int y )
6 {
7     return x + y;
8 }

 1 cFile.c
 2 -----------------------------------
 3 #include <stdio.h>
 4 //这样会编译出错
 5 //#include "cppExample.h"
 6
 7 extern int add(int x, int y);
 8
 9 int main(int argc, char* argv[])
10 {
11     printf("%d/n", add(2, 3));
12     return 0;
13 }

 1 gcc cFile.c cppExample.cpp 

三、变量定义与声明的区别

我们在程序设计中,时时刻刻都用到变量的定义和变量的声明,可有些时候我们对这个概念不是很清楚,知道它是怎么用,但却不知是怎么一会事,下面我就简单的把他们的区别介绍如下:

变量的声明有两种情况:
    (1) 一种是需要建立存储空间的(定义、声明)。例如:int a在声明的时候就已经建立了存储空间。 
    (2) 另一种是不需要建立存储空间的(声明)。例如:extern int a其中变量a是在别的文件中定义的
    前者是"定义性声明(defining declaration)"或者称为"定义(definition)",而后者是"引用性声明(referncing declaration)"。从广义的角度来讲声明中包含着定义,但是并非所有的声明都是定义,例如:int a它既是声明,同时又是定义。然而对于extern a来讲它只是声明不是定义。一般的情况下我们常常这样叙述,把建立空间的声明称之为"定义",而把不需要建立存储空间称之为"声明"。很明显我们在这里指的声明是范围比较窄的,也就是说非定义性质的声明。

例如:在主函数中 
int main()
{
    extern int A; //这是个声明而不是定义,声明A是一个已经定义了的外部变量
                  //注意:声明外部变量时可以把变量类型去掉如:extern A;
    dosth();      //执行函数
}

int A;            //是定义,定义了A为整型的外部变量(全局变量)

外部变量(全局变量)的"定义"与外部变量的"声明"是不相同的,外部变量的定义只能有一次,它的位置是在所有函数之外,而同一个文件中的外部变量声明可以是多次的,它可以在函数之内(哪个函数要用就在那个函数中声明)也可以在函数之外(在外部变量的定义点之前)。系统会根据外部变量的定义(而不是根据外部变量的声明)分配存储空间的。对于外部变量来讲,初始化只能是在"定义"中进行,而不是在"声明"中。所谓的"声明",其作用,是声明该变量是一个已在后面定义过的外部变量,仅仅是在为了"提前"引用该变量而作的"声明"而已。extern只作声明,不作定义。

用static来声明一个变量的作用有二:
    (1) 对于局部变量用static声明,则是为该变量分配的空间在整个程序的执行期内都始终存在
    (2) 外部变量用static来声明,则该变量的作用只限于本文件模块

时间: 2024-10-14 05:06:10

头文件被重复包含,嵌套包含的两层含义的相关文章

防止头文件的重复包含问题

在我们自己编写 C/C++的头文件时,可能会忽略一点:用一些处理机制来避免头文件的重复包含,因为头文件的内容在预编译时是把头文件的内容完全拷贝到引入的地方替换头文件的包含命令,而包含的头文件可能有包含很多内容,所以要是重复包含头文件,可能会使预编译后的源文件代码冗余量很大,造成空间上的浪费. 1. #pragma once 2. #ifndef #define #endif 它们具体实现如下: 假定此时要保证头文件HeadFile.h不会被重复包含,那么两种方法对应的方式如下: 1. #prag

两种解决头文件被重复包含方法的联系与区别

在制作C/C++项目的过程中,应该会遇到关于头文件被重复包含的问题,几乎每一个C/C++程序员都应该知道如何来解决这一问题.通常来说,我们通常可以用两种方式来解决这一问题. 第一种 ---- 利用以下形式: #ifndef  __XX_H__                                                                              #ifndef   XX_H #define __XX_H__                    

#ifndef #define #endif 防止头文件被重复引用

想必很多人都看过“头文件中的 #ifndef/#define/#endif 防止该头文件被重复引用”.但是是否能理解“被重复引用”是什么意思?是不能在不同的两个文件中使用include来包含这个头文件吗?如果头文件被重复引用了,会产生什么后果?是不是所有的头文件中都要加入#ifndef/#define/#endif 这些代码?   其实“被重复引用”是指一个头文件在同一个cpp文件中被include了多次,这种错误常常是由于include嵌套造成的.比如:存在a.h文件#include "c.h

考题一:研究对全排列着色的性质 问题 A: 首先需要生成 n 的全排列然后对 n 的全排列进行着色, 使得相邻的两个数只需用最少颜色就可以把相邻的两个数用那区分开. (这里相邻包含两层含义:同时在自然

问题: (用C++实现)     **研究对全排列着色的性质. 首先需要生成n的全排列 然后对n的全排列进行着色, 使得相邻的两个数只需用最少颜色就可以把相邻的两个数用那区分开.  (这里相邻包含两层含义:同时在自然顺序和在当前排列的顺序中) 最后, 对着色的结果进行统计 结果需要 给定n,找出所有需要2种颜色的排列. 需要3种颜色的排列 需要4种颜色的排列 (已经证明最多只需要4色) (在第一问基础上)第二问: 需要找出需要4色的规律. 发现需要 4色的排列里面 有一些可以用以下个模型来表示(

函数放在头文件中被多次包含的重定义问题

Owed by: 春夜喜雨 http://blog.csdn.net/chunyexiyu 转载请标明来源 例如一个头文件headfile.h这样写 #pragma once bool Func (){return true;} 在这个头文件被多个地方包含的时候就会出问题,链接时报错: (FuncB报重定义) "fatal error LNK1169: 找到一个或多个多重定义的符号" 原因是,在headfile.h中定义了函数及其实现,如果被包含时,则会把函数实现放入包含的位置,被包含

前端技术:HTML---Head标签中包含的头文件标签,body标签包含的内部标签

1.Head标签中包含的 头文件标签的作用: (1)title标签:定义网页的标题. (2)meta标签:一般用于定义页面的特殊信息,例如页面的关键字.页面描述等 (3)link标签:用于引入外部样式文件(CSS 文件). (4)style标签:用于定义元素的CSS样式. (5)script标签:用于定义页面的JavaScript代码,或者引入外部JavaScript文件. meta标签包含的属性 (1)charset属性:字符集编码方式: ASCII:数字.英文字母.字符进行编码 GB2312

extern 用法,全局变量与头文件(重复定义)

http://www.cnblogs.com/chengmin/archive/2011/09/26/2192008.html 用#include可以包含其他头文件中变量.函数的声明,为什么还要extern关键字,如果我想引用一个全局变量或函数a,我只要直接在源文件中包含#include<xxx.h> (xxx.h包含了a的声明)不就可以了么,为什么还要用extern呢??这个问题一直也是似是而非的困扰着我许多年了,今天上网狠狠查了一下总算小有所获了: 头文件 首先说下头文件,其实头文件对计算

【转载】防止变量重复定义、头文件重复包含、嵌套包含

[转自] http://hi.baidu.com/zengzhaonong/blog/item/8a8871062d481f7f03088106.html #include文件的一个不利之处在于一个头文件可能会被多次包含,为了说明这种错误,考虑下面的代码: #include "x.h"#include "x.h" 显然,这里文件x.h被包含了两次,没有人会故意编写这样的代码.但是下面的代码:#include "a.h"#include "

#ifndef#define#endif防止头文件重复包含

#ifndef 主要目的:防止头文件的重复包含和编译. 如果在h文件中定义了全局变量,一个c文件包含同一个h文件多次,如果不加#ifndef宏定义,会出现变量重复定义的错误:如果加了#ifndef,则不会出现这种错误. 1 #ifndef x //先测试x是否被宏定义过 2 #define x 3 程序段1 //如果x没有被宏定义过,定义x,并编译程序段 1 4 #endif 5 程序段2 //如果x已经定义过了则编译程序段2的语句,"忽视"程序段 1