VC6.0詳細教程

1? 編制并運行一個簡單程序

1.1 編制并運行程序的“四步曲”
1.2 工程(PROJECT)以及工程工作區(PROJECT WORKSPACE)
1.3 啟動并進入VC6的集成開發環境
1.4 創建工程并輸入源程序代碼
(1)新建一Win32 Console Application工程
(2)在工作區窗口中查看工程的邏輯架構
(3)在工程中新建C源程序文件并輸入源程序代碼
1.5 最快的方法:不創建工程,直接輸入源程序代碼
1.6 編譯、鏈接而后運行程序
1.7 及時備份自己的創作
1.8 將自己設計的最終產品(非詳細設計)提供給他人使用

2? VC6集成開發環境使用參考

2.1 VC6的常用菜單命令項
(1)File菜單
(2)Edit菜單
(3)View菜單
(4)Project菜單
(5)Build菜單
(6)Debug菜單
(7)Help菜單
(8)上下文關聯菜單
2.2 VC6的主要工作窗口
(1)Workspace窗口
(2)Output窗口
(3)窗口布局調整讓我們用VC6先來編制一個最簡單的程序,并讓它運行(執行)而得出結果,以此來作為了解VC6的開端。這個程序的功能僅僅是向屏幕上輸出一個字符串“Hello World”。程序雖小,但與編制運行大程序的整個過程是相同的,都包含著如下所謂的“四步曲”:
  1. 編輯(把程序代碼輸入,交給計算機)。
  2. 編譯(成目標程序文件.obj)。
    編譯就是把高級語言變成計算機可以識別的2進制語言,計算機只認識1和0,編譯程序把人們熟悉的語言換成2進制的。編譯程序把一個源程序翻譯成目標程序的工作過程分為五個階段:詞法分析;語法分析;語義檢查和中間代碼生成;代碼優化;目標代碼生成。主要是進行詞法分析和語法分析,又稱為源程序分析,分析過程中發現有語法錯誤,給出提示信息。
  3. 鏈接(成可執行程序文件.exe)。
    鏈接是將編譯產生的.obj文件和系統庫連接裝配成一個可以執行的程序。由于在實際操作中可以直接點擊Build從源程序產生可執行程序,可能有人就會置疑:為何要將源程序翻譯成可執行文件的過程分為編譯和鏈接兩個獨立的步驟,不是多此一舉嗎?之所以這樣做,主要是因為:在一個較大的復雜項目中,有很多人共同完成一個項目(每個人可能承擔其中一部分模塊),其中有的模塊可能是用匯編語言寫的,有的模塊可能是用VC寫的,有的模塊可能是用VB寫的,有的模塊可能是購買(不是源程序模塊而是目標代碼)或已有的標準庫模塊,因此,各類源程序都需要先各自編譯成目標程序文件(2進行機器指令代碼),再通過鏈接程序將這些目標程序文件連接裝配成可執行文件。
  4. 運行(可執行程序文件)。
    上述四個步驟中,其中第一步的編輯工作是最繁雜而又必須細致地由人工在計算機上來完成,其余幾個步驟則相對簡單,基本上由計算機來自動完成。
在開始編程之前,必須先了解工程Project(也稱“項目”,或稱“工程項目”)的概念。工程又稱為項目,它具有兩種含義,一種是指最終生成的應用程序,另一種則是為了創建這個應用程序所需的全部文件的集合,包括各種源程序、資源文件和文檔等等。絕大多數較新的開發工具都利用工程來對軟件開發過程進行管理。

用VC6編寫并處理的任何程序都與工程有關(都要創建一個與其相關的工程),而每一個工程又總與一個工程工作區相關聯。工作區是對工程概念的擴展。一個工程的目標是生成一個應用程序,但很多大型軟件往往需要同時開發數個應用程序,VC開發環境允許用戶在一個工作區內添加數個工程,其中有一個是活動的(缺省的),每個工程都可以獨立進行編譯、連接和調試。
實際上,VC6是通過工程工作區來組織工程及其各相關元素的,就好像是一個工作間(對應于一個獨立的文件夾,或稱子目錄),以后程序所牽扯到的所有的文件、資源等元素都將放入到這一工作間中,從而使得各個工程之間互不干擾,使編程工作更有條理,更具模塊化。最簡單情況下,一個工作區中用來存放一個工程,代表著某一個要進行處理的程序(我們先學習這種用法)。但如果需要,一個工作區中也可以用來存放多個工程,其中可以包含該工程的子工程或者與其有依賴關系的其他工程。

可看出,工程工作區就像是一個“容器”,由它來“盛放”相關工程的所有有關信息,當創建新工程時,同時要創建這樣一個工程工作區,而后則通過該工作區窗口來觀察與存取此工程的各種元素及其有關信息。創建工程工作區之后,系統將創建出一個相應的工作區文件(.dsw),用來存放與該工作區相關的信息;另外還將創建出的其他幾個相關文件是:工程文件(.dsp)以及選擇信息文件(.opt)等。
編制并處理C++程序時要創建工程,VC6已經預先為用戶準備好了近20種不同的工程類型以供選擇,選定不同的類型意味著讓VC6系統幫著提前做某些不同的準備以及初始化工作(例如,事先為用戶自動生成一個所謂的底層程序框架或稱框架程序,并進行某些隱含設置,如隱含位置、預定義常量、輸出結果類型等)。工程類型中,其中有一個為“Win32 Console Application”,它是我們首先要掌握的、用來編制運行C++程序方法中最簡單的一種。此種類型的程序運行時,將出現并使用一個類似于DOS的窗口,并提供對字符模式的各種處理與支持。實際上,提供的只是具有嚴格的采用光標而不是鼠標移動的界面。此種類型的工程小巧而簡單,但已足以解決并支持本課程中涉及到的所有編程內容與技術,使我們把重點放在程序的編制而并非界面處理等方面,至于VC6支持的其他工程類型(其中有許多還將涉及到Windows或其他的編程技術與知識),有待在今后的不斷學習中來逐漸了解、掌握與使用。
圖1-1 VC6的集成開發環境窗口
了解了工程及其上述一般概念后,現在就讓我們開始Visual C++ 6.0之旅吧!首先按如下兩種方法之一啟動并運行VC6,進入到它的集成開發環境窗口(假設在Windows系統下已經安裝了VC6),其具體窗口式樣如圖1-1所示。

方法一:
若桌面上有VC6圖標(“橫躺著”即“倒下”的“8”字型圖標,且標有“Microsoft Visual Studio 6.0”字樣,如圖1-2所示),則用鼠標雙擊該圖標。
圖1-2 VC6在桌面上的快捷方式
方法二(假設按照通常方式對VC6進行了安裝的話):
通過“開始”→“程序”→“Microsoft Visual Studio 6.0”→“Microsoft Visual C++ 6.0”,單擊一下該菜單項。
圖1-1式樣的窗口從大體上可分為四部分。上部:菜單和工具條;中左:工作區(workspace)視圖顯示窗口,這里將顯示處理過程中與項目相關的各種文件種類等信息;中右:文檔內容區,是顯示和編輯程序文件的操作區;下部:輸出(Output)窗口區,程序調試過程中,進行編譯、鏈接、運行時輸出的相關信息將在此處顯示。
注意,由于系統的初始設置或者環境的某些不同,可能你所啟動的VC6初始窗口式樣與圖1-1有所不同,也許會沒出現Workspace窗口或Output窗口,這時可通過“View→Workspace”菜單選項的執行,總可使中左處的工作區窗口顯現出來;而通過“View→Output”菜單選項的執行,又總可使下部的輸出區窗口得以顯現。當然,如果不想看到這兩個窗口,可以點擊相應窗口的“x”按鍵來關閉窗口。為了把程序代碼輸入而交給計算機,需要使用VC6的編輯器來完成。如前所述,首先要創建工程以及工程工作區,而后才能輸入具體程序完成所謂的“編輯”工作(注意,該步工作在四步驟中最繁雜、而又必須細致地由人工來完成!)。
圖1-3 新建一個名為Sample的工程(同時自動創建一工作區)

(1)新建一Win32 Console Application工程

選擇菜單File下的New項,會出現一個選擇界面,在屬性頁中選擇Projects標簽后,會看到近20種的工程類型,我們只需選擇其中最簡單的一種:“Win32Console Application”,而后往右上處的“Location”文本框和“Project name”文本框中填入工程相關信息所存放的磁盤位置(目錄或文件夾位置)以及工程的名字,設置到此時的界面信息如圖1-3所示。
在圖1-3中,“Location”文本框中填入如“D:\myData\VC6”,這是假設你準備在D磁盤的\myData\VC6文件夾即子目錄下存放與工程工作區相關的所有文件及其相關信息,當然也可通過點擊其右部的“…”按鈕去選擇并指定這一文件夾即子目錄位置。“Project name”文本框中填入如“Sample”的工程名(注意,名字由你根據工程性質確定,此時VC6會自動在其下的Location文本框中用該工程名“Sample”為你建立一個同名子目錄,隨后的工程文件以及其他相關文件都將存放在這個目錄下)。
選擇OK按鈕進入下一個選擇界面。這個界面主要是詢問用戶想要構成一個什么類型的工程,其界面如圖1-4所示。
圖1-4 選擇創建一個什么樣的工程
若選擇“An empty project”項將生成一個空的工程,工程內不包括任何東西。若選擇“A simple application”項將生成包含一個空的main函數和一個空的頭文件的工程。選“A"Hello World!"application”項與選“A simple application”項沒有什么本質的區別,只是需要包含有顯示出“Hello World!”字符串的輸出語句。選擇“An application that supports MFC”項的話,可以利用VC6所提供的類庫來進行編程。
為了更清楚的看到編程的各個環節,我們選擇“An empty project”項,從一個空的工程來開始我們的工作。單擊Finish按鈕,這時VC6會為你生成一個小型報告,報告的內容是剛才所有選擇項的總結,并且詢問你是否接受這些設置。如果接受選擇OK按鈕,否則選擇Cancel按鈕。我們選OK從而可進入到真正的編程環境下了。界面情況如圖1-5所示。
圖1-5 剛完成創建工程Sample的VC6集成開發環境窗口

