1. 程式人生 > >防止了標頭檔案被重複包含

防止了標頭檔案被重複包含

【轉自】 http://hi.baidu.com/zengzhaonong/blog/item/8a8871062d481f7f03088106.html

#include檔案的一個不利之處在於一個頭檔案可能會被多次包含,為了說明這種錯誤,考慮下面的程式碼:

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

顯然,這裡檔案x.h被包含了兩次,沒有人會故意編寫這樣的程式碼。但是下面的程式碼:
#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按照被包含標頭檔案的檔名進行取名,以避免由於其他標頭檔案使用相同的符號而引起的衝突。

但是,你必須記住前處理器仍將整個標頭檔案讀入,即使這個標頭檔案所有內容將被忽略。由於這種處理將託慢編譯速度,所以如果可能,應該避免出現多重包含。

test-1.0使用#ifndef只是防止了標頭檔案被重複包含
(其實本例中只有一個頭件,不會存在重複包含的問題),但是無法防止變數被重複定義

# vi test.c
-------------------------------
#include <stdio.h>
#include "test.h"

extern i;
extern void test1();
extern void test2();

int main()
{
   test1();
   printf("ok/n");
   test2();
   printf("%d/n",i);
   return 0;
}


# vi test.h
-------------------------------
#ifndef _TEST_H_

#define _TEST_H_

char add1[] = "www.shellbox.cn/n";
char add2[] = "www.scriptbox.cn/n";
int i = 10;
void test1();
void test2();

#endif



# vi test1.c
-------------------------------
#include <stdio.h>
#include "test.h"

extern char add1[];

void test1()
{
   printf(add1);
}



# vi test2.c
-------------------------------
#include <stdio.h>
#include "test.h"

extern char add2[];
extern i;

void test2()
{
   printf(add2);
   for (; i > 0; i--) 
       printf("%d-", i);
}



# 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檔案 。






test-2.0

# vi test.c
-------------------------------
#include <stdio.h>
#include "test.h"

int i = 10;
char add1[] = "www.shellbox.cn/n";
char add2[] = "www.scriptbox.cn/n";
extern void test1();
extern void test2();

int main()
{
   test1();
   printf("ok/n");
   test2();
   printf("%d/n",i);
   return 0;
}


# vi test.h
-------------------------------
#ifndef _TEST_H_
#define _TEST_H_


extern i;
extern char add1[];
extern char add2[];


void test1();
void test2();

#endif



# vi test1.c
-------------------------------
#include <stdio.h>
#include "test.h"

void test1()
{
   printf(add1);
}


# vi test2.c
-------------------------------
#include <stdio.h>
#include "test.h"

void test2()
{
   printf(add2);
   for (; i > 0; i--) 
       printf("%d-", i);

}

 二、連結指示符:extern

如果希望呼叫其他程式設計語言(尤其是C)寫的函式,那麼,呼叫函式時必須告訴編譯器使用不同的要求.例如,當這樣的函式被呼叫時,函式名或引數排列的順序可能不同,無論是C++函式呼叫它,還是用其他語言寫的函式呼叫它.
    程式設計師用連結指示符(linkage directive)告訴編譯器,該函式是用其他的程式設計語言編寫的.

    連結指示符有兩種形式:
    單一語句(single statement)形式
    複合語句(compound statement)形式

    當複合語句連結指示符的括號中包含有#include時,在標頭檔案中的函式宣告都被假定是用連結指示符的程式設計語言所寫的.

    連結指示符不能出現在函式體中.

vi externC.cpp
-------------------------------------
#include <iostream>
extern "C" double sqrt(double);
int main()
{
    using std::cout;
    using std::endl;
    double result = sqrt(25);
    cout << "result = " << result << endl;
    return 0;
}

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++的函數了。





