C++程序的編輯、編譯和運行(C語言的局限C++的特點C++ 程序特征)
時間:2023-11-01 來源:華清遠見
C與C++
C++是一門以C為基礎發展而來的一門面向對象的高級程序設計語言,由美國AT&T貝爾實驗室的本賈尼·斯特勞斯特盧普博士在20世紀80年代初期發明并實現(最初這種語言被稱作“C with Classes”帶類的C)。它是一種靜態數據類型檢查的、支持多重編程范式的通用程序設計語言。它支持過程化程序設計、數據抽象、面向對象程序設計、泛型程序設計等多種程序設計風格。 C++是C語言的繼承,進一步擴充和完善了C語言,成為一種面向對象的程序設計語言。
1972年,Bell實驗室的Dennis Ritchie和Ken Thompson共同發明了C語言,并使用C重寫Unix。1979年,Bjame Stroustrup到了Bell實驗室,開始從事將C改良為帶類的C(C with Classes)的工作,1983年該語言被正式命名為C++,主要意圖是表明C++是C的增強版,1985年發布了第一個C++版本。第一個版本的C++,因其面向對象的思想使得編程變得簡單,并且又保持了C語言的運行效率,在推出的一段時間內,得到了快速的發展,占據了編程語言界的半壁江山。C++從最初的C with class,經歷了從C++98、C++ 03、C++ 11、C++ 14再到C++17多次標準化改造,功能得到了極大的豐富,已經演變為一門集面向過程、面向對象、函數式、泛型和元編程等多種編程范式的復雜編程語言。
C++優勢
相對于其他的語言,C++具有它自己的優勢,主要體現在以下幾個方面:
1、C++實現了面向對象程序設計。在高級語言當中,處理運行速度是最快的,大部分的游戲軟件,系統都是由C++來編寫的。
2、C++語言非常靈活,功能非常強大。如果說C語言的優點是指針,那么C++的優點就是性能和類層次結構的設計。
3、C++非常嚴謹、精確和數理化,標準定義很細致。
4、C++語言的語法思路層次分明、相呼應;語法結構是顯式的、明確的。
C++的小缺點:門檻高。新手無需用到指針管理、內存管理、線程管理,直接用stl的vector或MFC的CArray分配空間。內存管理、線程管理一個產品、項目只需要一個人會,就可以了。開發效率低。如果有多年積累的基礎庫,開發效率反而高。因為這些基礎庫,是非常適合當前團隊的。初級程序員易犯錯誤。初級程序員,只有權限修改少量庫(以界面居多)。
C++相比較于java和C
寬泛點來說,從結果角度上看C幾乎什么都能做,C++幾乎什么都能做好。但是從開發成本上說,很多情況下C/C++不是最好的選擇。眼下C最主要的使用領域應該是Unix系系統的開發以及某些Unix系系統的軟件的相關開發。C++恐怕還是游戲開發。
作為通用語言,可以適應各種類型的開發算是必備屬性(同理,Java其實也是幾乎什么都能做,只是限制相對來說要大一點并且很多事情做不了那么好)。
C/C++的規則比較寬泛,可以用來進行一些更貼近硬件的開發,而大部分C/C++開發環境也是這么做的。這造成相對來說用C/C++開發往往對其他語言的依賴比較小。
C++方面,因為C++還同時具有完整的元編程功能(雖然并不特別好用),使得在保證功能的前提下也是目前性能最高的語言沒有之一。
所以當開發一個軟件特別是對性能有需求的軟件的時候,就算別的開發語言都不能用,最后總還是有C++可以用。
C++程序特征
封裝:把客觀事物封裝成抽象的類,并且類可以把自己的數據和方法只讓可信的類或者對象操作,對不可信的進行信息隱藏。 類將成員變量和成員函數封裝在類的內部,根據需要設置訪問權限,通過成員函數管理內部狀態
繼承:繼承所表達的是類之間相關的關系,這種關系使得對象可以繼承另外一類對象的特征和能力。 繼承的作用:避免公用代碼的重復開發,減少代碼和數據冗余。
多態:多態性可以簡單地概括為“一個接口,多種方法”,字面意思為多種形態。程序在運行時才決定調用的函數,它是面向對象編程領域的核心概念。比如函數重載、運算符重載、虛函數等
C++程序的編輯編譯和運行
C++語言是我們通常所說的高級語言,也就是,按照人的思維方式設計的,機器對這些可是莫名奇妙,不知所謂。于是必須要有一個橋梁來銜接兩者,那高級語言是如何變成機器語言的呢,如下為對生成可執行文件的這個過程的簡單描述,編譯程序讀取源代碼(字符流),對之進行詞法和語法的分析,將高級語言指令轉換為功能等效的匯編代碼,再由匯編程序轉換為機器語言,并且按照操作系統對可執行文件格式的要求鏈接生成可執行程序。而具體的對源程序進行處理,生成可執行文件的基本步驟,如下所示:
C/C++源程序->編譯預處理->編譯->匯編程序->鏈接程序->可執行文件
1.預處理
預處理器,進行預處理。預處理過程主要處理那些源代碼文件以“#”開始的預編譯指令。比如“#include”、“#define”和條件預編譯指令,如“#if”、“#ifdef”等。預處理時,將所有的“#define”刪除,展開所有的宏定義,并且替換掉“#include”。
(1)宏定義指令,如#define a b。對于這種偽指令,預編譯所要做的是將程序中的所有a用b替換,還有#undef,則將取消對某個宏的定義,使以后該串的出現不再被替換。
(2)條件編譯指令,如#ifdef,#ifndef,#else,#elif,#endif等。這些偽指令的引入使得程序員可以通過定義不同的宏來決定編譯程序對哪些代碼進行處理。預編譯程序將根據有關的文件,將那些不必要的代碼過濾掉。
(3) 頭文件包含指令,如#include"FileName"或者#include<FileName>等。兩者的區別是:系統提供的頭文件包含用尖括號,系統直接去系統目錄查找文件;自己寫的用雙引號,系統從工程目錄中查找,如果沒有再去系統目錄查找文件。在頭文件中一般用偽指令#define定義了大量的宏(最常見的是字符常量),同時包含有各種外部符號的聲明。采用頭文件的目的主要是為了使某些定義可以供多個不同的源程序使用。因為在需要用到這些定義的源程序中,只需加上一條#include語句即可,而不必再在此文件中將這些定義重復一遍。預編譯程序將把頭文件中的定義統統都加入到它所產生的輸出文件中,以供編譯程序對之進行處理。
預編譯程序所完成的基本上是對源程序的“替代”工作。經過此種替代,生成一個沒有宏定義、沒有條件編譯指令、沒有特殊符號的輸出文件。這個文件的含義同沒有經過預處理的源文件是相同的,但內容有所不同,經過預編譯后產生完整的源文件,將此文件作為編譯程序的輸入而被翻譯成為機器指令。
2.對源程序進行編譯
經過預編譯得到的輸出文件中,只有常量;如數字、字符串、變量的定義,以及C語言的關鍵字等。編譯過程就是把預處理完的文件進行一系列的詞法分析、語法分析、語義分析以及優化后產生相應的匯編代碼文件,這個過程是整個程序構建的核心部分,也是最復雜的部分之一。為了使計算機能執行高級語言源程序,必須先用一種稱為“編譯器(complier)”的軟件(也稱編譯程序或編譯系統)。編譯是以源程序文件為單位單別編譯的,頭文件不參加編譯。(在VC6.0里如果編譯頭文件則會彈出沒有可以工具函數,在VS2013中,對于頭文件,編譯按鈕為灰色,不可用狀態。)
現在編譯器種類很多,不同編譯器區別在于對編譯過程做了優化,添加了一些庫函數或類庫。優化處理是編譯系統中一項比較艱深的技術。它涉及到的問題不僅同編譯技術本身有關,而且同機器的硬件環境也有很大的關系。優化一部分是對中間代碼的優化。這種優化不依賴于具體的計算機。另一種優化則主要針對目標代碼的生成而進行的。對于前一種優化,主要的工作是刪除公共表達式、循環優化(代碼外提、強度削弱、變換循環控制條件、已知量的合并等)、復寫傳播,以及無用賦值的刪除,等等。后一種類型的優化同機器的硬件結構密切相關,最主要的是考慮是如何充分利用機器的各個硬件寄存器存放的有關變量的值,以減少對于內存的訪問次數。另外,如何根據機器硬件執行指令的特點(如流水線、RISC、CISC、VLIW等)而對指令進行一些調整使目標代碼比較短,執行的效率比較高,也是一個重要的研究課題。
編譯技巧:編譯的作用是對源程序進行詞法檢查、語法檢查和中間代碼生成。編譯時對文件中的全部內容進行檢查,如果有語法錯誤,編譯結束后會顯示出所有的編譯出錯信息,開發人員可以根據錯誤提示修改程序。對于新寫的一個保護多個文件的工程,一開始采用源文件分別編譯,這樣容易發現每個源文件的自身錯誤,限定了錯誤的范圍,如果一開始就采用全部編譯,多個源文件可能會產生許多錯誤,無形中增加了開發難度。如果每個源文件都通過了編譯,再將所有文件進行編譯。對源文件分別編譯對于調試,糾錯是一種很好的方法。
3.匯編
匯編實際上指把匯編語言代碼翻譯成目標機器指令的過程。匯編器的編譯過程相對于編譯器來講比較簡單,它沒有復雜的語法,也沒有語義,也不需要做指令優化,只是根據匯編指令和機器指令的對照表一一翻譯。對于被翻譯系統處理的每一個語言源程序,都將最終經過這一處理而得到相應的目標文件。目標程序一般以.obj或.o作為后綴,這具體看操作系統,如Windows是下是.obj目標文件,Linux下是.o目標文件。目標文件中所存放的也就是與源程序等效的目標機器語言代碼。有時候我們也將預編譯、編譯和匯編統稱為編譯。
4.鏈接
由匯編程序生成的目標文件并不能立即就被執行,因為各個源文件之間可能是有相互聯系的,例如,某個源文件中的函數可能引用了另一個源文件中定義的某個符號(如變量或者函數調用等);在程序中可能調用了某個庫文件中的函數,等等。所有的這些問題都需要經鏈接解決,即將源程序產生的多個目標文件鏈接為一個整體。即通過系統提供的“連接程序(linker)”將一個程序的所有目標程序和系統的庫文件以及系統提供的其他信息連接起來,最終形成一個可執行的二進制文件,它的后綴是.exe,此時產生了完整的執行文件。鏈接程序的主要工作就是將有關的目標文件彼此相連接,如源文件產生的目標文件和庫文件等,使得所有的這些目標文件成為一個能夠被操作系統裝入執行的統一整體。根據指定的庫函數的不同,鏈接處理可分為兩種:
(1)靜態鏈接:在這種鏈接方式下,函數的代碼將從其所在地靜態鏈接庫中被拷貝到最終的可執行程序中。這樣該程序在被執行時這些代碼將被裝入到該進程的虛擬地址空間中。靜態鏈接庫實際上是一個目標文件的集合,其中的每個文件含有庫中的一個或者一組相關函數的代碼。
(2)動態鏈接:此種方式下,函數的代碼被放到稱作是動態鏈接庫或共享對象的某個目標文件中。鏈接程序此時所作的只是在最終的可執行程序中記錄下共享對象的名字以及其它少量的登記信息。在此可執行文件被執行時,動態鏈接庫的全部內容將被映射到運行時相應進程的虛地址空間。動態鏈接程序將根據可執行程序中記錄的信息找到相應的函數代碼。
對于可執行文件中的函數調用,可分別采用動態鏈接或靜態鏈接的方法。使用動態鏈接能夠使最終的可執行文件比較短小,并且當共享對象被多個進程使用時能節約一些內存,因為在內存中只需要保存一份此共享對象的代碼。但并不是使用動態鏈接就一定比使用靜態鏈接要優越。 鏈接將相關關聯文件鏈接起來,所以這個階段的錯誤不好調試,發生錯誤可能在我們自己編寫的代碼中,也有可能是與別的文件關聯產生的,對于因關聯產生錯誤就比較復雜了,有時需要調整編譯器或鏈接器。
經過如上的步驟和過程,C/C++程序就成功的變為可執行文件了,我們在運行的時候直接執行前面鏈接過程產生的可執行的二進制文件,就能得到運行結果。通過對運行結果的分析,從而檢驗設計的程序是否滿足期望和要求。如果運行結果不正確,應檢查程序或算法,重新編輯代碼。

