版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)
文檔簡介
第8章源文件和程序8.1分別編譯8.2鏈接8.3頭文件的有效使用8.4命令行參數(shù)8.5程序小結(jié)練習(xí)題本章要點:
程序源文件的分別編譯;
程序的鏈接;
使用頭文件的作用與意義;
主程序和命令行參數(shù);
程序的結(jié)構(gòu)、初始化和終止。
在詳細講述本章的內(nèi)容之前,讓我們首先看一下一個C++源程序的處理過程,以此了解程序編譯、處理過程中的一些基本概念及術(shù)語。一個C++源程序的處理過程如圖8.1所示,步驟如下:
(1)當(dāng)用戶將一個C++源程序(C++源程序的后綴名為.cpp,假定某一源文件名為myprog.cpp)提交給編譯器(Compiler,假定環(huán)境為VC?6.0)時,編譯器首先對C++源程序進行預(yù)處理(Preprocess),即將程序中的#include指令中的頭文件內(nèi)容全部包含進該源文件中。經(jīng)預(yù)處理后得到的結(jié)果(文件名為myprog.i)稱為編譯單元(TranslationUnit)。每個編譯單元才是編譯器將要真正施加編譯和C++的各種語法規(guī)則所能作用之處。每個編譯單元文件是一個臨時文件,它只對編譯器可見。
(2)編譯器對所產(chǎn)生的編譯單元進行編譯,產(chǎn)生目標(biāo)文件(myprog.obj)。
(3)鏈接器(Linker,有時亦稱為裝載器Loder)將目標(biāo)文件及源程序所需的庫資源(如庫中的類)進行鏈接。
(4)產(chǎn)生源文件的可執(zhí)行文件(myprog.exe)。圖8.1C++源程序的處理過程如果一個程序由多個源文件構(gòu)成,則鏈接器的作用除了鏈接庫外,還將多個分別編譯的源文件綁定/鏈接在一起,并對其中的各種定義(如變量、類、函數(shù)等定義)做一致性檢查。
鏈接分為靜態(tài)鏈接和動態(tài)鏈接兩種。靜態(tài)鏈接指在程序運行前便完成了鏈接任務(wù);動態(tài)鏈接指程序運行時可鏈接一些新的東西,如動態(tài)鏈接庫DLL文件等。
8.1分別編譯
在文件系統(tǒng)中,一個文件既是存儲的基本單元,亦是傳統(tǒng)的編譯單元,但將一個實用的C++源程序存儲在一個文件中通常是不可能的。一個實用的程序往往由多個源文件構(gòu)成。這是因為:
(1)實用的程序一般需要多個程序員分工合作開發(fā),多個人同時對同一個源程序文件進行編輯是不實際的且易于造成版本的混亂。
(2)實用程序應(yīng)利用多個源程序文件(程序的物理結(jié)構(gòu)(PhysicalStructure))來強調(diào)程序的邏輯結(jié)構(gòu)(LogicalStructure)。
(3)編譯程序的基本處理單元是源程序文件,其編譯開銷(即所需的編譯時間和所占用的內(nèi)存空間)與源程序文件的大小相關(guān)。因此,為了節(jié)省編譯開銷,常常需要將程序組織成多個源文件。
(4)對于有一定規(guī)模的程序,應(yīng)當(dāng)盡量避免小范圍的修改引起大范圍的重新編譯。因此,將程序以多個源文件進行組織可避免此問題。
因此,我們應(yīng)將一個實用的C++源程序劃分成多個源文件,并分別實施編譯。然而,對一個源程序各文件實施分別編譯是有條件的,即:
(1)編譯程序應(yīng)具有分別編譯的能力。
(2)在該源程序各文件中出現(xiàn)的任何符號名,程序員都在程序中給出了定義聲明、非定義聲明或者聲明的來源。
如下段代碼main函數(shù)對printf和prompt的引用都應(yīng)預(yù)先聲明后才能正確進行編譯:
extern“C”intprintf(constchar*,...); //對C庫函數(shù)printf的聲明
char*prompt=“Helloworld!\n”; //對prompt的定義聲明
intmain()
{
printf(prompt);
return0;
}
8.2鏈接
8.2.1鏈接與一致性的基本概念
所謂鏈接,即將C++程序的各個源文件綁定在一起,并進行程序中各源文件的一致性檢查。
若一個C++程序由多個源文件構(gòu)成,又各自進行了分別編譯,則鏈接器在鏈接它們時將對各源文件中聲明(定義聲明或非定義聲明)的每一個標(biāo)識符(函數(shù)名、類名、名字空間名、變量名、模板名、枚舉類型名、枚舉符等)做跨越整個C++程序各文件編譯單元的一致性檢查,除非它們被顯式地描述為是局部的。因此,為了進行一個C++程序各編譯單元的成功鏈接,程序員的任務(wù)是:
(1)保證在各編譯單元中使用的標(biāo)識符都進行了聲明。
(2)各編譯單元的聲明(定義聲明和非定義聲明)具有一致性。
(3)上述標(biāo)識符保證在各編譯單元中僅定義聲明了一次,在各編譯單元中可非定義聲明多次,但各編譯單元中的聲明必須保證類型的完全一致。如果一個名字可以用在并非其定義的編譯單元中,則稱該名字具有外部鏈接(ExternalLinkage)性,即該名字的作用域在定義的編譯單元和其它的編譯單元中。與之相反,若一個名字僅能用于它所定義的編譯單元中,則稱該名字具有內(nèi)部鏈接(InternalLinkage)性。
具有內(nèi)部鏈接性的名字有:
由const和typedef所定義的名字;
未命名的名字空間;
static變量名;
inline函數(shù)名。即上述類別的名字具有局部性(局部于特定編譯單元)。
若一個源程序由多個文件組成,各文件又進行了分別編譯,在鏈接時保證其各編譯單元中所有聲明的一致性的具體含義是:
(1)在不同的編譯單元中出現(xiàn)的同一全局名字,其定義聲明和非定義聲明要有一致的類型。
(2)在不同的編譯單元中出現(xiàn)的同一全局變量或函數(shù),在且僅在一個編譯單元中有定義,其它編譯單元均應(yīng)做“extern”聲明。
例1一個源程序由file1和file2構(gòu)成,若在file1中定義聲明了變量名x和函數(shù)名f:
file1.cpp:
intx=1;
intf(){/*...*/}
則在file2中,我們必須對x和f做非定義聲明:
file2.cpp:
externintx;
externintf();
例2
一個源程序由file3和file4構(gòu)成,若在兩文件中做了以下聲明(定義聲明或非定義聲明):
file3.cpp:
intx=1; //定義聲明
intb=1; //定義聲明
externintc; //非定義聲明
file4.cpp:
intx; //定義聲明
externdoubleb; //非定義聲明
externintc; //非定義聲明
則程序鏈接時將報錯如下:
①x在兩個編譯單元中定義聲明了兩次;
②b在定義聲明和非定義聲明中的類型不一致;
③c只有非定義聲明而沒有定義聲明。
(3)在不同編譯單元中出現(xiàn)的同一自定義類型是一致的。
例3
下述C++程序的兩個源文件(file1和file2)中所聲明的兩個自定義類型不一致。保證各編譯單元自定義類型一致的有效辦法是使得每一個類型僅在一個源文件中定義。通常采用頭文件(HeaderFiles)機制以實現(xiàn)這一目標(biāo),即將各種自定義類型寫入到特定的頭文件中,然后再利用#include指令將其包含到使用該自定義類型的各文件中。8.2.2頭文件
頭文件(Header)是一種特殊的文件,它是共享性信息的承載體,以便在不同的物理程序文件之間維護某種一致性的關(guān)系(具體示例見后續(xù)內(nèi)容)。
頭文件分為兩類:標(biāo)準(zhǔn)庫頭文件和用戶自定義頭文件。
標(biāo)準(zhǔn)庫頭文件是標(biāo)準(zhǔn)庫提供的一組頭文件,標(biāo)準(zhǔn)庫的功能亦由這一組標(biāo)準(zhǔn)頭文件給出。如C標(biāo)準(zhǔn)庫頭文件math.h、stdio.h(與之對應(yīng)的C++標(biāo)準(zhǔn)頭文件是cmath和cstdio)等。
用戶自定義頭文件就是用戶自己定義的頭文件,其中用戶存放了一些供一個C++程序各源文件共享的信息等。8.2.3#include指令
#include指令是一條預(yù)處理指令,該指令由運行環(huán)境中的預(yù)處理器進行識別與處理。
#include指令具有以下兩種形式:
#include<頭文件名>
#include“頭文件名”
這兩種形式的功能是一樣的:用查找到的頭文件的一份副本取代這條預(yù)處理指令,即把查找到的指定的頭文件內(nèi)容包含到源文件中。兩者的唯一差別是查找頭文件的路徑不同。前者在系統(tǒng)指定的路徑下查找;而后者首先在該源文件所在的路徑下查找,若找不到,則再到系統(tǒng)路徑下查找。因此,我們應(yīng)采用第一種形式來包含系統(tǒng)標(biāo)準(zhǔn)庫頭文件,而用第二種形式來包含用戶自定義的頭文件(假定用戶頭文件和其源文件放在同一目錄下)。8.2.4用戶頭文件內(nèi)容的設(shè)計
從上述的講述中知,對一個實用的、具有一定規(guī)模的程序而言,我們應(yīng)將其邏輯劃分成若干個源文件,并將各源文件中共享的信息存放到一個或多個頭文件中,以確保分別編譯和正確鏈接。
例如,為了確保8.2.1小節(jié)例3中文件file1和file2中自定義類型的一致性,我們應(yīng)將file1和file2文件中所用到的自定義類型的定義存儲在某個自定義頭文件中(假設(shè)為myheader1.h),然后在file1和file2中分別包含它們。示例代碼如下:如上所述,為了保證正確鏈接,必須遵循8.2.1節(jié)所陳述的各種原則,因此,作為首要原則,我們在設(shè)計一個頭文件時(庫頭文件/用戶自定義頭文件),其中只應(yīng)包含表8.1所示的內(nèi)容,只有這樣,才能保證在頭文件中聲明的實體在跨各編譯單元中僅定義了一次,并且其定義聲明和非定義聲明的同一實體的類型保證一致。永遠不能將表8.2所示的內(nèi)容放入到頭文件中。特別重要的是,在各編譯單元中一定要保證一次定義原則(One-DefinitionRule,ODR)。ODR的準(zhǔn)確含義是:
(1)每個實體在各編譯單元中僅定義了一次;
(2)每個實體在不同的編譯單元中被定義了多次,但它們是單詞對單詞(token-token)的完全相同;它們所指的是同一實體且含義相同。
因此,下述代碼片斷中的定義就符合一次定義原則:
//file1.cpp
structS{inta;charb;};
voidf(*S);
//file2.cpp
structS{inta;charb;};
voidf(*Sp){/*…*/}; //編譯器忽略形參參數(shù)名
而下述代碼片斷中的定義就不符合一次定義原則:
例4
//file1.cpp
structS1{inta;charb;};
//file2.c
structS1{inta;charbb;};//兩個S1非token-token一致
例5
//file1.cpp
typedefintX; //X是int的別名
structS2{Xa;charb;};
//file2.cpp
typedefcharX; //X是char的別名
structS2{Xa;charb;};//兩個S2成員的類型不一致
C++程序與C函數(shù)(或其它語言)鏈接是十分常見的。由于C++函數(shù)與C函數(shù)(或其它語言)的編譯處理不同(函數(shù)的參數(shù)表等處理規(guī)則不同),因此,必須對所鏈接的C函數(shù)(或其它語言)進行特別的聲明。例如聲明:
extern“C”char*strcpy(char*,constchar*);
extern“C”
{
intprintf(constchar*,...);
intstrlen(constchar*);
}
中的extern"C"(在某一平臺上)即是通知鏈接器以C的方式進行鏈接。
8.3頭文件的有效使用
為了有效地實現(xiàn)編譯(或分別編譯)和正確鏈接,必須使用頭文件機制,但頭文件內(nèi)容的組織及其所采用的#include指令必須恰當(dāng),否則,仍然會造成鏈接失敗。
一般,一個頭文件內(nèi)容的組織策略有兩種:
(1)將應(yīng)當(dāng)在頭文件中出現(xiàn)的定義放在同一個頭文件中。
(2)為程序的每一個邏輯單元設(shè)立一個對應(yīng)的頭文件。
第一種策略僅適合于程序規(guī)模較小并且無分別編譯的情況。實際應(yīng)用中我們常采用第二種策略。采用第二種策略的動力之一是每一個頭文件都可以獨立地起作用,這樣的頭文件本身自然被盡可能地完整化。
但這樣又有可能出現(xiàn)同一個源程序文件多次地包含了同一個頭文件,從而造成同一個類型被多次定義的冗余。解決該問題常用的方法是在頭文件中使用條件編譯指令,使得同一個頭文件只會被包含一次。例如,如果我們采用了如下頭文件組織與#include指令:則在myprog2.cpp中myheader1.h的內(nèi)容就被包含了兩次,致使myheader1.h中定義的類型在myprog2.cpp中定義了兩次。正確的做法是采用合適的條件編譯指令(條件編譯指令請參見C/C++手冊和相關(guān)書籍),即:
//myprog2.cpp:
#include“myheader1.h”
#include“myheader2.h”//注意:此時myheader1.h的內(nèi)容被包含了一次
type_a*func2(type_b&r)
{
/*...*/
}
#endif
8.4命?令?行?參?數(shù)
每個C++程序總有且僅有一個名叫main的函數(shù),并且程序是從main開始執(zhí)行的。這個函數(shù)起著C++主程序的作用。
由于main也是一個函數(shù),因此在其函數(shù)體內(nèi)定義的變量仍然是局部變量。
然而,main又是一個特殊的函數(shù),它可以接受啟動這個程序時從外部傳入的、用空格分隔的多個實參(稱為命令行參數(shù));它的返回值則回送給調(diào)用這個程序的程序(例如操作
系統(tǒng))。
因此,C++的main函數(shù)可具有如下兩種形式:
intmain() //無參的main函數(shù)
intmain(intargc,char*argv[]) //可接受命令行參數(shù)的main函數(shù)
其中:argc用于存放命令行參數(shù)的個數(shù);argv用于存放每個命令行參數(shù)(字符串)的首地址。
在C/C++程序中,argv[0]中存放的是源程序名(不帶后綴),真正的命令行參數(shù)從argv[1]開始存放。
例如,當(dāng)執(zhí)行下述程序時,若在命令行中輸入:
myprogarg1arg2arg3 //假定鏈接后的程序名為myprog.exe:
則系統(tǒng)自動賦給
argc=4
argv[0]=″myprog″
argv[1]=″arg1″
argv[2]=″arg2″
argv[3]=″arg3″
例如,打印輸出命令行的參數(shù),程序如下:
constintMAX_LEN_ARGV=80;
doublex=2;
doubley;
doublesqx=sqrt(x+y);
intmain(intargc,char*argv[])采用帶命令行參數(shù)的main函數(shù),使得我們可以更加靈活的方式啟動一個程序的執(zhí)行。
常見的典型命令行參數(shù)如下:
程序的操作對象。例如:
C>copyfile1file2
//文件拷貝,參數(shù)不同,執(zhí)行的結(jié)果不同
控制程序執(zhí)行的參數(shù)。例如:
C>dir*.cpp/o-d/p
//顯示當(dāng)前目錄下后綴為.cpp的文件,參數(shù)不同,顯示的方式不同
允許程序改變的參數(shù)。例如:
C>attrib+rmyprog.h
//設(shè)置/顯示文件屬性,參數(shù)不同,文件的屬性不同
其他不宜寫死在程序中、用文件讀入也不方便的少量參數(shù)。
8.5程序
8.5.1程序的執(zhí)行
C++的每個程序有且僅有一個main函數(shù)。
從概念上講,程序的每一次執(zhí)行,都始于main函數(shù)。但實際上在main的執(zhí)行之前系統(tǒng)就可能開始了一些動作,例如,對所有非局部量(全局量)進行實例化等。在如下的代碼段中,在執(zhí)行main之前已開始了若干動作。因此,全局量實際上在main之前就進行了初始化動作。另外,在同一個編譯單元中,非局部量的實例化順序與定義的順序相同;在不同的翻譯單元之間,實例化順序則無法規(guī)定,它與平臺相關(guān)。因此,在由多個源文件構(gòu)成的程序中,我們不能對非局部量的實例化順序做任何假定。8.5.2程序的終止
程序的終止有以下幾種方式。
從main函數(shù)執(zhí)行語句:
return<返回碼>;
從程序的某一函數(shù)中調(diào)用函數(shù):
exit(<返回碼>);//清理現(xiàn)場
從程序的某一函數(shù)中調(diào)用函數(shù):
abort(<返回碼>);//不清理現(xiàn)場
throw出一個沒有catch對應(yīng)的異常;
其它的非正常程序終止。注:<返回碼>回送給調(diào)用該程序的程序(如操作系統(tǒng)),以標(biāo)志該程序的終止?fàn)顟B(tài)。
一個真正意義上的程序應(yīng)能正常地終止程序。理解程序終止的方式有助于我們檢測和判斷程序執(zhí)行中的錯誤。小結(jié)
一個實用的C++?源程序一般都具有一定的規(guī)模,由若干個文件(
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 二零二五版機床進出口買賣合同范本詳析2篇
- 2024年禁牧區(qū)水資源保護與管理合同
- 專業(yè)化油漆施工協(xié)議(2024年版)版B版
- 二零二五年高性能環(huán)保材料供應(yīng)與技術(shù)研發(fā)協(xié)議2篇
- 天府新區(qū)職業(yè)學(xué)院《信息內(nèi)容安全的理論與應(yīng)用》2023-2024學(xué)年第一學(xué)期期末試卷
- 二零二五版快遞行業(yè)專用包裝材料供應(yīng)協(xié)議2篇
- 2024版股權(quán)投資協(xié)議(投資金額和股權(quán)比例)3篇
- 2024運輸管理實訓(xùn)機構(gòu)運營合作協(xié)議3篇
- 天津商業(yè)大學(xué)寶德學(xué)院《圖像處理與機器視覺》2023-2024學(xué)年第一學(xué)期期末試卷
- 二零二五年高端混凝土預(yù)制構(gòu)件委托加工合同3篇
- 中國人民銀行清算總中心直屬企業(yè)2023年招聘筆試上岸歷年典型考題與考點剖析附帶答案詳解
- 第5課《弘揚勞動精神勞模精神工匠精神》第1框《理解勞動精神勞模精神工匠精神》-【中職專用】《職業(yè)道德與法治》同步課堂課件
- 2025美國國防部財年美軍武器裝備采購預(yù)算中文版
- 70歲換證三力測試題附答案
- 中華醫(yī)學(xué)會利益沖突聲明模板
- 帶你玩轉(zhuǎn)VR虛擬現(xiàn)實智慧樹知到期末考試答案2024年
- DAM10KW中波發(fā)射機各單元的檢測與調(diào)整指導(dǎo)示意圖
- 物業(yè)采購工作總結(jié)
- 組織文化與領(lǐng)導(dǎo)力
- 膠粘性不良改善報告
- 河北省石家莊市橋西區(qū)2023-2024學(xué)年九年級上冊期末英語模擬試題(附答案)
評論
0/150
提交評論