(2)在工作區窗口中查看工程的邏輯架構

注意屏幕中的Workspace窗口,該窗口中有兩個標簽,一個是ClassView,一個是FileView。ClassView中列出的是這個工程中所包含的所有類的有關信息,當然我們的程序將不涉及到類,這個標簽中現在是空空如也。點擊FileView標簽后,將看到這個工程所包含的所有文件信息。點擊“+”圖標打開所有的層次會發現有三個邏輯文件夾:Source Files文件夾中包含了工程中所有的源文件;Header Files文件夾中包含了工程中所有的頭文件;Resource Files文件夾中包含了工程中所有的資源文件。所謂資源就是工程中所用到的位圖,加速鍵等信息,在我們的編程中不會牽扯到這一部分內容。現在FileView中也不包含任何東西。
邏輯文件夾是邏輯上的,他們只是在工程的配置文件中定義的,在磁盤上并沒有物理地存在這三個文件夾。我們也可以刪除自己不使用的邏輯文件夾;或者根據我們項目的需要,創建新的邏輯文件夾,來組織工程文件。這三個邏輯文件夾是VC預先定義的,就編寫簡單的單一源文件的C程序而言,我們只需要使用Source Files一個文件夾就夠了。

(3)在工程中新建C源程序文件并輸入源程序代碼

下面該輪到生成一個“Hello.cpp”的源程序文件,而后通過編輯界面來輸入所需的源程序代碼。選擇菜單Project中子菜單Add To Project下的new項,在出現的對話框的Files標簽(選項卡)中,選擇“C++ Source File”項,在右中處的File文本框中為將要生成的文件取一個名字,我們取名為Hello(其他遵照系統隱含設置,此時系統將使用Hello.cpp的文件來保存所鍵入的源程序),此時的界面情況如圖1-6所示。
而后選擇OK按鈕,進入輸入源程序的編輯窗口(注意所出現的呈現“閃爍”狀態的輸入位置光標),此時只需通過鍵盤輸入你所需要的源程序代碼:
#include <stdio.h>
void main()
{
printf("Hello World!\n");
}
可通過Workspace窗口中的FileView標簽,看到Source Files文件夾下文件Hello.cpp已經被加了進去,此時的界面情況如圖1-7所示。
圖1-7 在Hello.cpp輸入C源程序代碼
實際上,這時在Workspace窗口的ClassView標簽中的Globals文件夾下,也可以看到我們剛才所鍵入的main函數。不需要象前面描述的那樣顯示地創新一個工程,對于新編寫一個程序,只需要在圖1-3所示的界面中,選“Files”標簽,再選擇“C++ Source File”,其界面與圖1-6相似(僅Add to projec是暗淡的、無法選擇),后續操作則與前述相同。

最簡單的做法是:直接使用工具欄上的新建文件按鈕新建一空白文件,緊接著單擊工具欄上的保存按鈕保存此空文件——注意,保存時一定要以“.c”或“.cpp”作為擴展名,否則邏輯程序時自動格式化和特殊顯示等很多特性將無法使用,程序無法被運行。

這種方式新建的C源程序文件在編譯時,會提示用戶,要求允許系統為其創新一個默認的工程(含相應的工作區)。程序編制完成(即所謂“四步曲”中第一步的編輯工作得以完成)之后,就可以進行后三步的編譯、鏈接與運行了。所有后三步的命令項都處在菜單Build之中。注意,在對程序進行編譯、鏈接和運行前,最好先保存自己的工程(使用“File→Save All”菜單項)以避免程序運行時系統發生意外而使自己之前的工作付之東流,應讓這種做法成為自己的習慣、素質。

首先選擇執行菜單第一項Compile,此時將對程序進行編譯。若編譯中發現錯誤(error)或警告(warning),將在Output窗口中顯示出它們所在的行以及具體的出錯或警告信息,可以通過這些信息的提示來糾正程序中的錯誤或警告(注意,錯誤是必須糾正的,否則無法進行下一步的鏈接;而警告則不然,它并不影響進行下一步,當然最好還是能把所有的警告也“消滅”掉)。當沒有錯誤與警告出現時,Output窗口所顯示的最后一行應該是:“Hello.obj-0 error(s), 0warning(s)”。
圖1-8 程序Hello.cpp的運行結果界面
編譯通過后,可以選擇菜單的第二項Build來進行鏈接生成可執行程序。在鏈接中出現的錯誤也將顯示到Output窗口中。鏈接成功后,Output窗口所顯示的最后一行應該是:“Sample.exe-0 error(s), 0 warning(s)”。最后就可以運行(執行)我們所編制的程序了,選擇Execute項(該選項前有一個深色的感嘆號標志“!”,實際上也可通過單擊窗口上部工具欄中的深色感嘆號標志“!”來啟動執行該選項),VC6將運行已經編好的程序,執行后將出現一個結果界面(所謂的類似于DOS窗口的界面),如圖1-8所示,其中的“press any key to continue”是由系統產生的,使得用戶可以瀏覽輸出結果,直到按下了任一個鍵盤按鍵時為止(那時又將返回到集成界面的編輯窗口處)。

至此我們已經生成并運行(執行)了一個完整的程序,完成了一個“回合”的編程任務。此時應執行“File→Close Workspace”菜單項,待系統詢問是否關閉所有的相關窗口時,回答“是”,則結束了一個程序從輸入到執行的全過程,回到了剛剛啟動VC6的那一個初始畫面。 (1)完全備份。
對于剛才工作的工程Sample而言,只需將D:\myData\VC6下的文件夾Sample復制到U盤或打包成一個文件后放到自己的郵箱。需要在其它計算機上繼續完成該工程時,將該文件夾復制到該計算機的硬盤上,進入VC6,通過“File→Open Workspace”菜單項將該工程打開即可。

(2)只備份C源程序文件。

對于剛才工作的工程Sample而言,工程非常簡單,沒有什么專門的設置,因此,僅備份其中的C源程序Hello.cpp就足矣。需要在其它計算機上繼續完成該程序時,只需將備份的程序復制到該計算機的硬盤上,進入VC6,根據前面的講述,新建一Win32 Console Application(做到圖1-5所示的界面),然后通過“Project→Add to Project→Files”菜單項將Hello.cpp添加新建的工程中。

最簡單的做法是:直接使用工具欄上的文件打開按鈕“ ”打開Hello.cpp。

將自己設計的最終產品(非詳細設計)提供給他人使用

需要將自己設計的產品提供給他人使用時,針對前述的Sample工程,只需將鏈接產生的可執行文件Sample.exe復制/發送給他人即可,不能復制整個工程文件夾或復制.cpp文件,這是因為:

(1)復制可執行文件已足夠。

(2)復制整個工程文件夾或復制.cpp文件,等于提供了自己的詳細設計,在商業上一般是不這樣做了,除非事先雙方談妥需要這樣做或對方愿意出高價購買你的詳細設計。
圖1-9 選擇生成什么類型的可執行程序
另外,應當說明的是:前面所說的編譯、鏈接過程都是Debug類型的,也就是說,當VC6在進行這些工作時將加入一些調試信息,致使編譯鏈接后生成的代碼很龐大,效率也降低。如果確信你的程序已經完美無缺或者是要正式發布,就應該選擇菜單Build中的Batch Build項,產生如圖1-9所示的對話框,其中的兩個選項分別代表編譯的代碼形式。如果選擇第一項Release,那么生成的就是最終代碼,其運行效率會增高。

選擇Sample-Win32 Release項,再進行Build或Rebuild All就會在工程所在的目錄下產生一個新的目錄release,在release目錄下生成的可執行程序代碼規模小,執行效率高,是我們最后的產品。

(1)File菜單

New:打開“new”對話框,以便創建新的文件、工程或工作區。
Close Workspace:關閉與工作區相關的所有窗口。
Exit:退出VC6環境,將提示保存窗口內容等。

(2)Edit菜單

