變量 還是從變量講起來吧。變量(variable)實際上是賦予內存地址的名稱。聲明變量后,就可以用它操作內存中的數據。下面舉幾個例子進行說明。下列碼段用了兩個變量,每條語句末尾用說明語句描述執行該語句時發生的情況: int x;// variable declared as an integer variable x = 100;// 'x' now contains the value 100 x +=50;// 'x' now contains the value 150 int y = 150;// 'y' declared and initialized to 150 x += y;// 'x' now contains the value 300 x++;// 'x' now contains the value 301 新術語 變量(variable)是留作存放某個數值的計算機內存地址。注重x的值在變量操作時會改變,稍后會介紹操作變量的C++操作符。警告 聲明而未初始化的變量包含隨機值。由于變量所指向的內存還沒有初始化,所以不知道該內存地址包含什么值。 例如,下列代碼 int k; int y; x=y+10; //oops! 本例中變量y沒有事先初始化,所以x可能取得任何值。例外的情況是全局變量和用static修飾聲明的變量總是初始化為0。而所有其它變量在初始化或賦值之前包含隨機值。變量名可以混合大寫、小寫字母和數字與下劃線(_),但不能包含空格和其它非凡字符。變量名必須以字母或下劃線開始。一般來說,變量名以下劃線或雙下劃線開始不好。變量名答應的最大長度隨編譯器的不同而不同。假如變量名保持在32個字符以下,則絕對安全。實際中,任何超過20個字符的變量名都是不實用的。 下例是有效變量名的例子: int aVeryLongVariableName;// a long variable name int my_variable;// a variable with an underscore int_ x;// OK,but not advisedint X;// uppercase variable name int Labe12;// a variable name containing a number int GetItemsInContainer(); // thanks Pete! 說明 C++中的變量名是考慮大小寫的,下列變量是不同的:int XPos;int xpos;假如你原先所用語言不考慮大小寫(如Pascal),則開始接觸考慮大小寫的語言可能不太適應。
C++數據類型 新術語 C++數據類型定義編譯器在內存中存放信息的方式。在有些編程語言中,可以向變量賦予任何數值類型。例如,下面是BASIC代碼的例子:x = 1;x = 1000;x = 3.14;x = 457000;在BASIC中,翻譯器能考慮根據數字長度和類型分配空間。而在C++,則必須先聲明變量類型再使用變量:int x1 = 1;int x = 1000;float y = 3.14;long z = 457000;這樣,編譯器就可以進行類型檢查,確保程序運行時一切順利。數據類型使用不當會導致編譯錯誤或警告,以便分析和糾正之后再運行。有些數據類型有帶符號和無符號兩種。帶符號(signed)數據類型可以包含正數和負數,而無符號(unsigned)數據類型只能包含正數。表1.1列出了C++中的數據類型、所要內存量和可能的取值范圍。 表1.1C++數據類型(32位程序) 數據類型 字節 數取值范圍 char 1 -128到126 unsigned char 1 0到255 short 2 -32,768到32,767 unsigned short 2 0到65,535 long 4 -2,147,483,648到2,147,483,648 unsigned long 4 0到4,294,967,295 int 4 同long unsigned int 4 同unsigned long float 4 1.2E-38到3.4E381 double 8 2.2E-308到1.8E3082 bool 1 true或false 從上表可以看出,int與long相同。那么,為什么C++還要區分這兩種數據類型呢?實際上這是個遺留問題。在16位編程環境中,int要求2個字節而long要求4個字節。而在32位編程環境中,這兩種數據都用4個字節存放。C++Builder只生成32位程序,所以int與long相同。說明 在C++ Builder和BorLand C++ 5.0中,Bool是個真正的數據類型。有些C++編譯器有Bool要害字,則Bool不是個真正的數據類型。有時Bool只是個typedef,使Bool等價于int。typedef實際上建立別名,使編譯器在一個符號與另一符號間劃上等號。typedef的語法如下:typedef int Bool;這就告訴編譯器:Bool是int的別名。說明 只有double和float數據類型使用浮點數(帶小數點的數)。其它數據類型只涉及整數值。盡管integer數據類型也可以指定帶小數點的數值,但小數部分會舍棄,只將整數部分賦予整型變量,例如:int x=3.75;得到的x取值為3。注重,這個整數值并不是四舍五入,而是放棄小數部分。順便說一句,大多數Windows程序很少用到浮點數。C++可以在必要時進行不同數據類型間的換算。例如:short result;long num1 = 200;long num2 = 200;result = num1 * num2;這里我想將兩個長整型的積賦予一個短整型。盡管這個公式混用了兩種數據類型,但C++能夠進行換算。計算結果會怎樣呢?結果會讓你大吃一驚,是25536,這是繞接(wrop)的結果。從表1.1可以看出,短整型的最大取值為32767,在最大值之上加1會怎么樣呢?得到的是32768。這實際上與汽車里程計從99999回到00000的道理一樣。為了說明這點,請輸入并運行下列清單1.3中包含的程序。 清單1.3Wrapme.cpp 1: #include <iostream.h> 2: #include <conio.h> 3: #PRagma hdrstop 4: 5: int main(int argc,char **argv) 6: { 7:short x = 32767; 8:cout << " x = " << x << endl; 9:x++; 10: cout << " x = " << x << endl; 11: getch(); 12: return 0; 13: } 說明后面幾節要介紹的有些清單沒有下列語句: #include<condefs.h> C++ Builder生成新的控制臺應用程序時會自動加上這條語句。這在你所用的程序中不是必須的,所以代碼清單中將其省略。無論有無這條語句,程序運行結果是一致的。分析輸出結果為:x=32767 x=32768假如用int數據類型,則不會有這個問題,因為int數據類型的取值范圍在正向20億之間,一般不會有繞回的問題。但這時程序可能會稍大一些,因為int需要4字節存儲,而short只需要2字節存儲。對于大多數應用程序,這個差別是不顯著的。前面介紹了自動類型換算。有時C++無法進行換算,這時可能在編譯器中產生編譯錯誤,說Cannot convert from x to y(無法從x換算到Y)。編譯器也可能警告說Conversion might lose significant digits(換算可能丟失顯著位)。提示 編譯器警告應當作編譯器錯誤,因為它表明出了錯誤。我們應努力生成無警告的編譯。有時警告無法避免,但一定要認真檢查所有警告。應充分了解警告的原因并盡量予以糾正。
main()函數 C++程序必須有main()函數。main()函數是程序的入口點。前面介紹的每個樣本程序都有main()函數。但是,并非所有C++程序都有傳統的main()函數。用C或C++寫成的Windows程序入口點函數稱為WinMain(),而不是傳統的main()函數。說明 C++ Builder GUI應用程序有WinMain(),但隱藏起來了。C++ Builder使用戶無需考慮Windows程序的低級細節,而可以集中考慮程序用戶界面和其它部分的創建。main()函數和其它函數一樣是函數,有相同的構成部分。在32位控制臺應用程序中,C++ Builder生成具有下列原型的缺省main()函數:int main(int argc,char** argv);這個main()函數形式取兩個參數并返回一個整型值。前面說過,數值在調用函數時傳遞給函數。但對于main()函數,沒有直接調用,而是在程序運行時自動執行。那么,main()函數如何取得參數呢?辦法是從命令行取得。現說明如下:假設有個Win32控制臺應用程序要在DOS提示下用下列命令行執行:grep WM_KILLFOCUS 杁 -i 這里要用命令行變元WM_KILLFOCUS、d和i啟動程序grep,我們要演示如何在main()函數中將其變為argc和argv.首先,整型變量argc包含命令行中傳遞的參數個數,至少為1,因為程序名也算作參數。變量argv是個數組,包含字串的指針。這個數組包含命令行中傳遞的每個字串。本例中: argc包含4 argv[0] 包含C:cbuilderbingrep.exe argv[1] 包含WM_KILLFOCUS argv[2] 包含 d argv[3] 包含 i 下面用一個小程序驗證這個事實。在C++ Builder中生成新的控制臺應用程序并輸入清單1.5所示的程序。清單1.5Argstest.cpp 1: #include <iostream.h> 2: #include <conio.h> 3: #pragma hdrstop 4: 5: int main(int argc,char **argv) 6: { 7:cout << "argv = "argc << end1; 8.for (int i=0;i<argc;i++) 9. cout << "Parameter " << i << ": " << argv[i]<< end1; 10. cout << end1 << "Press any key to continue..."; 11: getch(); 12: return 0; 13: } 將這個項目存為Argstest,然后不是單擊Run按鈕,而是選擇主菜單中的ProjectBuild All,這樣只建立項目而不執行程序。項目建成后,選擇主菜單中的RunParameters,在RunParameters對話框RunParameters字段中輸入下列內容:one two three "four five" six然后單擊Run按鈕,程序即用所指定的命令行參數運行。另一種辦法是用下列命令行在DOS提示下運行程序:argstest one two three "four five" six程序運行時,它會顯示傳入的變元數,然后列出每個變元。運行幾次,每次提供不同命令行變元,注重產生的結果。 大多數程序中main()函數的返回值并不重要,因為通常不使用返回值。事實上,可以不要求main()函數返回數值。main()函數的形式有多種,下列聲明均有效:main();int main();// same as above int main(void); // same as above int main(int argc,char** argv); void main(); void main(int argc, char** argv); 還有更多的形式。假如不想使用命令行變元,則可以用第一種main()函數形式,其不取參數(括號內為空的)并返回一個int(不指定時返回缺省返回值)。換句話說main()函數最基本的形式不取參數并返回一個int。
數組 任何C++固有數據類型都可以放進數組中。數組(array)就是數值的集合。例如,假設要保存一個整型數組,放五個整型值??梢月暶鲾到M如下:int myArray[5];這里編譯器為數組分配圖1.7所示的內存空間。由于每個int要4個字節存儲,所以整個數組占用20字節的內存空間。 mArray[0]mArray[1]mArray[2]mArray[3] mArray[4] baseAddrbasseAddr+4baseAddr+8 baseAddr+12baseAddr+16 聲明數組后,就可以用如下腳標操作符([])填入數值: myArray[0] = -200; myArray[1] = -100; myArray[2] = 0; myArray[3] = 100; myArray[4] = 200; 由上可見,C++中的數組是以0為基數的。后面程序中可以用腳標操作符訪問數組的各個元素: int result=myarray[3]+myArray[4]; // result will be 300 還有一次聲明和填入整個數組內容的簡捷方法如下: int myArray[5] = {-200, -100,0,100,200}; 進一步說,假如知道數組的元素個數,并在聲明數組時填充數組,則聲明數組時連數組長度都可以省略。例如:int myArray[] = {-200, -100,0,100,200 };這是可行的,因為編譯器從賦予的數值表可以判定出數組中元素的個數和分配給數組的內存空間。 數組可以是多維的。為了生成兩維整型數組,可用下列代碼: int mdArray[3][5]; 這樣就分配15個int空間(共60字節)。數組的元素可以和一維數組一樣訪問,只是要提供兩個腳標操作符:int x = mdArray[1][1]+mdArray[2][1]; 圖1.8兩維數組在內存中的樣子警告 注重不要重載數組末尾。 C++一個強大的特性是能直接訪問內存。由于這個特性,C++無法阻止你寫入特定內存地址,即使這個地址是程序不讓訪問的。下列代碼是合法的,但會導致程序或Windows崩潰:int array[5];array[5]=10;這是常見的錯誤,因為數組是以0為基數的,最大腳標應是4而不是5。假如重載數組末尾,則無法知道哪個內存被改寫了,使結果難以預料,甚至會導致程序或Windows崩潰。這類問題很難診斷,因為受影響的內存通常要在很久以后才訪問,這時才發生崩潰(讓你莫名其中之妙)。所以寫入數組時一定要小心。 數組規則 ·數組是以0為基數。數組中的第一個元素為0,第二個元素為1,第三個元素為2,等等。 ·數組長度應為編譯常量。編譯器在編譯時必須知道為數組分配多少內存空間。不能用變量指定數組長度。所以下列代碼不合法,會導致編譯錯誤: int x = 10;int myArray[x]; // compiler error here· 小心不要重載數組末尾。 · 大數組從堆疊(heap)而不是堆棧(stack)中分配(詳見稍后)?!?從堆疊分配的數組可以用變量指定數組長度。例如:int x = 10;int* myArray = new int[x]; // this is OK
字符數組 希奇的是,C++不支持字串變量(放置文本的變量),C++程序中的字串是用char數據類型的數組表示的。例如,可以將變量賦予char數組如下: char text[] = "This is a string."; 這就在內存中分配18字節的內存空間用于存放字串。根據你的領悟能力,也許你會發現該字串中只有17個字符。分配18個字節的原因是字串要以終止null結尾,C++在分配內存空間時把終止null算作一個字符。 新術語 終止null是個非凡字符,用0表示,等于數值0。程序碰到字符數組中的0時,表示已經到字串末尾。為了說明這點,輸入并運行下列控制臺應用程序。 清單1.6Nulltest.cpp 1: #include <iostream.h> 2: #include <conio.h> 3: #pragma hdrstop 4: 5: int main(int argc,char **argv) 6: { 7:char str[]="This is a string."; 8.cout << str << end1; 9.str[7]= '/0'; 10. cout << str << end1 11. cout << end1 << "Press any key to continue..."; 12: getch(); 13: return 0; 14: } 分析 最初,字符數組包含字符串This is a string和一個終止null,這個字串通過cout送到屏幕上。下一行將數組的第7個元素賦值為0,即終止null。字串再次發送到屏幕上,但這時只顯示This is。原因是計算機認為數組中字串在第7個元素上終止,余下字串仍然在內存空間中,但不顯示,因為碰到了終止null。圖1.10演示了將數組的第7個元素賦值為0的語句前后的字符數組。 之前 Thi sisastri ng./0 之后 This is/0astri ng./0 圖1.10字符數組的內容 清單1.6中也可以賦值0而不是'0',結果相同,因為數字0和char數據類型'0'是等值的。例如,下列語句是等價的: str[7] = '0'; str[7] = 0; 說明 C++程序中單引號與雙引號是有差別的。向數組元素賦值終止null和其它字符值時,必須用單引號。單引號的作用是將引號內的字符變成整型值(該字符的ASCII值),然后將這個值存放在內存地址中。將字串賦予字符數組時,必須用雙引號。假如用錯引號,則編譯器會發生編譯錯誤。
字串操作函數 假如你用過具有string數據類型的編程語言,你可能很不習慣,別人也有同感,所以標準C語言庫中提供了幾個字串操作函數。表1.3列出了最常用的字串操作函數及其用法說明。關于每個函數的具體說明和實例,見C++ Builder聯機幫助。 表1.3字串操作函數 函數 說明 strcat() 將字串接合到目標字串的末尾 strcmp() 比較兩個字串是否相等 strcmpi() 比較兩個字串是否相等,不考慮大小寫 strcpy() 將字串內容復制到目標字串中 strstr() 掃描字串中第一個出現的字串 strlen() 返回字串長度 strupr() 將字串中的所有字符變成大寫 sprintf() 根據幾個參數建立字串 說明 這里介紹的字串操作是C語言中的字串處理方法。大多數C++編譯器提供了cstring類,可以簡化字串的處理(C++ Builder的Visual構件庫中有個AnsiString類,可以處理字串操作。C++ Builder聯機幫助中具體介紹了AnsiString類)。盡管C語言中的字串處理方法比較麻煩,但并不過時,C++編程人員經常在使用cstring類和AnsiString類等字串類的同時使用C語言中的字串處理方法。這里不想對表中的每個函數進行舉例說明,只想舉兩個最常用的函數。strcpy()函數將一個字串復制到另一字串中,源字串可以是變量或直接字串。例如下列代碼: //set up a string to hold 29 characters char buff[30]; //copy a string literal to the buffer strcpy (buff,"This is a test.");//display it cout << buff << end; //initialize a second string buffer char buff2[]="A second string."; //copy the contents of this string to the first buffer strcpy (buff,buff2); cout << buff << end1; 字符數組中比數字數組中更輕易重載數字末尾。例如下列代碼: char buff[10]= "A string";// later.... strcpy(buff,"This is a test."); //oops! 這里建立了放10個字符的字符數組,最初指定需要9個字節的字符串(記住終止null)。后來可能忘記了數組長度,將需要16個字節的字串復制到了緩沖區,對數組重載了六個字節。這個小小錯誤就擦去了某個內存位置上的六個字節。所以將數據復制到字符數組中時要非凡小心。另一個常用的字串函數是sprintf()。這個函數可以混合文本和數字建立格式化字串。下面例子將兩個數相加,然后用sprintf()建立字串以報告結果: char buff[20]; int x = 10 * 20; sprintf(buff,"The result is: %d",x); cout << buff; 執行這個碼段時,程序顯示下列結果:The result is:200 本例中%d告訴sprintf()函數此處有個整型值,格式字串末尾插入變量x,告訴sprintf()在字串的這個位置放上變量x的值。sprintf()是個非凡的函數,可以取多個變元。你必須提供目標緩沖區和格式字串,但格式字串后面的變元數是個變量。下面的sprintf()例子用了另外三個變元: int x = 20; int y = 5; sprintf(buff, "%d + %d", x, y, x + y); cout << buff; 執行這個碼段時,屏幕上顯示的結果如下:20 + 5 = 25 說明 C++字串中的單斜杠表示非凡字符。例如,'/n'表示新行符,'/t'表示跳表符。為了在字串中放上實際的斜杠,要用雙斜杠如下: strcpy(fileName, "c://windows//system//win.ini"); 許多編程人員因為忘了這個簡單的事實而夜不能寐,苦苦折騰。這是個常見的錯誤,別說我沒有告訴你。sprintf()有個兄弟叫wsprintf(),是Windows版的sprintf().Windows程序中可能同時用這兩個函數。wsprintf()與sprintf()的作用相似,唯一的差別是不能在格式字串中放上浮點數。C++ Builder程序中兩個函數均可使用,但用sprintf()更好,因為它完全支持浮點數(還可以少輸入一個字符)。關于sprintf()的進一步介紹,見C++ Builder聯機幫助。
字串數組不僅可以有字符數組,還可以有字符數組的數組(即字串數組)。這聽起來有點復雜,其實前面的Argstest程序中已經用過。這類數組可以分配如下: char strings[][20] = { "This is string 1", "This is string 2", "This is string 3", "This is string 4"}; 這個代碼生成四個字串的數組,每個字串最多放19個字符。盡管可以使用這種字串數組,但C++ Builder中還有更簡單的字串數組處理辦法(將在后面介紹C++ Builder時介紹)。說明 假如經常用到字串數組,應當看看標準模板庫(STL).STL提供了比用C語言式字符數組更方便地存放和操作字串數組的方法.STL中還有個string類。