cppExample.h
-----------------------------------
#ifndef CPP_EXAMPLE_H
#define CPP_EXAMPLE_H
//被extern "C"限定的函式或變數首先是extern型別的;extern是C/C++語言中表明函式和全域性變數作用範圍(可見性)的關鍵字,該關鍵字告訴編譯器,其宣告的函式和變數可以在本模組或其它模組中使用。
//被extern "C"修飾的變數和函式是按照C語言方式編譯和連線的;
extern "C" int add(int x, int y);
//extern int add(int x, int y);
#endif



cppExample.cpp
-----------------------------------
#include "cppExample.h"

int add( int x, int y )
{
    return x + y;
}



cFile.c
-----------------------------------
#include <stdio.h>
//這樣會編譯出錯
//#include "cppExample.h"

extern int add(int x, int y);

int main(int argc, char* argv[])
{
    printf("%d/n", add(2, 3));
    return 0;
}



-----------------------------------

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來宣告,則該變數的作用只限於本檔案模組

相關推薦

防止檔案重複包含

【轉自】 http://hi.baidu.com/zengzhaonong/blog/item/8a8871062d481f7f03088106.html #include檔案的一個不利之處在於一個頭檔案可能會被多次包含,為了說明這種錯誤,考慮下面的程式碼: #inc

初夏小談:C/C++防止檔案重複包含的兩種方法及區別

防止標頭檔案被重複包含的兩種方式: 1.條件編譯的巨集 2.#pragma once 先來說說第一種: #ifndef __HEADNAME__H__ #define __HEADNAME__H__ //... //標頭檔案 #endif C/C++標準支援

include怎麼關聯檔案檔案如何關聯原始檔?檔案重複包含的危害?怎麼防止重複包含

標頭檔案(.h)的功能是一般用來進行宣告的(函式,類或變數引用,巨集定義)。 原始檔(.cpp)是用來進行定義的(函式,類定義,變數定義)。 #include 是在編譯器進行編譯之前,即在預編譯時把它後面所寫的那個標頭檔案的內容,完完整整地、 一字不改地包含到當前的原始檔中來。 &nbs

如何防止檔案重複包含或引用?

一、#pragma once ( 比較常用) 只要在標頭檔案的最開始加入這條指令就能夠保證標頭檔案被編譯一次,這條指令實際上在VC6中就已經有了,但是考慮到相容性並沒有太多的使用。 #pragmaonce是編譯相關,就是說這個編譯系統上能用,但在其他編譯系統不一定可以,也就

如何防止檔案重複包含、巢狀包含

【轉自】 http://hi.baidu.com/zengzhaonong/blog/item/8a8871062d481f7f03088106.html #include檔案的一個不利之處在於一個頭檔案可能會被多次包含,為了說明這種錯誤,考慮下面的程式碼: #inclu

如何防止檔案重複包含

在windows平臺下,有一個很方便的巨集:  #pragma once 指定當前檔案在構建時只被包含(或開啟)一次,這樣就可以減少構建的時間,因為加入#pragma once後,編譯器在開啟或讀取第一個#include 模組後,就不會再開啟或讀取隨後出現的相同#includ

檔案重複包含的危害及解決辦法

從概念上講,標頭檔案的功能是一般用來進行申明的(等函式原型或變數引用,巨集定義)。C檔案是用來進行定義的(函式定義、變數定義)。#include 是在編譯器進行編譯之前,即在預編譯時把它後面所寫的那個檔案的內容,完完整整地、 一字不改地包含到當前的檔案中來。實際上C檔案中是C語言的原始碼,H標頭檔案裡也是C語

C++/C中防止檔案重複包含的解決辦法及區別

為了避免同一個檔案被include多次,C/C++中有兩種方式,一種是#ifndef方式,一種是#pragma once方式。在能夠支援這兩種方式的編譯器上,二者並沒有太大的區別,但是兩者仍然還是有一些細微的區別。  方式一:   #ifndef __SOMEFILE_H_

怎樣防止檔案重複引用

方式一:條件編譯: #ifndef #define ......//一些宣告語句 #endif 這種方式依賴於巨集名稱不能衝突,而且這種方式可以保證同一個檔案不被包含多次,也能保證內容完全相同的兩個檔案不會不小心同時包含。但是,如果巨集名稱相同,