Cut:快捷鍵Ctrl+X。將選定內容復制到剪貼板,然后再從當前活動窗口中刪除所選內容。與“Paste”聯合使用可以移動選定的內容。
Copy:快捷鍵Ctrl+C。將選定內容復制到剪貼板,但不從當前活動窗口中刪除所選內容。與“Paste”聯合使用可以復制選定的內容。
Paste:快捷鍵Ctrl+V。將剪貼板中的內容插入(粘貼)到當前鼠標指針所在的位置。注意,必須先使用Cut或Copy使剪貼板中具有準備粘貼的內容。
Find:快捷鍵Ctrl+F。在當前文件中查找指定的字符串。順便指出,可按快捷鍵F3尋找下一個匹配的字符串。
Find in Files:在指定的多個文件中查找指定的字符串。
Replace:快捷鍵Ctrl+H。替換指定的字符串(用某一個串替換另一個串)。
Go To: 快捷鍵Ctrl+G。將光標移到指定行上。
Breakpoints:快捷鍵Alt+F9。彈出對話框,用于設置、刪除或查看程序中的所有斷點。斷點將告訴調試器應該在何時何地暫停程序的執行,以便查看當時的變量取值等現場情況。

(3)View菜單

Workspace:如果工作區窗口沒顯示出來,選擇執行該項后將顯示出工作區窗口。
Output:如果輸出窗口沒顯示出來,選擇執行該項后將顯示出輸出窗口。輸出窗口中將隨時顯示有關的提示信息或出錯警告信息等。

(4)Project菜單

Add To Project:選擇該項將彈出子菜單,用于添加文件或數據鏈接等到工程之中去。例如子菜單中的New選項可用于添加“C++ Source File”或“C/C++ Header File”;而子菜單中的Files選項則用于插入已有的文件到工程中。
Settings:為工程進行各種不同的設置。當選擇其中的“Debug”標簽(選項卡),并通過在“Program arguments:”文本框中填入以空格分割的各命令行參數后,則可以為帶參數的main函數提供相應參數(呼應于“void main(int argc, char* argv[ ]){…}”形式的main函數中所需各argv數組的各字符串參數值)。注意,在執行帶參數的main函數之前,必須進行該設置,當“Program arguments:”文本框中為空時,意味著無命令行參數。

(5)Build菜單

Compile:快捷鍵Ctrl+F7。編譯當前處于源代碼窗口中的源程序文件,以便檢查是否有語法錯誤或警告,如果有的話,將顯示在Output輸出窗口中。
Build:快捷鍵F7。對當前工程中的有關文件進行連接,若出現錯誤的話,也將顯示在Output輸出窗口中。
Execute:快捷鍵Ctrl+F5。運行(執行)已經編譯、連接成功的可執行程序(文件)。
Start Debug:選擇該項將彈出子菜單,其中含有用于啟動調試器運行的幾個選項。例如其中的Go選項用于從當前語句開始執行程序,直到遇到斷點或遇到程序結束;Step Into選項開始單步執行程序,并在遇到函數調用時進入函數內部再從頭單步執行;Run to Cursor選項使程序運行到當前鼠標光標所在行時暫停其執行(注意,使用該選項前,要先將鼠標光標設置到某一個你希望暫停的程序行處)。執行該菜單的選擇項后,就啟動了調試器,此時菜單欄中將出現Debug菜單(而取代了Build菜單)。

(6)Debug菜單

啟動調試器后才出現該Debug菜單(而不再出現Build菜單)。
Go:快捷鍵F5。從當前語句啟動繼續運行程序,直到遇到斷點或遇到程序結束而停止(與Build→Start Debug→Go選項的功能相同)。
Restart:快捷鍵Ctrl+Shift+F5。重新從頭開始對程序進行調試執行(當對程序做過某些修改后往往需要這樣做!)。選擇該項后,系統將重新裝載程序到內存,并放棄所有變量的當前值(而重新開始)。
Stop Debugging:快捷鍵Shift+F5。中斷當前的調試過程并返回正常的編輯狀態(注意,系統將自動關閉調試器,并重新使用Build菜單來取代Debug菜單)。
Step Into:快捷鍵F11。單步執行程序,并在遇到函數調用語句時,進入那一函數內部,并從頭單步執行(與Build→Start Debug→Step Into選項的功能相同)。
Step Over:快捷鍵F10。單步執行程序,但當執行到函數調用語句時,不進入那一函數內部,而是一步直接執行完該函數后,接著再執行函數調用語句后面的語句。
Step Out:快捷鍵Shift+F11。與“Step Into”配合使用,當執行進入到函數內部,單步執行若干步之后,若發現不再需要進行單步調試的話,通過該選項可以從函數內部返回(到函數調用語句的下一語句處停止)。
Run to Cursor:快捷鍵Ctrl+F10。使程序運行到當前鼠標光標所在行時暫停其執行(注意,使用該選項前,要先將鼠標光標設置到某一個你希望暫停的程序行處)。事實上,相當于設置了一個臨時斷點,與Build→Start Debug→Run to Cursor選項的功能相同。
Insert/Remove Breakpoint:快捷鍵F9。本菜單項并未出現在Debug菜單上(在工具欄和程序文檔的上下文關聯菜單上),列在此處是為了方便大家掌握程序調試的手段,其功能是設置或取消固定斷點——程序行前有一個圓形的黑點標志,表示已經該行設置了固定斷點。另外,與固定斷點相關的還有Alt+F9(管理程序中的所有斷點)、Ctrl+F9(禁用/使能當前斷點)。

(7)Help菜單

通過該菜單來查看VC6的各種聯機幫助信息。

(8)上下文關聯菜單

除了主菜單和工具欄外,VC6開發環境還提供了大量的上下文關聯菜單,用鼠標右鍵的單擊窗口中很多地方都會彈出一個關聯菜單,里面包含有與被單擊項目相關的各種命令,建議大家在工作時可以試著多點點鼠標右鍵,說不定會發現很多有用的命令,從而大大加快一些常規操作的速度。

(1)Workspace窗口

Workspace窗口顯示了當前工作區中各個工程的類、資源和文件信息,當新建或打開一個工作區后,Workspace窗口通常就會出現三個樹視圖:ClassView(類視圖)、ResourceView(資源視圖)和FileView(文件視圖),如果在VC6企業版中打開了數據庫工程,還會出現第四個視圖DataView(數據視圖)。如同前面所述,在Workspace窗口的各個視圖內單擊鼠標右鍵可以得到很多有用的關聯菜單。
ClassView顯示當前工作區中所有工程定義的C++類、全局函數和全局變量,展開每一個類后,可以看到該類的所有成員函數和成員變量,如果雙擊類的名字,VC6會自動打開定義這個類的文件,并把文檔窗口定位到該類的定義處,如果雙擊類的成員或者全局函數及變量,文檔窗口則會定位到相應函數或變量的定義處。

ResourceView顯示每個工程中定義的各種資源,包括快捷鍵、位圖、對話框、圖標、菜單、字符串資源、工具欄和版本信息,如果雙擊一個資源項目,VC6就會進入資源編輯狀態,打開相應的資源,并根據資源的類型自動顯示出Graphics、Color、Dialog、Controls等停靠式窗口。

FileView顯示了隸屬于每個工程的所有文件。除了C/C++源文件、頭文件和資源文件外,我們還可以向工程中添加其它類型的文件,例如Readme.txt等,這些文件對工程的編譯連接不是必需的,但將來制作安裝程序時會被一起打包。同樣,在FileView中雙擊源程序等文本文件時,VC6會自動為該文件打開一個文檔窗口,雙擊資源文件時,VC6也會自動打開其中包含的資源。

在FileView中對著一個工程單擊鼠標右鍵后,關聯菜單中有一個“Clean”命令,在此特地要解釋一下它的功能:VC6在建立(Build)一個工程時,會自動生成很多中間文件,例如預編譯頭文件、程序數據庫文件等,這些中間文件加起來的大小往往有數兆,很多人在開發一個軟件期間會使用辦公室或家里的數臺機器,如果不把這些中間文件刪除,在多臺機器之間使用軟盤拷貝工程就很麻煩。“Clean”命令的功能就是把VC6生成的中間文件全部刪除,避免了手工刪除時可能會出現誤刪或漏刪的問題。另外,在某些情況下,VC6編譯器可能無法正確識別哪些文件已被編譯過了,以致于在每次建立工程時都進行完全重建,很浪費時間,此時使用“Clean”命令刪除掉中間文件就可以解決這一問題。

應當指出,承載一個工程的還是存儲在工作文件夾下的多個文件(物理上),在Workspace窗口中的這些視圖都是邏輯意義上的,它們只是從不同的角度去自動統計總結了工程的信息,以方便和幫助我們查看工程、更有效地開展工作。如果開始時你不習慣且工程很簡單(學習期間很多時候都只有一個.cpp文件),則你完全沒有必要去搭理這些視圖,只需要在.cpp文件內容窗口中工作。

(2)Output窗口

與Workspace窗口一樣,Output窗口也被分成了數欄,其中前面4欄最常用。在建立工程時,Build欄將顯示工程在建立過程中經過的每一個步驟及相應信息,如果出現編譯連接錯誤,那么發生錯誤的文件及行號、錯誤類型編號和描述都會顯示在Build欄中,用鼠標雙擊一條編譯錯誤,VC6就會打開相應的文件,并自動定位到發生錯誤的那一條語句。
工程通過編譯連接后,運行其調試版本,Debug欄中會顯示出各種調試信息,包括DLL裝載情況、運行時警告及錯誤信息、MFC類庫或程序輸出的調試信息、進程中止代碼等。
兩個Find in Files欄用于顯示從多個文件中查找字符串后的結果,當你想看看某個函數或變量出現在哪些文件中,可以從“Edit”菜單中選擇“Find in Files…”命令,然后指定要查找的字符串、文件類型及路徑,按“查找”后結果就會輸出在Output的Find in Files欄中。

