六月婷婷综合激情-六月婷婷综合-六月婷婷在线观看-六月婷婷在线-亚洲黄色在线网站-亚洲黄色在线观看网站

明輝手游網中心:是一個免費提供流行視頻軟件教程、在線學習分享的學習平臺!

C++程序設計從零開始之語句

[摘要]前面已經說過程序就是方法的描述,而方法的描述無外乎就是動作加動作的賓語,而這里的動作在C++中就是通過語句來表現的,而動作的賓語,也就是能夠被操作的資源,但非?上У谻++語言本身只支持一種資源——內存。由于電腦實際可以操作不止內存這一種資源,導致C++語言實際并不能作為底層硬件程序的編寫語言(即...
前面已經說過程序就是方法的描述,而方法的描述無外乎就是動作加動作的賓語,而這里的動作在C++中就是通過語句來表現的,而動作的賓語,也就是能夠被操作的資源,但非?上У谻++語言本身只支持一種資源——內存。由于電腦實際可以操作不止內存這一種資源,導致C++語言實際并不能作為底層硬件程序的編寫語言(即使是C語言也不能),不過各編譯器廠商都提供了自己的嵌入式匯編語句功能(也可能沒提供或提供其它的附加語法以使得可以操作硬件),對于VC,通過使用__asm語句即可實現在C++代碼中加入匯編代碼來操作其他類型的硬件資源。對于此語句,本系列不做說明。

  語句就是動作,C++中共有兩種語句:單句和復合語句。復合語句是用一對大括號括起來,以在需要的地方同時放入多條單句,如:{ long a = 10; a += 34; }。而單句都是以“;”結尾的,但也可能由于在末尾要插入單句的地方用復合語句代替了而用“}”結尾,如:if( a ) { a--; a++; }。應注意大括號后就不用再寫“;”了,因為其不是單句。

  方法就是怎么做,而怎么做就是在什么樣的情況下以什么樣的順序做什么樣的動作。因為C++中能操作的資源只有內存,故動作也就很簡單的只是關于內存內容的運算和賦值取值等,也就是前面說過的表達式。而對于“什么樣的順序”,C++強行規定只能從上朝下,從左朝右來執行單句或復合語句(不要和前面關于表達式的計算順序搞混了,那只是在一個單句中的規則)。而最后對于“什么樣的情況”,即進行條件的判斷。為了不同情況下能執行不同的代碼,C++定義了跳轉語句來實現,其是基于CPU的運行規則來實現的,下面先來看CPU是如何執行機器代碼的。

   機器代碼的運行方式

  前面已經說過,C++中的所有代碼到最后都要變成CPU能夠認識的機器代碼,而機器代碼由于是方法的描述也就包含了動作和動作的賓語(也可能不帶賓語),即機器指令和內存地址或其他硬件資源的標識,并且全部都是用二進制數表示的。很正常,這些代表機器代碼的二進制數出于效率的考慮在執行時要放到內存中(實際也可以放在硬盤或其他存儲設備中),則很正常地每個機器指令都能有一個地址和其相對應。

  CPU內帶一種功能和內存一樣的用于暫時記錄二進制數的硬件,稱作寄存器,其讀取速度較內存要快很多,但大小就小許多了。為了加快讀取速度,寄存器被去掉了尋址電路進而一個寄存器只能存放1個32位的二進制數(對于32位電腦)。而CPU就使用其中的一個寄存器來記錄當前欲運行的機器指令的位置,在此稱它為指令寄存器。

  CPU運行時,就取出指令寄存器的值,進而找到相應的內存,讀取1個字節的內容,查看此8位二進制數對應的機器指令是什么,進而做相應的動作。由于不同的指令可能有不同數量的參數(即前面說的動作的賓語)需要,如乘法指令要兩個參數以將它們乘起來,而取反操作只需要一個參數的參與。并且兩個8位二進制數的乘法和兩個16位二進制數的乘法也不相同,故不同的指令帶不同的參數而形成的機器代碼的長度可能不同。每次CPU執行完某條機器代碼后,就將指令寄存器的內容加上此機器代碼的長度以使指令寄存器指向下一條機器代碼,進而重復上面的過程以實現程序的運行(這只是簡單地說明,實際由于各種技術的加入,如高速緩沖等,實際的運行過程要比這復雜得多)。

  

   語句的分類

  在C++中,語句總共有6種:聲明語句、定義語句、表達式語句、指令語句、預編譯語句和注釋語句。其中的聲明語句下篇說明,預編譯語句將在《C++從零開始(十六)》中說明,而定義語句就是前面已經見過的定義變量,后面還將說明定義函數、結構等。表達式語句則就是一個表達式直接接一個“;”,如:34;、a = 34;等,以依靠操作符的計算功能的定義而生成相應的關于內存值操作的代碼。注釋語句就是用于注釋代碼的語句,即寫來給人看的,不是給編譯器看的。最后的指令語句就是含有下面所述關鍵字的語句,即它們的用處不是操作內存,而是實現前面說的“什么樣的情況”。

  這里的聲明語句、預編譯語句和注釋語句都不會轉換成機器代碼,即這三種語句不是為了操作電腦,而是其他用途,以后將詳述。而定義語句也不一定會生成機器代碼,只有表達式語句和指令語句一定會生成代碼(不考慮編譯器的優化功能)。

  還應注意可以寫空語句,即;或{},它們不會生成任何代碼,其作用僅僅只是為了保證語法上的正確,后面將看到這一點。下面說明注釋語句和指令語句——跳轉語句、判斷語句和循環語句(實際不止這些,由于異常和模板技術的引入而增加了一些語句,將分別在說明異常和模板時說明)。

  

   注釋語句——//、/**/

  注釋,即用于解釋的標注,即一些文字信息,用以向看源代碼的人解釋這段代碼什么意思,因為人的認知空間和電腦的完全不同,這在以后說明如何編程時會具體討論。要書寫一段話用以注釋,用“/*”和“*/”將這段話括起來,如下:

  

   long a = 1;

   a += 1; /* a放的是人的個數,讓人的個數加一 */

   b *= a; /* b放的是人均花費,得到總的花費 */

  上面就分別針對a += 1;和b *= a;寫了兩條注釋語句以說明各自的語義(因為只要會C++都知道它們是一個變量的自增一和另一個變量的自乘a,但不知道意義)。上面的麻煩之處就是需要寫“/*”和“*/”,有點麻煩,故C++又提供了另一種注釋語句——“//”:

  

   long a = 1;

   a += 1; // a放的是人的個數,讓人的個數加一

   b *= a; // b放的是人均花費,得到總的花費

  上面和前面等效,其中的“//”表示從它開始,這一行后面的所有字符均看成注釋,編譯器將不予理會,即:

  

   long a = 1; a += 1; // a放的是人的個數,讓人的個數加一 b *= a;

  其中的b *= a;將不會被編譯,因為前面的“//”已經告訴編譯器,從“//”開始,這一行后面的所有字符均是注釋,故編譯器不會編譯b *= a;。但如果

  

   long a = 1; a += 1; /* a放的是人的個數,讓人的個數加一 */ b *= a;

  這樣編譯器依舊會編譯b *= a;,因為“/*”和“*/”括起來的才是注釋。

  應該注意注釋語句并不是語句,其不以“;”結束,其只是另一種語法以提供注釋功能,就好象以后將要說明的預編譯語句一樣,都不是語句,都不以“;”結束,既不是單句也不是復合語句,只是出于習慣的原因依舊將它們稱作語句。

  

   跳轉語句——goto

  前面已經說明,源代碼(在此指用C++編寫的代碼)中的語句依次地轉變成用長度不同的二進制數表示的機器代碼,然后順序放在內存中(這種說法不準確)。如下面這段代碼:

  

   long a = 1; // 假設長度為5字節,地址為3000

   a += 1; // 則其地址為3005,假設長度為4字節

   b *= a; // 則其地址為3009,假設長度為6字節

  上面的3000、3005和3009就表示上面3條語句在內存中的位置,而所謂的跳轉語句,也就是將上面的3000、3005等語句的地址放到前面提過的指令寄存器中以使得CPU開始從給定的位置執行以表現出執行順序的改變。因此,就必須有一種手段來表現語句的地址,C++對此給出了標號(Label)。

  寫一標識符,后接“:”即建立了一映射,將此標識符和其所在位置的地址綁定了起來,如下:

  

   long a = 1; // 假設長度為5字節,地址為3000

  P1:

  

   a += 1; // 則其地址為3005,假設長度為4字節

  P2:

  

   b *= a; // 則其地址為3009,假設長度為6字節

   goto P2;

  上面的P1和P2就是標號,其值分別為3005和3009,而最后的goto就是跳轉語句,其格式為goto <標號>;。此語句非常簡單,先通過“:”定義了一個標號,然后在編寫goto時使用不同的標號就能跳到不同的位置。

  應該注意上面故意讓P1和P2定義時獨占一行,其實也可以不用,即:

  

   long a = 1; P1: a += 1; P2: b *= a; goto P2;

  因此看起來“P1:”和“P2:”好象是單獨的一條定義語句,應該注意,準確地說它們應該是語句修飾符,作用是定義標號,并不是語句,即這樣是錯誤的:

  

   long a = 1; P1: { a += 1; P2: b *= a; P3: } goto P2;

  上面的P3:將報錯,因為其沒有修飾任何語句。還應注意其中的P1仍然是3005,即“{}”僅僅只是其復合的作用,實際并不產生代碼進而不影響語句的地址。

  

   判斷語句——if else、switch

  if else 前面說過了,為了實現“什么樣的情況”做“什么樣的動作”,故C++非常正常地提供了條件判斷語句以實現條件的不同而執行不同的代碼。if else的格式為:

  

   if(<數字>)<語句1>else<語句2> 或者 if(<數字>)<語句1>

   long a = 0, b = 1;

   P1:

   a++;

   b *= a;

   if( a < 10 )

   goto P1;

   long c = b;

  上面的代碼就表示只有當a的值小于10時,才跳轉到P1以重復執行,最后的效果就是c的值為10的階乘。

  上面的<數字>表示可以在“if”后的括號中放一數字,即表達式,而當此數字的值非零時,即邏輯真,程序跳轉以執行<語句1>,如果為零,即邏輯假,則執行<語句2>。即也可如此:if( a – 10 ) goto P1;,其表示當a – 10不為零時才執行goto P1;。這和前面的效果一樣,雖然最后c仍然是10的階乘,但意義不同,代碼的可讀性下降,除非出于效率的考慮,不推薦如此書寫代碼。

  而<語句1>和<語句2>由于是語句,也就可以放任何是語句的東西,因此也可以這樣:

  

   if( a ) long c;

  上面可謂吃飽了撐了,在此只是為了說明<語句1>實際可以放任何是語句的東西,但由于前面已經說過,標號的定義以及注釋語句和預編譯語句其實都不是語句,因此下面試圖當a非零時,定義標號P2和當a為零時書寫注釋“錯誤!”的意圖是錯誤的:

  

   if( a ) P2: 或者 if( !a ) // 錯誤!

   a++; a++;

  但編譯器不會報錯,因為前者實際是當a非零時,將a自增一;后者實際是當a為零時,將a自增一。還應注意,由于復合語句也是語句,因此:

  

   if( a ){ long c = 0; c++; }

  由于使用了復合語句,因此這個判斷語句并不是以“;”結尾,但它依舊是一個單句,即:

  

   if( a )

   if( a < 10 ) { long c = 0; c++; }

   else

   b *= a;

  上面雖然看起來很復雜,但依舊是一個單句,應該注意當寫了一個“else”時,編譯器向上尋找最近的一個“if”以和其匹配,因此上面的“else”是和“if( a < 10 )”匹配的,而不是由于上面那樣的縮進書寫而和“if( a )”匹配,因此b *= a;只有在a大于等于10的時候才執行,而不是想象的a為零的時候。

  還應注意前面書寫的if( a ) long c;。這里的意思并不是如果a非零,就定義變量c,這里涉及到作用域的問題,將在下篇說明。

  switch 這個語句的定義或多或少地是因為實現的原因而不是和“if else”一樣由于邏輯的原因。先來看它的格式:switch(<整型數字>)<語句>。

  上面的<整型數字>和if語句一樣,只要是一個數字就可以了,但不同地必須是整型數字(后面說明原因)。然后其后的<語句>與前相同,只要是語句就可以。在<語句>中,應該使用這樣的形式:case <整型常數1>:。它在它所對應的位置定義了一個標號,即前面goto語句使用的東西,表示如果<整型數字>和<整型常數1>相等,程序就跳轉到“case <整型常數1>:”所標識的位置,否則接著執行后續的語句。

  

   long a, b = 3;

   switch( a + 3 )

   case 2: case 3: a++;

   b *= a;

  上面就表示如果a + 3等于2或3,就跳到a++;的地址,進而執行a++,否則接著執行后面的語句b *= a;。這看起來很荒謬,有什么用?一條語句當然沒意義,為了能夠標識多條語句,必須使用復合語句,即如下:

  

   long a, b = 3;

   switch( a + 3 )

   {

   b = 0;

   case 2:

   a++; // 假設地址為3003

   case 3:

   a--; // 假設地址為3004

   break;

   case 1:

   a *= a; // 假設地址為3006

   }

   b *= a; // 假設地址為3010

  應該注意上面的“2:”、“3:”、“1:”在這里看著都是整型的數字,但實際應該把它們理解為標號。因此,上面檢查a + 3的值,如果等于1,就跳到“1:”標識的地址,即3006;如果為2,則跳轉到3003的地方執行代碼;如果為3,則跳到3004的位置繼續執行。而上面的break;語句是特定的,其放在switch后接的語句中表示打斷,使程序跳轉到switch以后,對于上面就是3010以執行b *= a;。即還可如此:

  

   switch( a ) if( a ) break;

  由于是跳到相應位置,因此如果a為-1,則將執行a++;,然后執行a--;,再執行break;而跳到3010地址處執行b *= a;。并且,上面的b = 0;將永遠不會被執行。

  switch表示的是針對某個變量的值,其不同的取值將導致執行不同的語句,非常適合實現狀態的選擇。比如用1表示安全,2表示有點危險,3表示比較危險而4表示非常危險,通過書寫一個switch語句就能根據某個怪物當前的狀態來決定其應該做“逃跑”還是“攻擊”或其他的行動以實現游戲中的人工智能。那不是很奇怪嗎?上面的switch通過if語句也可以實現,為什么要專門提供一個switch語句?如果只是為了簡寫,那為什么不順便提供多一些類似這種邏輯方案的簡寫,而僅僅只提供了一個分支選擇的簡寫和后面將說的循環的簡寫?因為其是出于一種優化技術而提出的,就好象后面的循環語句一樣,它們對邏輯的貢獻都可以通過if語句來實現(畢竟邏輯就是判斷),而它們的提出一定程度都是基于某種優化技術,不過后面的循環語句簡寫的成分要大一些。

  我們給出一個數組,數組的每個元素都是4個字節大小,則對于上面的switch語句,如下:

  

   unsigned long Addr[3]; Addr[0] = 3006; Addr[1] = 3003; Addr[2] = 3004;

  而對于switch( a + 3 ),則使用類似的語句就可以代替:goto Addr[ a + 3 – 1 ];

  上面就是switch的真面目,應注意上面的goto的寫法是錯誤的,這也正是為什么會有switch語句。編譯器為我們構建一個存儲地址的數組,這個數組的每個元素都是一個地址,其表示的是某條語句的地址,這樣,通過不同的偏移即可實現跳轉到不同的位置以執行不同的語句進而表現出狀態的選擇。

  現在應該了解為什么上面必須是<整型數字>了,因為這些數字將用于數組的下標或者是偏移,因此必須是整數。而<整型常數1>必須是常數,因為其由編譯時期告訴編譯器它現在所在位置應放在地址數組的第幾個元素中。

  了解了switch的實現后,以后在書寫switch時,應盡量將各case后接的整型常數或其倍數靠攏以減小需生成的數組的大小,而無需管常數的大小。即case 1000、case1001、case 1002和case 2、case 4、case 6都只用3個元素大小的數組,而case 0、case 100、case 101就需要102個元素大小的數組。應該注意,現在的編譯器都很智能,當發現如剛才的后者這種只有3個分支卻要102個元素大小的數組時,編譯器是有可能使用重復的if語句來代替上面數組的生成。

  switch還提供了一個關鍵字——default。如下:

  

   long a, b = 3;

   switch( a + 3 )

   {

   case 2:

   a++;

   break;

   case 3:

   a += 3;

   break;

   default:

   a--;

   }

   b *= a;

  上面的“default:”表示當a + 3不為2且不為3時,則執行a--;,即default表示缺省的狀況,但也可以沒有,則將直接執行switch后的語句,因此這是可以的:switch( a ){}或switch( a );,只不過毫無意義罷了。