檔案重複包含和變數的重複定義

 為什麼會出錯呢?按照條件編譯,a.h並沒有重複包含,可是還是提示變數A重複定義了。 在這裡我們要注意一點,變數,函式,類,結構體的重複定義不僅會發生在源程式編譯的時候,在目標程式連結的時候同樣也有可能發生。我們知道c/c++編譯的基本單元是.c或.cpp檔案,各個基本單元的編譯是相互獨立的,#ifndef等

防止檔案多次包含

為了避免同一個檔案被include多次 1 #ifndef方式 2 #pragma once方式 在能夠支援這兩種方式的編譯器上,二者並沒有太大的區別,但是兩者仍然還是有一些細微的區別。 方式一: #ifndef __SOMEFILE_H__ #def

檔案多次包含的解決辦法

檔案的一個不利之處在於一個頭檔案可能會被多次包含,為了說明這種錯誤,考慮下面的程式碼:#include"x.h" #include"x.h" 顯然,這裡檔案x.h被包含了兩次,沒有人會故意編寫這樣的程式碼。但是下面的程式碼:#include"a.h" #include"b.h

檔案多次包含編譯出錯

在編譯一個工程時,有多個c檔案包含同一個.h檔案 比如a.c,b.c都包含c.h(#ifndef DH #idefine DH ... #endif) 當只有a.c包含c.h時,工程沒有問題 但是a.c和b.c同時包含c.h時,工程出現了很多問題。 首先排除重複編譯的問題

C語言中自帶的檔案(.h)所包含的函式

由於之前沒有好好學習過C語言,所以對其自帶標頭檔案所包含的內容總是不清楚,每次寫程式碼都是盲目的#include很多.h,現在重新整理一下,發現了不少很好的函式,以方便複習查閱。 不完全統計,C語言標

檔案多次呼叫時的解決辦法

微控制器程式設計中有時會出現標頭檔案多次呼叫,編譯時導致結構體函式被重複定義的錯誤以STM3210X為例當頭檔案stm32f10x.h被多次呼叫時,會出現函式體被多次定義的錯誤,此時在標頭檔案中新增#ifndef __STM32F10X_H#define __STM32F10

防止變數重複定義、檔案重複包含、巢狀包含

【轉自】 http://hi.baidu.com/zengzhaonong/blog/item/8a8871062d481f7f03088106.html #include檔案的一個不利之處在於一個頭檔案可能會被多次包含,為了說明這種錯誤,考慮下面的程式碼: #include "x.h"#include "

C語言:解決多個C檔案包含同一檔案引起的檔案重複包含問題

解決多個C檔案包含同一標頭檔案引起的檔案重複包含問題,並給出全域性變數如何定義和宣告的方法. 解決方法: 1. 定義公共檔案: global.c 和global.h 其中, global.c檔案中: #include "global.h" //定義全域性變數 int g

C/C++避免檔案重複包含的方法

C/C++避免標頭檔案重複包含的方法 1. #ifndef 2. #pragma once 3. 混合使用 在實際的程式設計過程中,因為會使用多個檔案,所以在檔案中不可避免的要引入一些標頭檔案,這樣就可能會出現一個頭檔案被 include

C/C++——檔案重複包含問題

C++程式設計中,一般將類的宣告,類的定義分開,將類的宣告放在.h的標頭檔案中,而將類的定義放在.cpp的原始檔中,這樣我們的程式設計起來更加的模組化也清晰明瞭。但是,這樣的設計也會帶來一些問題,一個典型的問題就是重複定義問題。如果從一個類派生出一個類,派生類宣告時需要包含

檔案管理 使用#ifndef防止多重包含 部分標檔案用途及其中的函式介紹

       1、 在同一個檔案中只能將同一個標頭檔案包含一次。記住這個規則很容易,但很可能在不知情的情況下將標頭檔案包含多次。例如,可能使用包含了另一個頭檔案的標頭檔案。有一種標準的C++技術可以避免多次包含同一個標頭檔案。它是基於前處理器編譯指令#ifnd