(3)窗口布局調整

VC6的智能化界面允許用戶靈活配置窗口布局,例如菜單和工具欄的位置都可以重新定位。讓我們在菜單或工具欄左方類似于把手的兩個豎條紋處或其它空白處點擊鼠標左鍵并按住,然后試試把它拖動到窗口的不同地方,就可以發現菜單和工具欄能夠停靠在窗口的上方、左方和下方,雙擊豎條紋后,它們還能以獨立子窗口的形式出現,獨立子窗口能夠始終浮動在文檔窗口的上方,并且可以被拖到VC6主窗口之外,如果有雙顯示器,甚至可以把這些子窗口拖到另外一個顯示器上,以便進一步加大編輯區域的面積。Workspace和Output等停靠式窗口(Docking View)也能以相同的方式進行拖動,或者切換成獨立的子窗口,此外,這些停靠式窗口還可以切換成普通的文檔窗口模式,不過文檔窗口不能被拖出VC6的主窗口,切換的方法是選中某個停靠式窗口后,在“Windows”菜單中把“Docking View”置于非選中狀態。所謂程序調試,是指當程序的工作情況(運行結果)與設計的要求不一致——通常是程序的運行結果不對時,科學地(而不是憑偶然的運氣)通過一定的方法、使用一定的手段來檢查程序中存在的設計問題(某種邏輯錯誤而不是語法、鏈接錯誤,修正語法、鏈接錯誤不是調試程序要做的事)。

當程序編譯出錯或者鏈接出錯時,系統都將在Output輸出窗口中隨時顯示出有關的提示信息或出錯警告信息等(如果是編譯出錯,只要雙擊Output窗口中的出錯信息就可以自動跳到出錯的程序行,以便仔細查找)。但若編譯和鏈接都正確,而執行結果又總是不正確時,這時就需要使用調試工具來幫著“偵察”出程序中隱藏著的出錯位置(某種邏輯錯誤)。
強調:初學者常犯的錯誤是認為“編譯和鏈接”都正確,程序就應該沒有問題,怎么會結果不對呢?“編譯和鏈接”都正確,只能說明程序沒有語法和拼寫上的錯誤,但在算法(邏輯)上有沒有錯,還得看結果對不對。反過來講,無論讓你設計一個什么樣的程序,你都只寫以下幾行,則“編譯和鏈接”肯定都正確,但能實現設計的要求嗎?

#include <stdio.h>
void main()
{
printf("Hello World!\n");
}

事實上,程序設計的重點完全不是修正編譯和鏈接過程中的錯誤——相對而言,這種工作基本沒有技術含量,程序設計的主要工作是設計正確的算法。
調試程序的方法與醫生看病的道理類似:先問清基本情況,再進行大致的檢查,然后分析檢查的結果、確定范圍,再進行專項檢查,再分析檢查結果,如此反復,最后確定問題所在并進行治療、檢查療效。

必須指出的是:用戶調試自己的程序時,應對程序的設計(工作)思路非常清楚,知道每一段、每一行程序所應起到(盡管不見得都能實現)的作用,這是基本的前提。若自己對設計都不清楚、甚至不知道每一段、每一行程序應發揮的作用,是談不上調試程序的。

(1)觀察了解程序的“病癥”表現

首先是看清情況,程序的任務、程序的預期表現與程序工作的實際表現,大概是什么方面的“病”——對于常見的小“病”,經驗豐富的專家不用后續檢查就能知道問題所在。經驗當然重要,但對于初學者而言,掌握正確的調試思路則更加重要,因為初學者很難通過觀察程序而發現問題所在。

(2)弄清程序的主要工作流程

在學習過程中設計的程序一般都不太復雜,從總體算法上總是可以劃分為幾個大的模塊(也可稱為步驟,可以是一段程序或一個子程序——函數):接收用戶的要求和任務(讀取相應的參數、輸入相應的數據)、對數據進行計算和處理、按格式要求輸出相應的結果。對于每一個大的模塊,又可以分為許多子模塊。

#include <stdio.h>
int main(void)
{
int a[10000], i, j, num, x, tmp, mini;

//從鍵盤讀入用戶輸入的數據,數據存放在數組a中,num記錄讀入數據的個數
printf("\nPlease input numbers:");
for (i=0; i<10000; j++)
{
? scanf("%f", x);
? if (x = -222)? //如果讀入的數為結束標志,則結束輸入
? {
?? break;
? }
? a[i] = x;
? num++;? //num記錄已讀入的有效數據的個數
}

//計算與處理:對數據進行從小到大排序,排序使用的方法是選擇法
for (i=0; i<num; i++)? //依次找出第0,1,2…個最小的并放到相應位置a[i]
{
? mini = i;? //開始找第i個最小的,先假定a[i]最小,mini負責記最小的所在位置
? for (j=i+1; j<num; j++)? //從i后的所有數中,找出最小的一個,位置記入mini
? {
?? if (a[j] > a[mini])? //如果有誰比當前認為最小的還小,則記住其位置
?? {
??? mini = j;
?? }
? }
? //將找到的最小數與第i個數交換位置,實現第i個最小數到位
? tmp = a[i];
? a[mini] = a[i];
? a[i] = tmp;
}

//輸出計算、處理的結果
printf("Output:\n");
for (i=1; i<=num; i++);? //依次輸出第1個到最后一個數
{
? printf("%-6d", a[i]);
//如果當前為第6個數或最后一個數,則不輸出“,”而換行
? if (i % 6 != 0? &&? i != num)
? {
?? printf("\n");
? }
? else;
? {
?? printf(",");
? }
}
}
程序3-1 程序運行效果示例
例如程序3-1是有問題的,它是為了實現以下功能(其中的注釋寫明了主要模塊的功能以及每個模塊的實現方法):

①程序運行時先顯示Please input numbers:,再從鍵盤上讀入一組整數(只考慮int型),數與數之間只使用空格或回車作分隔。數可正可負,最多10000個,但若讀入的數為-222時,則表示輸入結束且-222不算在該組數內。
②對這一組數按從小到大的順序進行排序。
③將排序后的這一組數輸出到屏幕上,輸出格式為每行6個數,數與數之間使用逗號(,)分隔,兩個逗號之間的寬度(不算逗號)為6且使用左對齊格式。注意,行尾沒有逗號。


程序的運行效果應類似地如圖3-1所示,其中的100 120 89 72 -19 200 500 210 235 6 24 1234 78 234 -234 -2342 346 23524 7823 -3411 23423 -222是從鍵盤輸入的內容。

(3)進行大致的檢查,確定問題存在的模塊

檢查的任務,就是查看程序的實際工作狀態(屏幕輸出是否正確、各變量的值是否正確)與預期的設計是否一致,若不一致,則肯定有問題。

對于較長、較復雜的程序,檢查時不應從開始一行一行檢查,這種方法效率低、不科學,也不易發現問題。正確的方法是:先分大模塊檢查,確定大模塊有無問題,再針對有問題的大模塊,檢查其內部的工作過程。例如,對于程序3-1,應先檢查輸入完成時工作是否正確,即讓程序運行至“計算與處理”時暫停(從鍵盤輸入一組數據),查看相應的結果(這段程序的運行目的就是將輸入數據存放至數組a中并由num記錄數據個數,因此應檢查數組a和num的內容)是否正確,若不正確,則至少找到一部分問題。排除輸入的故障后,則可讓程序運行到“輸出”時暫停,檢查相應的結果(即數組a中的數據是否按要求排好順序)。

在檢查過程中,用戶應根據自己的經驗,靈活調整檢查策略,提高工作效率,例如可以使用二分法定位故障,也可觀察后估計問題位置再進行檢查。

(4)檢查故障模塊,確定問題并解決

對于復雜故障模塊內部的運行檢查,可以再分子模塊(部分)進行分部檢查。檢查模塊的設計是否正確的基本思路是:一步一步運行程序,看程序的運行流程是否如設計期望,看每步程序的運行結果(屏幕輸出和相關變量)是否與設計(心算)的一致。例如程序3-1的輸入部分,假定未看到問題,則可檢查:輸入一個數據后,x中的數據是否是輸入的數據——若不是,則該條語句肯定有問題,仔細檢查應能發現問題;當輸入不是結束標志時,則否將數據存入了a[i]、i和計數器num的值是否正確;當輸入的是結束標志時,是否如期望的結束輸入。

(1)設置固定斷點或臨時斷點

所謂斷點,是指定程序中的某一行,讓程序運行至該行后暫停運行,使得程序員可以觀察分析程序的運行過程中的情況。這些情況一般包括:

①在變量窗口(Varibles)中觀察程序中變量的當前值。程序員觀察這些值的目的是與預期值對比,若與預期值不一致,則此斷點前運行的程序肯定在某個地方有問題,以此可縮小故障范圍。例如以下程序是計算cos(x)并顯示,運行時發現無論x輸入為多少,結果都是0.046414。
#include <stdio.h>
#include <math.h>