主站蜘蛛池模板: 天堂在线最新资源 | 青青草原网站在线观看 | 亚洲 欧美 精品 中文第三 | 亚洲欧洲精品在线 | 青草导航 | 色爱区综合激情五月综合激情 | 又粗又硬又大又爽免费观看 | 日本免费网站在线观看 | 日韩生活片| 青青青线在线观看 | 天天干天天干天天干 | 伊人婷婷 | 青青青手机视频在线观看 | 中国国产一级毛片视频 | 亚洲欧美日韩在线线精品 | 日韩高清一级 | 日本在线网站 | 任我鲁这里有精品视频在线播 | 欧美在线播放视频 | 深夜福利免费视频 | 天天躁日日躁狠狠躁黑人躁 | 最新91网址 | 欧美又肥又胖的大bbwⅹ | 中文字幕第23页 | 性爽视频 | 日本欧美一区二区三区视频麻豆 | 亚洲成人娱乐网 | 热99视频| 青娱乐成人 | 欧美线人一区二区三区 | 在线看片日本 | 日本中文一二区有码在线观看 | 日韩成人黄色 | 日本视频网址 | 影音先锋国产资源 | 午夜免费福利在线 | 午夜性伦鲁啊鲁免费视频 | 性福利影院 | 欧美一级视频精品观看 | 色噜噜噜噜噜噜 | 中文字幕第35页 |