void main()
{
int? x;

printf("Please input x:");
scanf("% d", &x);
printf("cos(x)=%f\n", cos(x));
}
在該程序中,若你沒有看到問題——程序較長、較復雜時很難看出問題所在,則應該使用調試手段定位故障位置。

②在監控窗口(Watch)中觀察指定變量或表達式的值。當變量較多時,使用Varibles窗口可能不太方便,使用Watch窗口則可以有目的、有計劃地觀察關鍵變量的變化。

③在輸出窗口中觀察程序當前的輸出與預期是否一致。同樣地,若不一致,則此斷點前運行的程序肯定在某個地方有問題。

④在內存窗口(Memory)中觀察內存中數據的變化。在該窗口中能直接查詢和修改任意地址的數據。對初學者來說,通過它能更深刻地理解各種變量、數組和結構等是如何占用內存的,以及數組越界的過程。

⑤在調用堆棧窗口(Call Stack)中觀察函數調用的嵌套情況。此窗口在函數調用關系比較復雜或遞歸調用的情況下,對分析故障很有幫助。

(2)單步執行程序

讓程序被一步一步(行)地執行,觀察分析執行過程是否符合預要求。例如,以下程序預期的功能是從鍵盤上讀入兩個數(x和y),判斷x和y是否相等,相等則在屏幕上顯示x=y,不相等則顯示x<>y。這是要求實現的功能,但程序實際的運行狀況卻是:無論輸入什么,都會在屏幕上顯示x=y和x&lt;>y,程序肯定有問題,但表面上看卻可能找不到問題所在,使用單步執行,則能定位故障點,縮小看的范圍。例如,在單步執行的過程中,若輸入“2,3”,發現x和y的值的確變成了2和3,此時按道理不應執行“printf("x=y\n");”,但單步跟蹤卻發現被執行了,因此多半問題出在“if (x = y)”。
#include <stdio.h>
void main()
{
int? x, y;

printf("Please input x, y:");
scanf("%d,%d", &x, &y);
if (x = y)
{
? printf("x=y\n");
}
else;
{
? printf("x<>y\n");
}
}
在單步執行的過程中,應靈活應用Step Over、Step Into、Step Out、Run to Cursor等方法,提高調試效率。建議在程序調試過程中,記住并使用“Step Over、Step Into、Step Out、Run to Cursor”等菜單項的快捷鍵,開始時可能較生疏、操作較慢,但堅持一段時間就能生巧、效率提高。

(3)使用斷言

斷言是對某種假設條件進行檢查(可理解為若條件成立則無動作,否則應報告),它可以快速發現并定位軟件問題,同時對系統錯誤進行自動報警。斷言可以對在系統中隱藏很深,用其它手段極難發現的問題進行定位,從而縮短軟件問題定位時間,提高系統的可測性。實際應用時,可根據具體情況靈活地設計斷言。
使用斷言時,必須在程序的開頭加上:
#include <assert.h>
①可用斷言來確認函數的參數。示例:假設某函數參數中有一個指針,那么使用指針前可對它檢查,以防止其他人調用本函數時使用空指針作參數。代碼如下:
int exam_fun( unsigned char *str )
{
??? assert(str != NULL);? // 斷言“指針不為空”,若“空”(斷言不成立)則報錯
??? ... //other program code
}
②可用斷言來確認是否發生了不該發生的情況。示例:以下程序段運行結果有錯,檢查起來很困難而且搞了很久都不知是什么地方有問題。因此,建議分析程序的正常運行情況應該是什么,運行過程中是否出了異常,針對所有(或關鍵狀態)應當正常的情況,使用斷言,就很有可能發現異常原因,且調試效率很高。針對該程序段,我們斷言(斷定)變量i的取值應該為“i>=0 && i<SIZE”且較關鍵,但在運行過程中是否有可能被無意修改(例如其它變量越界)而超出范圍呢,就可使用斷言檢查是否發生了這樣的情況。
for (i=0; i<SIZE; i++)
{
??? ... //other program code
assert(i>=0 && i<SIZE);? // 斷言“i的正常取值范圍”,若斷言不成立則報錯
array[i] = i;
??? ... //other program code
}
斷言不成立時(一出現異常),系統將立即報錯,此時可進入程序調試狀態,檢查程序的運行情況。

(4)與調試相關的操作菜單:Build菜單

Compile:快捷鍵Ctrl+F7。編譯當前處于源代碼窗口中的源程序文件,以便檢查是否有語法錯誤或警告,如果有的話,將顯示在Output輸出窗口中。
Build:快捷鍵F7。對當前工程中的有關文件進行連接,若出現錯誤的話,也將顯示在Output輸出窗口中。
Execute:快捷鍵Ctrl+F5。運行(執行)已經編譯、連接成功的可執行程序(文件)。
Start Debug:選擇該項將彈出子菜單,其中含有用于啟動調試器運行的幾個選項。例如其中的Go選項用于從當前語句開始執行程序,直到遇到斷點或遇到程序結束;Step Into選項開始單步執行程序,并在遇到函數調用時進入函數內部再從頭單步執行;Run to Cursor選項使程序運行到當前鼠標光標所在行時暫停其執行(注意,使用該選項前,要先將鼠標光標設置到某一個你希望暫停的程序行處)。執行該菜單的選擇項后,就啟動了調試器,此時菜單欄中將出現Debug菜單(而取代了Build菜單)。

(5)與調試相關的操作菜單:Debug菜單

啟動調試器后才出現該Debug菜單(而不再出現Build菜單)。
Go:快捷鍵F5。從當前語句啟動繼續運行程序,直到遇到斷點或遇到程序結束而停止(與Build→Start Debug→Go選項的功能相同)。
Restart:快捷鍵Ctrl+Shift+F5。重新從頭開始對程序進行調試執行(當對程序做過某些修改后往往需要這樣做!)。選擇該項后,系統將重新裝載程序到內存,并放棄所有變量的當前值(而重新開始)。
Stop Debugging:快捷鍵Shift+F5。中斷當前的調試過程并返回正常的編輯狀態(注意,系統將自動關閉調試器,并重新使用Build菜單來取代Debug菜單)。
Step Into:快捷鍵F11。單步執行程序,并在遇到函數調用語句時,進入那一函數內部,并從頭單步執行(與Build→Start Debug→Step Into選項的功能相同)。
Step Over:快捷鍵F10。單步執行程序,但當執行到函數調用語句時,不進入那一函數內部,而是一步直接執行完該函數后,接著再執行函數調用語句后面的語句。
Step Out:快捷鍵Shift+F11。與“Step Into”配合使用,當執行進入到函數內部,單步執行若干步之后,若發現不再需要進行單步調試的話,通過該選項可以從函數內部返回(到函數調用語句的下一語句處停止)。
Run to Cursor:快捷鍵Ctrl+F10。使程序運行到當前鼠標光標所在行時暫停其執行(注意,使用該選項前,要先將鼠標光標設置到某一個你希望暫停的程序行處)。事實上,相當于設置了一個臨時斷點,與Build→Start Debug→Run to Cursor選項的功能相同。
Insert/Remove Breakpoint:快捷鍵F9。本菜單項并未出現在Debug菜單上(在工具欄和程序文檔的上下文關聯菜單上),列在此處是為了方便大家掌握程序調試的手段,其功能是設置或取消固定斷點——程序行前有一個圓形的黑點標志,表示已經該行設置了固定斷點。另外,與固定斷點相關的還有Alt+F9(管理程序中的所有斷點)、Ctrl+F9(禁用/使能當前斷點)。假設準備編制進行如下計算任務的一個簡單程序:在已知x=3、y=5的情況下,先計算出x與y的和s,差d,商q,模r,而后計算res=s+2d+3q+4r的值(res應該等于16)并顯示在屏幕上。但編制的如下程序運行后卻得出了一個錯誤結果“res=26”。

#include <stdio.h>
void main()
{
int x=3, y=5;
int s, d, q, r, res;

s = x + y;
d = s - y;
q = x / y;
r = x % y;
res = s + 2*d + 3*q + 4*r;
printf("res=%d\n", res);
}
圖3-2 程序dbgTest.cpp的跟蹤調試

分析上述所編制的程序行,假設能在要輸出res結果值的那一程序行(倒數第二行)處設置一個臨時斷點,讓程序先執行到此斷點處(注意設為斷點的那一行尚未被執行!),看一看那時各變量的動態取值情況,有可能就會找到出錯的原因!基于上述分析,先將鼠標光標移動到“printf("res=%d\n", res);”那一行處(左鍵單擊那一行任意位置),從而指定了臨時性斷點的行位置,而后執行“Build→Start Debug→Run to Cursor”選項,使程序運行到所指定行時暫停其執行,并顯示出如圖3-2的界面,其中的左下方窗口中就列出了當時各變量的取值情況:和s=8,差d=3(x=3,y=5,它們的差d=3肯定是錯誤的!),商q=0,模r=3,最終結果res=26。再仔細查看程序中負責計算差d的那一個語句“d=s-y;”就會恍然大悟,原來將“x-y”誤寫成了“s-y”!找到了錯誤,此時可以通過菜單選項“Debug→Stop Debugging”,中斷當前的調試過程并返回正常的編輯狀態,修改所發現的錯誤后,再一次執行將能得出正確結果“res=16”。

順便指出,圖3-2中顯示的變量是“自動查看”方式的,即VC6自動顯示當前運行上下文中的變量的值。如果變量比較多,自動顯示的窗口比較混亂,則可以在 Watch 列表中添加自己想要監控的變量名。

上述設置臨時斷點(到鼠標光標那一行處)的調試手段使用起來很方便,會經常使用(也經常在到達一個斷點后,又設置另一個新的臨時斷點)。另外也常配合使用“單步執行”的方式,來仔細檢查每一步(一個程序行)執行后各變量取值的動態變化情況,如,先通過“Run to Cursor”執行到某一個鼠標光標臨時斷點行處,而后通過使用Debug菜單的“Step Over”或“Step Into”來進行所謂的“單步執行”,當然,每執行一步后,都要仔細觀察并分析系統自動給出的各變量取值的動態變化情況,以便及時發現異常而找到出錯原因。讓我們來分析并設計對如下程序進行調試的具體方法與手段(實際上,對不同的程序,都需要在分析其執行結果以及其程序編寫結構的基礎上,來設計相應的對其進行具體調試的方法與手段,宗旨是想方設法逐步縮小“偵察”范圍,直到最后找到出錯位置)。

該程序除main外,還有一個自定義函數f。若已經能確認調用f函數前計算出的res值(或s、d、q或r其中之一的結果值)不正確的話,則可像上一程序那樣,在計算出res變量值的下一行(或在靠前一些的某一行)處設置斷點,看到達那一斷點處是否一切正常。若到達斷點處的數據結果已經不正常的話,錯誤已經出現(出現在跟前或出現在前面,從而找到了錯誤或者縮小了“偵察”范圍);若斷點處仍然正常,可斷言錯誤出現在后面,而后,①可又一次通過鼠標光標往更靠后一些的適當位置設置新斷點,再一次“Debug→Run to Cursor”(一下向后“邁”過了許多行,再繼續“偵察”!);②通過“單步執行”(Debug→StepOver),在重點懷疑的那一塊地方仔細地逐行進行“偵察”。

注意,“Step Over”不會“跟蹤”進入f函數內部,若懷疑f函數可能有問題的話,要通過使用“Debug→Step Into”進入f內部再進行細致調試(在不遇到函數調用的地方,“Step Over”與“Step Into”的功能是相同的。若通過“Step Into”進入到函數內部,單步執行若干步之后,若發現不再需要進行單步調試的話,可通過“Step Out”從函數內部返回到調用語句的下一語句處)。

作為練習,請讀者利用這一程序對上述的調試方法與手段進行多方面的靈活使用與體驗!可以看出,程序調試是一件很費時費力而又非常細致的工作,需要耐心,要通過不斷的實踐來總結與積累調試經驗。至于VC6提供的其他調試方法與手段,這兒就不一一介紹了。

#include <stdio.h>
int f(int a)
{
int b, c;

b = a + 5;
c = 2*b + 100;
return c;
}

void main()
{
int x=3, y=5;
int s, d, q, r, res, z;

s = x + y;
d = x - y;
q = x / y;
r = x % y;
res = s + 2*d + 3*q + 4*r;
printf("res=%d\n", res);
z = f(36);
printf("z=%d\n", z);
}
前面也提到過,通過“Run to Cursor”所設置并到達的斷點是一個臨時性的斷點。實際上,VC6還提供設置與清除固定性斷點的方法。 設置固定性斷點最簡單的方法是:在某一程序行處,單擊鼠標右鍵,在菜單中選擇“Insert/Remove Breakpoint”項(通過左鍵單擊該選項,此時該行前將出現一個圓形的黑點標志,意味著已經將該行設置成了固定斷點)。

清除固定性斷點的方法為:在具有圓形黑點標志的固定斷點行處,單擊鼠標右鍵,在菜單中選擇“Remove Breakpoint”項(通過左鍵單擊該選項,此時該行前的那一個圓形黑點標志將消失,意味著已經清除了該固定斷點)。

設置了固定性斷點后,通常通過“Build→Start Debug→Go”或“Debug→Go”選項使程序開始執行,直到遇到某斷點或遇到程序結束而停止。

還要說明的是,可以隨時設置任意多個固定性斷點,也可以隨時清除它們。通過使用菜單選項“Edit→Breakpoints”,會出現一個對話框,在其中的“Break at”文本框中鍵入要設置斷點的程序行的行數信息(但通常是先通過鼠標光標選定某一程序行,再利用菜單選項進入上述對話框,而后通過點擊“Break at”文本框右邊的小三角按鈕,并選定系統自動提供的程序行的行數,以免自己要真正地去數清楚那一行的行數),也能夠在指定行處設置一個固定性斷點(通過OK按鈕確定);如果要清除某斷點,可在“Breakpoints”列表欄中先選定它,之后單擊Remove按鈕。實際上,除位置斷點外,通過“Edit→Breakpoints”,還可以設置數據斷點,消息斷點,以及條件斷點等,這兒就不再細說了。

VC6是一個極為龐大的開發工具,我們所介紹的僅僅是一些基本的應用,使用這些應用已經可以完成書中所涉及到的例子和作業,有興趣的讀者可通過參看其他有關介紹VC6的資料或書籍來進行進一步的學習與提高。

(1)程序運行結果看起來對了,但并不意味著程序沒有隱藏的問題

①以下程序是從鍵盤輸入一個數(x,x是一個int型整數),計算y(y=1000x+9)并在屏幕上輸出,程序基本上是對的,能輸出正確的結果。
#include <stdio.h>
void main()
{
int? x, y;

printf("Please input x:");
scanf("%d", &x);
y = 1000*x + 9;
printf("y=%d\n", y);
}
但是,當x輸入為5000000,屏幕上卻輸出y=705032713,出現了錯誤。

②以下程序的功能是從鍵盤上讀入一串字符,然后在屏幕上輸出。
#include <stdio.h>
void main()
{
char? str[10];

printf("Please input str:");
scanf("%s", &str);
printf("str=%s\n", str);
}
若用戶在輸入str時不小心多按了(或無意碰觸)幾下鍵盤,則程序運行會出錯,如圖3-3所示——可能稍嚴重的是程序運行死機(不報錯),特別嚴重的是若有人精心設計輸入的字符,完全可能造成系統被入侵或被嚴重破壞。
圖3-3 程序運行過程中因用戶的輸入而出錯
③以下程序的功能是從計算班中男生(boy)和女生(girl)的比例。如果女生數為0時,程序會怎樣?
#include <stdio.h>
void main()
{
int boy, girl;

printf("Please input boy,girl:");
scanf("%d,%d", &boy, &girl);
printf("boy/girl=%.2f\n", boy*1.0/girl);
}
實際工作中,類似的隱藏的問題往往是破壞力巨大的“地雷”,往往造成極大的損失,例如火箭發射、宇宙飛船飛行、高速列車運行中的事故,我們平常接觸最多的恐怕是微軟的各種系統需要不斷地打補丁。因此,在程序設計中,必須縝密考慮各種情況,哪怕是機率極小的意外。

(2)嚴謹思維的典范——程序員學習的榜樣

豐富的想象力、嚴謹的思維是一個優秀的程序應當具備的素質。 設計程序時不要拘泥于固定的思維方式,遇到問題的時候要多想幾種解決問題的方案,并且考慮全面、思維嚴謹。
以下2個小故事,應當是一個優秀程序員的標準思維,它形象、幽默、充分地展示了一個優秀程序員的嚴謹、全面的思維。一個優秀的程序員只有這樣思考并設計程序,才能保證程序始終能可靠、穩定地工作,減少和避免發生事故。

①方程僅僅對于正實數的簡單情形成立
物理教授走過校園,遇到數學教授。 物理教授在進行一項實驗,他總結出一個經驗方程,似乎與實驗數據吻合,他請數學教授看一看這個方程。 一周后他們碰頭,數學教授說這個方程不成立。可那時物理教授已經用他的方程預言出進一步的實驗結果,而且效果頗佳,所以他請數學教授再審查一下這個方程。 又是一周過去,他們再次碰頭。數學教授告訴物理教授說這個方程的確成立, "但僅僅對于正實數的簡單情形成立。"

②判斷開槍后樹上還有幾只鳥
某日,老師在課堂上想看看一學生智商有沒有問題,問他 “樹上有十只鳥,開槍打死一只,還剩幾只?”
他反問“是無聲手槍或別的無聲的槍嗎?”(例如激光槍)
“不是。”
“槍聲有多大?”
“80-100分貝。”
“那就是說會震的耳朵疼?”
“是。”
“在這個城市里打鳥犯不犯法?”
“不犯。”
“您確定那只鳥真的被打死啦?”
“確定。”偶已經不耐煩了“拜托,你告訴我還剩幾只就行了,OK”
“OK,樹上的鳥里有沒有聾子?”
“沒有。”
“有沒有關在籠子里的?”
“沒有。”
“邊上還有沒有其他的樹,樹上還有沒有其他鳥?”
“沒有。”
“有沒有殘疾的或餓的飛不動的鳥?”
“沒有。”
“算不算懷孕肚子里的小鳥?”
“不算。”
“打鳥的人眼有沒有花?保證是十只?”
“沒有花,就十只。” 偶已經滿腦門是汗,且下課鈴響,但他繼續問
“有沒有傻的不怕死的?”
“都怕死。”
“會不會一槍打死兩只?”?
“不會。”
“所有的鳥都可以自由活動嗎?”
“完全可以。”
“如果您的回答沒有騙人,”學生滿懷信心的說,“打死的鳥要是掛在樹上沒掉下來,那么就剩一只,如果掉下來,就一只不剩。”
老師當即暈倒。編譯、鏈接過程中,主要由于初學和錄入階段的擊鍵失誤,VC經常會提示程序有錯(語法和拼寫問題,肯定不會指明算法有問題,否則就不用編程了)。遇到這些英文的提示時,不少同學無從下手。一定要克服畏難情緒和一看英文就怕的心理,憑自己能考上大學的英語水平,只要仔細、一個單詞一個單詞地看,這些英文、包括在線幫助中的英文語句應基本上能看懂,個別單詞實在不認識就查一查,做IT的哪能不學英語,這本身也是在日常生活中學習英語的機會。再者,即便沒有完全理解、似懂非懂,也沒有很大關系,只要雙擊Output窗口中的出錯信息就可以自動跳到出錯的程序行,仔細查看,加上經驗的逐漸積累和人類舉一反三、觸類旁通的自我學習進步能力,解決這些簡單問題并非難事。

以下是一些常見的編譯、鏈接期間的程序出錯英文提示及相應的中文意思,供參考。


(1)error C2001: newline in constant

編號:C2001
直譯:在常量中出現了換行。
錯誤分析:
  1. ①字符串常量、字符常量中是否有換行。
  2. ②在這句語句中,某個字符串常量的尾部是否漏掉了雙引號。
  3. ③在這語句中,某個字符創常量中是否出現了雙引號字符“"”,但是沒有使用轉義符“\"”。
  4. ④在這句語句中,某個字符常量的尾部是否漏掉了單引號。
  5. ⑤是否在某句語句的尾部,或語句的中間誤輸入了一個單引號或雙引號。

(2)error C2015: too many characters in constant

編號:C2015
直譯:字符常量中的字符太多了。
錯誤分析:
單引號表示字符型常量。一般的,單引號中必須有且只能有一個字符(使用轉義符時,轉義符所表示的字符當作一個字符看待),如果單引號中的字符數多于4個,就會引發這個錯誤。
另外,如果語句中某個字符常量缺少右邊的單引號,也會引發這個錯誤,例如:
if (x == 'x || x == 'y') { … }
值得注意的是,如果單引號中的字符數是2-4個,編譯不報錯,輸出結果是這幾個字母的ASC碼作為一個整數(int,4B)整體看待的數字。

(3)error C2137: empty character constant

編號:C2137
直譯:空的字符定義。
錯誤分析:
原因是連用了兩個單引號,而中間沒有任何字符,這是不允許的。

(4)error C2018: unknown character '0x##'

編號:C2018
直譯:未知字符‘0x##’。
錯誤分析:
0x##是字符ASC碼的16進制表示法。這里說的未知字符,通常是指全角符號、字母、數字,或者直接輸入了漢字。如果全角字符和漢字用雙引號包含起來,則成為字符串常量的一部分,是不會引發這個錯誤的。

(5)error C2041: illegal digit '#' for base '8'

編號:C2141
直譯:在八進制中出現了非法的數字‘#’(這個數字#通常是8或者9)。
錯誤分析:
如果某個數字常量以“0”開頭(單純的數字0除外),那么編譯器會認為這是一個8進制數字。例如:“089”、“078”、“093”都是非法的,而“071”是合法的,等同于是進制中的“57”。

(6)error C2065: 'xxxx' : undeclared identifier

編號:C2065
直譯:標識符“xxxx”未定義。
錯誤分析:
首先,解釋一下什么是標識符。標志符是程序中出現的除關鍵字之外的詞,通常由字母、數字和下劃線組成,不能以數字開頭,不能與關鍵字重復,并且區分大小寫。變量名、函數名、類名、常量名等等,都是標志符。所有的標志符都必須先定義,后使用。 標志符有很多種用途,所以錯誤也有很多種原因。
  1. 如果“xxxx”是一個變量名,那么通常是程序員忘記了定義這個變量,或者拼寫錯誤、大小寫錯誤所引起的,所以,首先檢查變量名是否正確。(關聯:變量,變量定義)
  2. 如果“xxxx”是一個函數名,那就懷疑函數名是否沒有定義。可能是拼寫錯誤或大小寫錯誤,當然,也有可能是你所調用的函數根本不存在。還有一種可能,你寫的函數在你調用所在的函數之后,而你有沒有在調用之前對函數原形進行申明。(關聯:函數申明與定義,函數原型)
  3. 如果“xxxx”是一個庫函數的函數名,比如“sqrt”、“fabs”,那么看看你在cpp文件已開始是否包含了這些庫函數所在的頭文件(.h文件)。例如,使用“sqrt”函數需要頭文件math.h。如果“xxxx”就是“cin”或“cout”,那么一般是沒有包含“iostream.h”。(關聯:#include,cin,cout)
  4. 如果“xxxx”是一個類名,那么表示這個類沒有定義,可能性依然是:根本沒有定義這個類,或者拼寫錯誤,或者大小寫錯誤,或者缺少頭文件,或者類的使用在申明之前。(關聯:類,類定義)
  5. 標志符遵循先申明后使用原則。所以,無論是變量、函數名、類名,都必須先定義,后使用。如使用在前,申明在后,就會引發這個錯誤。
  6. C++的作用域也會成為引發這個錯誤的陷阱。在花括號之內變量,是不能在這個花括號之外使用的。類、函數、if、do(while)、for所引起的花括號都遵循這個規則。(關聯:作用域)
  7. 前面某句語句的錯誤也可能導致編譯器誤認為這一句有錯。如果你前面的變量定義語句有錯誤,編譯器在后面的編譯中會認為該變量從來沒有定義過,以致后面所有使用這個變量的語句都報這個錯誤。如果函數申明語句有錯誤,那么將會引發同樣的問題。

(7)error C2086: 'xxxx' : redefinition

編號:C2374
直譯:“xxxx”重復申明。
錯誤分析:
變量“xxxx”在同一作用域中定義了多次。檢查“xxxx”的每一次定義,只保留一個,或者更改變量名。

(8)error C2374: 'xxxx' : redefinition; multiple initialization

編號:C2374
直譯:“xxxx”重復申明,多次初始化。
錯誤分析:
變量“xxxx”在同一作用域中定義了多次,并且進行了多次初始化。檢查“xxxx”的每一次定義,只保留一個,或者更改變量名。

(9)C2143: syntax error : missing ';' before (identifier) 'xxxx'

編號:C2143
直譯:在(標志符)“xxxx”前缺少分號。
錯誤分析:
這是VC6的編譯期最常見的誤報,當出現這個錯誤時,往往所指的語句并沒有錯誤,而是它的上一句語句發生了錯誤。其實,更合適的做法是編譯器報告在上一句語句的尾部缺少分號。 上一句語句的很多種錯誤都會導致編譯器報出這個錯誤:
  1. 上一句語句的末尾真的缺少分號。那么補上就可以了。
  2. 上一句語句不完整,或者有明顯的語法錯誤,或者根本不能算上一句語句(有時候是無意中按到鍵盤所致)。
  3. 如果發現發生錯誤的語句是cpp文件的第一行語句,在本文件中檢查沒有錯誤,但其使用雙引號包含了某個頭文件,那么檢查這個頭文件,在這個頭文件的尾部可能有錯誤。

(10)error C4716: 'xxx' : must return a value

編號:C4716
直譯:“xxx”必須返回一個值。
錯誤分析:
函數聲明了有返回值(不為void),但函數實現中忘記了return 返回值。要么函數確實沒有返回值,則修改其返回值類型為void,要么在函數結束前返回合適的值。

(11) warning C4508: 'main' : function should return a value; 'void' return type assumed

編號:C4508
直譯:main函數應該返回一個值;void返回值類型被假定。
錯誤分析:
  1. 函數應該有返回值,聲明函數時應指明返回值的類型,確實無返回值的,應將函數返回值聲明為void。若未聲明函數返回值的類型,則系統默認為整型int。此處的錯誤估計是在main函數中沒有return返回值語句,而main函數要么沒有聲明其返回值的類型,要么聲明了。
  2. warning類型的錯誤為警告性質的錯誤,其意思是并不一定有錯,程序仍可以被成功編譯、鏈接,但可能有問題、有風險。

(12)warning C4700: local variable 'xxx' used without having been initialized

編號:C4700
直譯:警告局部變量“xxx”在使用前沒有被初始化。
錯誤分析:
這是初學者常見的錯誤,例如以下程序段就會造成這樣的警告,而且程序的確有問題,應加以修改,盡管編譯、鏈接可以成功——若不修改,x的值到底是多少無法確定,是隨機的,判斷其是否與3相同沒有意義,在運氣不好的情況下,可能在調試程序的機器上運行時,結果看起來是對的,但更換計算機后再運行,結果就不對,初學者往往感到迷惑。
int x;
if (x==3) printf("hello");

(1)error LNK2001: unresolved external symbol _main

編號:LNK2001
直譯:未解決的外部符號:_main。
錯誤分析:缺少main函數。看看main的拼寫或大小寫是否正確。

(2)error LNK2005: _main already defined in xxxx.obj

編號:LNK2005
直譯:_main已經存在于xxxx.obj中了。

錯誤分析:
直接的原因是該程序中有多個(不止一個)main函數。這是初學C++的低年級同學在初次編程時經常犯的錯誤。這個錯誤通常不是你在同一個文件中包含有兩個main函數,而是在一個project(項目)中包含了多個cpp文件,而每個cpp文件中都有一個main函數。引發這個錯誤的過程一般是這樣的:你寫完成了一個C++程序的調試,接著你準備寫第二個C++文件,于是你可能通過右上角的關閉按鈕關閉了當前的cpp文件字窗口(或者沒有關閉,這一操作不影響最后的結果),然后通過菜單或工具欄創建了一個新的cpp文件,在這個新窗口中,程序編寫完成,編譯,然后就發生了以上的錯誤。原因是這樣的:你在創建第二個cpp文件時,沒有關閉原來的項目,所以你無意中新的cpp文件加入你上一個程序所在的項目。切換到“File View”視圖,展開“Source Files”節點,你就會發現有兩個文件。
在編寫C++程序時,一定要理解什么是Workspace、什么是Project。每一個程序都是一個Project(項目),一個Project可以編譯為一個應用程序(*.exe),或者一個動態鏈接庫(*.dll)。通常,每個Project下面可以包含多個.cpp文件,.h文件,以及其他資源文件。在這些文件中,只能有一個main函數。初學者在寫簡單程序時,一個Project中往往只會有一個cpp文件。Workspace(工作區)是Project的集合。在調試復雜的程序時,一個Workspace可能包含多個Project,但對于初學者的簡單的程序,一個Workspace往往只包含一個Project。

當完成一個程序以后,寫另一個程序之前,一定要在“File”菜單中選擇“Close Workspace”項,已完全關閉前一個項目,才能進行下一個項目。避免這個錯誤的另一個方法是每次寫完一個C++程序,都把VC6徹底關掉,然后重寫打開VC6,寫下一個程序。


本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/450496.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/450496.shtml
英文地址,請注明出處:http://en.pswp.cn/news/450496.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

vim搜索替換工具

1、ag.vim(查找工具)安裝 在vimrc中添加 Plug rking/ag.vim 使用 :Ag 你要查找的內容 e open file and close the quickfix window. o open file (same as enter). go preview file (open but mainta…

JAVA好學嗎?工資待遇怎么樣?

時代的進步也離不開互聯網的推動&#xff0c;互聯網的發展已經形成了一種趨勢&#xff0c;而在這種大環境下我們不應該抗拒&#xff0c;如果自身條件允許&#xff0c;加入進來未嘗不是一個好的選擇。而在計算機行業內&#xff0c;JAVA的火爆毋庸自疑&#xff0c;很多人都意識到…

【project】十次方-01

前言 項目介紹 系統分為3大部分&#xff1a;微服務、網站前臺、網站管理后臺&#xff1b;功能模塊分為&#xff1a;問答、招聘、交友中心等 該項目融合了Docker容器化部署、第三方登陸、SpringBoot、SpringCloud、SpringData、RabbitMQ等&#xff0c;該項目采用完全的前后端分離…

Docker 容器 和 虛擬機 的異同

見&#xff1a;https://www.docker.com/what-container 將軟件打包成標準化的單元進行開發&#xff0c;發貨和部署 容器映像是一個軟件的輕量級獨立可執行軟件包&#xff0c;包含運行所需的所有內容&#xff1a;代碼&#xff0c;運行時&#xff0c;系統工具&#xff0c;系統庫&…

iTerm2 快捷鍵大全

轉載地址https://cnbin.github.io/blog/2015/06/20/iterm2-kuai-jie-jian-da-quan/ 標簽 新建標簽&#xff1a;command t關閉標簽&#xff1a;command w切換標簽&#xff1a;command 數字 command 左右方向鍵切換全屏&#xff1a;command enter查找&#xff1a;command …

大型軟件編程規范

“安全第一”的C語言編程規范 編者按&#xff1a;C語言是開發嵌入式應用的主要工具&#xff0c;然而C語言并非是專門為嵌入式系統設計&#xff0c;相當多的嵌入式系統較一般計算機系統對軟件安全性有更苛刻的要求。1998年&#xff0c;MISRA指出&#xff0c;一些在C看來可以接受…

設計行業的新寵——云渲染

無論是對任職設計崗位的人員還是專業的設計公司來說&#xff0c;3D渲染&#xff0c;都是工作中極其重要的一步。在做過渲染后&#xff0c;設計的作品才能展現出它最接近真實世界的狀態。 但是由于渲染實質上是對大量數據的處理&#xff0c;所以渲染這項工作對電腦硬件的要求非常…

[Xcode 實際操作]七、文件與數據-(17)解析JSON文檔

目錄&#xff1a;[Swift]Xcode實際操作 本文將演示如何解析JSON文檔。 項目中已添加一份JSON文檔&#xff1a;menu.json 1 {2 "menu":3 {4 "id": "file",5 "value": "File",6 "menuit…

Docker,容器,虛擬機和紅燒肉

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 Docker是什么&#xff1f;容器又是什么&#xff1f; Docker 直譯&#xff1a;碼頭工人。是在碼頭上搬運貨物的工人容器 英文&#xff1…

mac終端操作文件或文件夾(持續更新)

1、復制文件夾&#xff08;有文件&#xff09; cp -R 要復制的文件 要復制到哪個路徑 2、復制文件 cp 要復制的文件 要復制到哪個路徑 3、移動文件夾 mvdir 你要移動的文件夾 要移動到哪里

前端進階系列(六):盒模型

盒模型是界面布局需要掌握的基本功。盒模型基本概念 盒模型四要素&#xff1a;margin、border、padding、content。 盒模型分為&#xff1a;標準盒模型&#xff08;W3C盒模型&#xff09; 、 怪異盒模型&#xff08;IE盒模型&#xff09; 盒模型區別 怪異盒模型總寬度 content…

holer實現外網訪問內網數據庫

外網訪問本地數據庫 本地安裝了數據庫&#xff0c;只能在局域網內訪問&#xff0c;怎樣從公網也能訪問內網數據庫&#xff1f; 本文將介紹使用holer實現的具體步驟。 1. 準備工作 1.1 安裝并啟動數據庫 默認安裝的數據庫端口是3306。 2. 實現步驟 2.1 下載并解壓holer軟件包 Ho…

Docker 概念解析

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 Docker的功能定位Docker為基于Linux容器的開源項目&#xff0c;其利用Linux內核中的各項功能——例如命名空間與控制組——以在操作系統之…

C語言 常用API

MySQL的C語言API接口 1、首先當然是連接數據庫&#xff0c;函數原型如下&#xff1a; MYSQL * STDCALL mysql_real_connect(MYSQL *mysql, const char *host,const char *user,const char *passwd,const char *db,unsigned int port,const char *unix_socket,unsigned long …

hadoop生態搭建(3節點)-10.spark配置

# https://www.scala-lang.org/download/2.12.4.html# 安裝 scala tar -zxvf ~/scala-2.12.4.tgz -C /usr/local rm –r ~/scala-2.12.4.tgz # http://archive.apache.org/dist/spark/spark-2.3.0/ # 安裝 spark tar -zxf ~/spark-2.3.0-bin-hadoop2.7.tgz -C /usr/local mv /u…

持續集成coding

1、安裝docker yum -y install docker yum -y install composer yum -y install docker-compose 2、啟動docker服務 service docker start 3、測試安裝結果 docker-compose --version 4、創建目錄 mkdir /data/continus-deploy 5、寫入docker-compose.yml version: …

JSON字符串轉換為Map

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 本文是利用阿里巴巴封裝的FastJSON來轉換json字符串的。例子如下&#xff1a; [java] view plain copy package com.zkn.newlearn.json;…

排序與查找 詳細分析

C語言五種基本排序算法 程序員可以使用的基本排序算法有5種&#xff1a; 插入排序(insertionsort&#xff0e;)交換排序(exchangesOrt)選擇排序(selectionsort)歸并排序(mergesort)分布排序(distributionsort) 為了形象地解釋每種排序算法是怎樣工作的&#xff0c;讓我們來看…

《Netkiller Spring Cloud 手札》Spring boot 2.0 mongoTemplate 操作范例

2019獨角獸企業重金招聘Python工程師標準>>> 本文節選自 《Netkiller Spring Cloud 手札》 Netkiller Spring Cloud 手札 Spring Cloud Cookbook Mr. Neo Chan, 陳景峯(BG7NYT) 中國廣東省深圳市望海路半島城邦三期 518067 86 13113668890<netkillermsn.com> …