|
電腦遊戲製作開發設計論壇 任何可以在PC上跑的遊戲都可以討論,主要以遊戲之製作開發為主軸,希望讓台灣的遊戲人有個討論、交流、教學、經驗傳承的園地
|
上一篇主題 :: 下一篇主題 |
發表人 |
內容 |
yag Site Admin
註冊時間: 2007-05-02 文章: 689
2704.11 果凍幣
|
發表於: 2007-6-18, PM 8:31 星期一 文章主題: [C++][9]迴圈、複合及遞增遞減運算子、布林值、陣列跟次方函數 |
|
|
這章的重點是迴圈。什麼是迴圈?迴圈的用途就是讓我們可以多次重複執行同一段程式碼。在C++中,迴圈有三種,分別是for、while以及do...while。
for的基本語法如下:
代碼: | for( 起始值設定; 條件判斷; 條件變動 ){...}
例:for( i = 0; i < 100; i++ ){...} |
從上面的例中可以看出,我們先設 i 等於0,然後判斷 i 是否小於100,因為0 < 100所以進入{...}中執行敘述,執行完一遍後,程式會執行i++(*註1),也就是 i 的值會加上1,也就是說 i 在執行完後會從0變成1,接著再次判斷 i 是否小於100,是的話,再次執行{...},不是的話則跳出此for迴圈,執行之後的敘述。
在C++的三種迴圈中,如果是要執行一定次數的重複程式碼,那麼使用for是再適當不過了。
while的基本語法如下:
代碼: | while( 條件判斷 ){...}
例:while( i < 100 ){...} |
while我們可以說成是一種會重複的if,它跟if的不同之處在於,if的條件判斷若為true(*註2),則會執行其{...}一次,而while的條件判斷若為true,則會不斷地重複執行其{...}直到其條件判斷變為false為止。
像上例中,如果 i 永遠小於100,那麼while就會是一個無限迴圈,因此為了避免程式跑不完,我們就得在其{...}中對 i 值做變更使其最終得已大於等於100。
do...while的基本語法如下:
代碼: | do{...}while( 條件判斷 );
例:do{...}while( i < 100 ); |
do...while跟while最大的差別在於do...while會先無條件執行一次{...}後才做條件判斷,而while是先做條件判斷後才做{...},若條件一開始就是false,那while的{...}就會直接跳過,完全不會執行到。
do...while有個需要特別注意的地方,就是最後面必須加上個「;」做為結束,相對來說,另外兩個迴圈就沒有這個問題,不論是小刮號「()」後或者是大刮號「{}」後,都不用加上分號「;」。而且do...while並不是一個很常用到的語法,所以偶而使用時忘記加上分號是很常見的錯誤。
(*註1)在C++中有總共四個遞增遞減運算子,分別是前置++、後置++、前置--跟後置--。
i++;使用的就是後置++,其意思為 i = i + 1;,也就是把 i 的值加上1然後再回傳指定給 i ,也就是說:
代碼: | int i = 0;
i++;
cout << i; |
我們就會得到一個輸出:1。至於什麼是前置++呢?也就是像這樣:++i;,其意思依然為i = i + 1;,它跟後置++最大的不同是當此運算子放在運算式中時,前置++會先加1後再做運算式,而後置++會先做運算式後再加1:
代碼: | int i = 0, j = 5;
j = j - i++;
cout << j;
j = j - ++i;
cout << j; |
以上程式碼會讓我們得到輸出:53。為什麼呢?因為第一個j = j - i++;,i 會在運算式做完後才做加1的動作,所以j = 5 - 0得到的是5,而第二個j = j - ++i;,i 會在運算式之前先做加1的動作後才執行運算式,所以在上面運算式的i++後,i 為1,在此又先加1,因此 i 為2,然後j = 5 - 2就得到3,所以我們得到了輸出:53。
想必你們也已經了解前置--跟後置--的意思了,沒錯,一個是先執行減1,一個是後執行減1,我就不多說了。
(*註2)C++的基本變數型態中有一種叫做布林型態(Boolean),其值域只有兩個,也就是真(true)跟假(false),其定義方式如下:
如此一來我們宣告了一個布林變數flag並給予其初始值true。在C++中,布林事實上是一種整數值,我們會把0當成false,而將所有的非0值當成true(最常用1來代表),這在我們的範例中就會看到,請繼續往下看。
那麼,接著我們來講講陣列,陣列是一種很簡單的觀念,我們只是將一堆相同型態的變數以一個名字來表示,並且用數字來為其編號。
像是去大賣場都會有置物櫃,而每個置物櫃上都會有編號,像是1號置物櫃、2號置物櫃、3號置物櫃…。
又或者是學校中某班上可能有45位學生,我們也會給予其座號,像是學生1號、學生2號、學生3號…。
在程式中表現出來的話,就會像這樣:置物櫃[1]、置物櫃[2]、置物櫃[3]、學生[1]、學生[2]、學生[3]…。是不是很容易理解呢?
只是程式中跟現實中有個不一樣的地方,那就是我們在現實中往往從1開始編號,而程式中是由0開始編號的:
代碼: | int student[45];
student[0] = 48906101; // 記錄學生座號1號者之學號 |
以上應該不難理解,這就代表了此班學生共有45人,而座號1號者--也就是索引(index)為0者--其學號存為48906101。
需要注意的是,雖然宣告時寫的是student[45],代表了此student(學生)陣列有45個索引,但因為第一個索引是從0開始,所以最後一個索引只會到達44!
要是不小心使用到了student[45]來儲存資料,那可是會引起錯誤的。
講了這麼多,接著我們來看看範例吧,此範例為猜數字遊戲,想必大家對遊戲規則不會陌生吧?這可是上課傳紙條時最適合玩的遊戲…
代碼: | #include <iostream>
#include <time.h>
#include <limits>
using namespace std;
int main()
{
short Ans[4] = {0};
short Guess[4] = {0};
short Input = 0, A = 0, B = 0, i = 0, j = 0, count = 0;
char GoOn = ' ';
cout << "這是猜數字遊戲範例,程式會隨機給出一個數字,其為" << endl
<< "0~9中任意4個不重複數的組合,例:1234、3980、0657" << endl
<< "請你隨意輸入你想到的組合,程式回應你有猜中幾個數" << endl
<< "字,如果猜中數字,會說有幾個B,如果猜中數字的同時" << endl
<< "也猜中了它的位置,會說有幾個A,例如程式給的數字是" << endl
<< "1234,你猜8240,那就是1A1B,A是2,B是4。" << endl;
system( "pause" );
srand( (unsigned)time( NULL ) );
do{ // 遊戲迴圈
system( "cls" );
count = 0; // 每場遊戲要重新計算猜測次數
A = 0; // 因為A == 4為猜對的條件,所以在此必須歸零才能再度進入猜測迴圈中
// **************隨機產生不重複之四個數字***********************
for( i = 0; i < 4; i++ )
Ans[i] = rand() % 10;
while( Ans[1] == Ans[0] )
{
Ans[1] = rand() % 10;
}
while( Ans[2] == Ans[1] || Ans[2] == Ans[0] )
{
Ans[2] = rand() % 10;
}
while( Ans[3] == Ans[2] || Ans[3] == Ans[1] || Ans[3] == Ans[0] )
{
Ans[3] = rand() % 10;
}
// *************************************************************
while( A < 4 ) // 猜測迴圈
{
A = 0; // 每次猜測必須把A跟B歸零
B = 0;
while( 1 ) // 無限迴圈
{
Repeat:
cout << "請輸入你的猜測:";
cin >> Input;
if( cin.fail() )
{
cin.clear();
cin.ignore( numeric_limits<streamsize>::max(), '\n' );
cout << "錯誤的輸入格式!" << endl;
continue;
}
// 因為四個數字不能重複,所以最小為0123,最大為9876,存成數字型態,則0123的0可省略
if( Input >= 123 && Input <= 9876 )
{
// 如果( 1000 / pow( 10, i ) )沒括號起來,變成Input / 1000 * pow( 10, i );
// 則Input先除1000,百位數以下都會消失,不會得到正確的數值
// 若改成Input * pow( 10, i ) / 1000;,應該是可以,但可能會有overflow的危險
for( i = 0; i < 4; i++ )
{
Guess[i] = Input / ( 1000 / (int)pow( 10, i ) );
Input -= Guess[i] * (int)pow( 10, 3 - i ); // 減掉較高位數的數字
}
for( i = 0; i < 4; i++ )
for( j = 0; j < 4; j++ )
if( Guess[j] == Guess[i] && i != j )
{
cout << "輸入的四位數中不可有重複的數字出現!" << endl;
goto Repeat;
}
break; // 跳出無限迴圈
}
else
{
cout << "請輸入四位數字!" << endl;
}
}
for( i = 0; i < 4; i++ )
for( j = 0; j < 4; j++ )
{
if( Guess[j] == Ans[i] ) // 數字相同
{
if( i == j ) // 位置一樣
A++;
else // 位置不同
B++;
}
}
count++;
if( A == 4 )
{
cout << "恭喜你!猜對了!共猜了 " << count << " 次" << endl;
cout << "要繼續玩嗎?(y/n) ";
cin >> GoOn;
}
else
{
cout << A << " A " << B << " B" << endl;
}
}
}while( GoOn == 'y' || GoOn == 'Y' );
return 0;
} |
首先我們從最上面看起,include iostream自然是為了cin、cout,而time.h是為了要在srand中使用time()函式,至於limits...一提到這個我就心痛,上次那篇花了一個小時卻因為不小心按了F5而報銷的cin錯誤處理中有提到,這我之後會再重寫一次,在此就不多提了。
接著嘛,一進入main我們就可以看到陣列的定義(*註3),Ans跟Guess都是大小為4的short陣列,其索引範圍皆為0~3,而其後的= {0};是代表將其所有元素全部初值化為0。再下面有個字元變數GoOn,初值化為一個空白字元。
(*註3)定義(definition)是C++中的一個術語,它跟另一個術語宣告(declaration)有很密切的關係。一般來說,像以下這樣:
我們稱之為一個int型態變數 i 的宣告,但如果我們在宣告的同時給予初始值:
則我們稱這一行為一個int型態變數 i 的定義!
所以我們可以知道,有否給予初始值就是定義跟宣告的差別。
在簡單的遊戲規則說明以及給予隨機函數起始種子後,我們就進入了一個一直涵蓋到最後面的遊戲迴圈,其語法為do...while,應該還沒忘得那麼快吧?do...while會讓我們先執行一遍{...}中所有的敘述,然後才判斷條件,因此我們先直接來看其內容吧。
首先我們先隨機產生不重複的四個個位數字,這當然就是我們的謎底了,我們先看看:
代碼: | for( i = 0; i < 4; i++ )
Ans[i] = rand() % 10; |
這是一個for迴圈,很明顯地它會執行4次( i 從0~3 ),當 i 等於4時,這個迴圈就會結束,而陣列是迴圈的好夥伴,擅用兩個的搭配可以讓我們省下很多功夫,就像這個例子中,如果不是使用了Ans陣列,你沒有辦法像這樣完美地使用索引來指定多個變數之值(事實上也不是完全沒有辦法,只是那個辦法很詭異而且很冷僻)。至於rand() % 10;應該就不用再多說了,我們已經看過很多次這個東西了,它會取得0~9之間的數值。
緊接著下面有3個while,其用途很明顯,只要這四個數字中有重複出現的,那就再取一次隨機變數,直到沒有重複的數字出現為止。
接著我們會進入所謂的猜測迴圈,也就是遊戲中我們不斷嘗試猜測的過程,首先我們會看到一個while迴圈的特例:
代碼: | while( 1 ) // 無限迴圈
{...} |
這是一個我們刻意造成的無限迴圈,為什麼在while的條件判斷中填入1就會造成無限迴圈呢?忘記布林值特性的請往上拉到(*註2)的地方再看一次,我們會把0當成false,而把所有的非0值當成true,很明顯,1就是一個非0值,也是我們最常拿來代表true的數值,換句話說,這個無限迴圈也可以寫成:
代碼: | while( true ) // 無限迴圈
{...} |
這兩者是一模一樣的,當然下面這種方式要清楚得多,不過還是有很多程式設計師喜歡使用1。那麼,應該不會有人問我為何while( true )就會造成無限迴圈吧...請往上拉到介紹while基本語法那段,其中有句話我原封不動複製過來:while的條件判斷若為true,則會不斷地重複執行其{...}直到其條件判斷變為false為止,在while( true )的情況下,其條件判斷當然沒辦法變成false啦~true是一個值又不是變數,所以當然是無限迴圈囉。
為什麼我們需要這個無限迴圈呢?因為它裡面包住了輸入的判斷,我們並不能事先了解白目的玩家到底會去嘗試幾次格式錯誤的輸入,因此只好使用無限迴圈將輸入的這部份包住,直到正確的輸入被接收到後,我們才使用break;指令跳出這個迴圈。
嗯?break;?是不是很眼熟呢?沒錯,這就是switch中所使用的那個指令,這個指令最大的用途就是拿來中途跳出switch、for、while、do...while等迴圈與條件判斷語法。不過呢,break;只能跳出離自己最近的一層迴圈或switch唷!像是:
代碼: | while( ... ) // 較遠的一層
{
... // 第一段敘述
while( ... ) // 較近的一層
{
...
break;
}
... // 第二段敘述
} |
上面例子中的break;只會跳離較近的那層while,所以較遠的一層的while裡面的第二段敘述依然會執行到的唷,若是要連較遠的一層while也跳開,第一個方法是在較近的一層裡的break;那再加上一個旗標,比如說flag = 1;,然後到了第二段敘述時,判斷flag是否為1,若是1,就再break;一次跳出較遠的那層while。
第二個方法,就是使用傳說中的goto啦~這可以說是為人垢病的goto最常被使用到的地方,goto的好處就是可以無條件地跳到程式中任何一個地方去。
像這種兩層的while用break;還好一點,如果是十層的while呢?光是flag跟break;就夠搞死你啦~
所以這時只要在所有迴圈外面加個標示(label),也就是像範例中的Repeat:,然後就可以一口氣從深層的迴圈中使用goto跳出來囉~~這可是無敵的秘技呢,要好好記住喔。
嗯...扯太遠了,再看回來吧,接著是普通的輸入以及錯誤處理,有看到錯誤處理的最後一行寫著continue;嗎?這就是跟break;常常放在一起講解的另一個迴圈流程控制運算子囉。
break;是讓我們從迴圈中跳出,那continue(繼續)呢?看它的中文意思應該可以猜得到一些,就是要我們直接中斷此行之後的敘述,再重頭跑一次迴圈囉~~
所以呢,如果你的輸入格式有誤,當程式執行到這個continue;後,下面的if( Input >= 123 && Input <= 9876 )就不會執行了,程式會直接回到最上面的cout << "請輸入你的猜測:";重新開始執行。
值得一提的是,如果是在for裡面使用continue,而for的「條件變動」欄裡是i++,那麼此i++可是會執行的唷,當然那個條件判斷也是一樣會做比較的。
再來看到有一行註解寫著「減掉較高位數的數字」,可能會有人看不懂,所以我來講解一下。
這個的意思就是說,如果你輸入了3456,在上面那行敘述中,一開始會把3456 / 1000,得到3,此時如果不減掉較高位數的數字,直接跑下一輪的迴圈,那就會變成3456 / 100,得到34,而不是我們原先所想要的4。
此迴圈的目的就是把像3456這種數字拆開成四個個位數字放到Guess陣列中變成:Guess[0] = 3, Guess[1] = 4, Guess[2] = 5, Guess[3] = 6。
但如果不減掉高位數字,Guess[1]就會變成34,那就大錯特錯了,所以我們勢必得要先將3456減掉3000後,得到456再除以100而得到4,這麼一來應該清楚了吧?
那麼看到減掉較高位數的數字那行本身,這短短的一行可就有三個新東西!首先是複合運算子,什麼是複合運算子呢?就是此行中的「-=」,這是什麼意思呢?讓我們舉個比較清楚的例子:
代碼: | int a = 10, b = 3;
a -= b;
cout << a; |
這個例子的輸出是7。是否已經猜到它的意義了呢?a -= b;就是a = a - b;的縮寫,所以自然會得到a = 10 - 3也就是7了,這種複合運算子有很多個,配合基本的四則運算有:+=、-=、/=、*=、%=等等,意義應該很明顯就不多說了。
再來是pow()這個函式,這是C++中預設的一個數學函式,其參數有兩個,其用途為回傳第一個參數的第二個參數次方的值。這樣看起來有點看不懂吧?直接來看例子:
代碼: | cout << pow( 10, 2 ) << ' ';
cout << pow( 2, 3 ) << ' ';
cout << pow( 2, 10 ); |
以上例子的輸出為:100 8 1024。應該了解了吧?首先是10的2次方,也就是100,再來是2的3次方也就是8,最後是2的10次方為1024。因為C++中沒有像2^10這樣的表示法,所以只好使用pow( 2, 10 )來計算出2的10次方來,這個函式在寫遊戲時應該會滿常用到的,記住比較好。
最後是一個隱性型態轉換,也就是pow前面的(int),因為pow的回傳值是double型態,經過計算後整個運算式-=的右邊會是個double型態的值,要指定給左邊short型態的Input會出現警告訊息說這樣有可能會使得數值有遺失(比如說數值過大塞不下short裡面),雖然警告不會妨礙正常的compile跟run,不過加上一個型態轉換,代表我們確實了解自己在做什麼,是一種比較好的作法,當然,更好的情況下應該用顯性型態轉換,不過這篇講太多了,這個等下次再說吧。
接下來我們就看到了剛說過的goto以及break,break放在那個位置就是說,當輸入的是四位數字,而且此四個數字沒有重複時(如果有重複,那就會在上面的goto直接跳回開始的地方,自然不會執行到這個break;來),就跳出此無限迴圈。
跳出無限迴圈後,我們已經得到了輸入陣列,當然也有謎底陣列,兩相比較,有數字一樣而且位置一樣的就是A,而位置不同的,自然就是B。接著我們只要知道A的值為4,那就代表猜中了答案,顯示恭喜的訊息再詢問是否繼續遊戲。在這裡要注意一下,我們依然在while( A < 4 ) // 猜測迴圈當中,只是很明顯地,我們贏了的條件就是A==4,所以這個if完了之後,程式跑回上面做條件判斷時就會跳出此猜測迴圈,接著就是進入最外圍的do...while遊戲迴圈中判斷GoOn之值,所以我才會把cin >> GoOn;放在這個地方,不然的話,它較適當的位置應該是在do...while遊戲迴圈的{...}裡的最後一行才對。
那麼,我們現在看到了do...while遊戲迴圈的條件判斷,如果輸入是y或者Y,那就拉回最上面的do,重新產生一個新的謎底,再進入猜測迴圈,直到又猜出來為止。如果輸入的是其他字元,程式就結束了。
至此,冗長的範例解說完畢了。不過要多提一個小地方,while(1)是while的無限迴圈,那for的無限迴圈呢?就長這樣:
for當中的三個部份全都可以省略,只有分號「;」必定得要標注出來,而實際上for的第一跟第三部份也可以不用省略,只要第二個部份省略掉,自然沒有條件判斷,那麼它當然就是一個無限迴圈了。
另外while跟for事實上是可以互通的,底下的for跟while實際上是一樣的意思:
代碼: | for( i = 0; i < 100; i++ )
{
...
}
// 上面跟下面的程式碼是一樣的意思
i = 0;
while( i < 100 )
{
...
i++; // 這個要擺在最後一行
} |
仔細想想應該就可以了解上面的例子,for跟while何者適用於何時只是看你個人的喜好罷了,想用哪個就用哪個囉,而do...while當然更不用說,它跟while本來就只差在是否先執行一次{...}罷了。
原始碼下載:main.cpp(3KB)
執行檔下載:example9-1.exe(296KB) |
|
回頂端 |
|
|
lookfuxk098 稍嫌羞澀的路人
註冊時間: 2009-06-03 文章: 4
25.48 果凍幣
|
發表於: 2009-6-4, AM 10:41 星期四 文章主題: |
|
|
請問大大~我程式碼下載來編譯出來錯誤~pow()這個函式好像無法使用~
請問是否程式碼有誤~謝謝~! |
|
回頂端 |
|
|
yag Site Admin
註冊時間: 2007-05-02 文章: 689
2704.11 果凍幣
|
發表於: 2009-6-4, PM 12:42 星期四 文章主題: |
|
|
lookfuxk098 寫到: | 請問大大~我程式碼下載來編譯出來錯誤~
請問是否程式碼有誤~謝謝~! |
不同的編譯器會在細節地方有差異
導致在某些編譯器正常的code會在另一些編譯器出錯
而且同一個編譯器在設定不同時也會有接受的code不同的問題
所以你可能必須把你使用的編譯器以及遭遇到的錯誤資訊給po上來
我們才有辦法幫你解決問題
不過如果你是使用VC++2005以上的版本
最常遭遇的狀況可能是你使用Unicode編碼去編譯它
我的程式碼是Multi-Byte版本的
你可以改一下設定試試 |
|
回頂端 |
|
|
yag Site Admin
註冊時間: 2007-05-02 文章: 689
2704.11 果凍幣
|
發表於: 2009-6-4, PM 12:44 星期四 文章主題: |
|
|
lookfuxk098 寫到: | 請問大大~我程式碼下載來編譯出來錯誤~pow()這個函式好像無法使用~
請問是否程式碼有誤~謝謝~! |
pow啊…
你#include <math.h>試試
或者#include <cmath> |
|
回頂端 |
|
|
lookfuxk098 稍嫌羞澀的路人
註冊時間: 2009-06-03 文章: 4
25.48 果凍幣
|
發表於: 2009-6-4, PM 1:45 星期四 文章主題: |
|
|
原來是忘了加#include <math.h>
已經可以了~謝謝大大~哈哈~! |
|
回頂端 |
|
|
lookfuxk098 稍嫌羞澀的路人
註冊時間: 2009-06-03 文章: 4
25.48 果凍幣
|
發表於: 2009-6-4, PM 5:18 星期四 文章主題: |
|
|
for( i = 0; i < 4; i++ )
{
Guess[i] = Input / ( 1000 / (int)pow( 10, i ) );
Input -= Guess[i] * (int)pow( 10, 3 - i ); // 減掉較高位數的數字
}
不好意思~請問大大~
這一段是什麼功能小弟看了很久還是不懂~
請大大幫我解說一下~謝謝~ |
|
回頂端 |
|
|
yag Site Admin
註冊時間: 2007-05-02 文章: 689
2704.11 果凍幣
|
發表於: 2009-6-4, PM 8:06 星期四 文章主題: |
|
|
lookfuxk098 寫到: | for( i = 0; i < 4; i++ )
{
Guess[i] = Input / ( 1000 / (int)pow( 10, i ) );
Input -= Guess[i] * (int)pow( 10, 3 - i ); // 減掉較高位數的數字
}
不好意思~請問大大~
這一段是什麼功能小弟看了很久還是不懂~
請大大幫我解說一下~謝謝~ |
嗯,功能就是把四位數字拆成四個數字
像是3456拆成3、4、5、6
不過我也不知道我當初為何這樣寫
實際上(int)pow( 10, 3 - i )的值跟( 1000 / (int)pow( 10, i ) )的值是完全一樣的…
10的3次方除以10的i次方跟10的3-i次方是完全相同的東西
總之,以數值代進去的話,假設Input是3456,以上的迴圈就像下面這樣:
3 = 3456 / 1000;
456 = 3456 - 3 * 1000;
4 = 456 / 100;
56 = 456 - 4 * 100;
5 = 56 / 10;
6 = 56 - 5 * 10;
6 = 6 / 1;
0 = 6 - 6 * 1;
藍色字就是拆出來的Guess[i] |
|
回頂端 |
|
|
lookfuxk098 稍嫌羞澀的路人
註冊時間: 2009-06-03 文章: 4
25.48 果凍幣
|
發表於: 2009-6-4, PM 11:31 星期四 文章主題: |
|
|
yag 寫到: | lookfuxk098 寫到: | for( i = 0; i < 4; i++ )
{
Guess[i] = Input / ( 1000 / (int)pow( 10, i ) );
Input -= Guess[i] * (int)pow( 10, 3 - i ); // 減掉較高位數的數字
}
不好意思~請問大大~
這一段是什麼功能小弟看了很久還是不懂~
請大大幫我解說一下~謝謝~ |
嗯,功能就是把四位數字拆成四個數字
像是3456拆成3、4、5、6
不過我也不知道我當初為何這樣寫
實際上(int)pow( 10, 3 - i )的值跟( 1000 / (int)pow( 10, i ) )的值是完全一樣的…
10的3次方除以10的i次方跟10的3-i次方是完全相同的東西
總之,以數值代進去的話,假設Input是3456,以上的迴圈就像下面這樣:
3 = 3456 / 1000;
456 = 3456 - 3 * 1000;
4 = 456 / 100;
56 = 456 - 4 * 100;
5 = 56 / 10;
6 = 56 - 5 * 10;
6 = 6 / 1;
0 = 6 - 6 * 1;
藍色字就是拆出來的Guess[i] |
原來如此~感謝大大為我解說~謝謝~! |
|
回頂端 |
|
|
hello147258369 偶而上來逛逛的過客
註冊時間: 2009-08-01 文章: 6
40.05 果凍幣
|
發表於: 2009-8-5, AM 7:25 星期三 文章主題: |
|
|
yag 寫到: | lookfuxk098 寫到: | for( i = 0; i < 4; i++ )
{
Guess[i] = Input / ( 1000 / (int)pow( 10, i ) );
Input -= Guess[i] * (int)pow( 10, 3 - i ); // 減掉較高位數的數字
}
不好意思~請問大大~
這一段是什麼功能小弟看了很久還是不懂~
請大大幫我解說一下~謝謝~ |
嗯,功能就是把四位數字拆成四個數字
像是3456拆成3、4、5、6
不過我也不知道我當初為何這樣寫
實際上(int)pow( 10, 3 - i )的值跟( 1000 / (int)pow( 10, i ) )的值是完全一樣的…
10的3次方除以10的i次方跟10的3-i次方是完全相同的東西
總之,以數值代進去的話,假設Input是3456,以上的迴圈就像下面這樣:
3 = 3456 / 1000;
456 = 3456 - 3 * 1000;
4 = 456 / 100;
56 = 456 - 4 * 100;
5 = 56 / 10;
6 = 56 - 5 * 10;
6 = 6 / 1;
0 = 6 - 6 * 1;
藍色字就是拆出來的Guess[i] |
我想問一下在// 減掉較高位數的數字的一部份...是要/1000的...能寫成這樣嗎:
for( i = 0; i < 4; i++ )<-----一開始搞不懂i是代表什麼的值
{
Guess[i] = Input / 1000 );<------寫成這樣會因沒加入(int)讀取不到3出來的嗎?
Input -= Guess[i] * (int)pow( 10, 3 - i ); // 減掉較高位數的數字
}
還有這裡的(int)pow(10,3-i)怎樣會成為1000,小弟看了還是不懂i的值是為什麼變2? |
|
回頂端 |
|
|
yag Site Admin
註冊時間: 2007-05-02 文章: 689
2704.11 果凍幣
|
發表於: 2009-8-5, AM 11:35 星期三 文章主題: |
|
|
代碼: | for( i = 0; i < 4; i++ )
{
Guess[i] = Input / ( 1000 / (int)pow( 10, i ) );
Input -= Guess[i] * (int)pow( 10, 3 - i ); // 減掉較高位數的數字
} |
我不是很懂你問什麼
但應該是迴圈看不懂的問題
迴圈事實上是很簡單的
看不懂時把它拆開就是了
像上面那段就可以拆成:
代碼: | // i == 0
Guess[0] = Input / ( 1000 / (int)pow( 10, 0 ) );
Input -= Guess[0] * (int)pow( 10, 3 - 0 );
// i == 1
Guess[1] = Input / ( 1000 / (int)pow( 10, 1 ) );
Input -= Guess[1] * (int)pow( 10, 3 - 1 );
// i == 2
Guess[2] = Input / ( 1000 / (int)pow( 10, 2 ) );
Input -= Guess[2] * (int)pow( 10, 3 - 2 );
// i == 3
Guess[3] = Input / ( 1000 / (int)pow( 10, 3 ) );
Input -= Guess[3] * (int)pow( 10, 3 - 3 ); |
只是這樣寫很麻煩,所以把遞增的數值抽出來成為 i ,以迴圈的方式寫,減少編程的複雜度罷了 |
|
回頂端 |
|
|
hello147258369 偶而上來逛逛的過客
註冊時間: 2009-08-01 文章: 6
40.05 果凍幣
|
發表於: 2009-8-5, PM 5:12 星期三 文章主題: |
|
|
yag 寫到: | 代碼: | for( i = 0; i < 4; i++ )
{
Guess[i] = Input / ( 1000 / (int)pow( 10, i ) );
Input -= Guess[i] * (int)pow( 10, 3 - i ); // 減掉較高位數的數字
} |
我不是很懂你問什麼
但應該是迴圈看不懂的問題
迴圈事實上是很簡單的
看不懂時把它拆開就是了
像上面那段就可以拆成:
代碼: | // i == 0
Guess[0] = Input / ( 1000 / (int)pow( 10, 0 ) );
Input -= Guess[0] * (int)pow( 10, 3 - 0 );
// i == 1
Guess[1] = Input / ( 1000 / (int)pow( 10, 1 ) );
Input -= Guess[1] * (int)pow( 10, 3 - 1 );
// i == 2
Guess[2] = Input / ( 1000 / (int)pow( 10, 2 ) );
Input -= Guess[2] * (int)pow( 10, 3 - 2 );
// i == 3
Guess[3] = Input / ( 1000 / (int)pow( 10, 3 ) );
Input -= Guess[3] * (int)pow( 10, 3 - 3 ); |
只是這樣寫很麻煩,所以把遞增的數值抽出來成為 i ,以迴圈的方式寫,減少編程的複雜度罷了 |
哦原來i是遞增的數值...我以為i是輪入來的input.......yag大大教懂我不少東西..感謝 |
|
回頂端 |
|
|
Alchemist 偶而上來逛逛的過客
註冊時間: 2010-06-10 文章: 7
130.57 果凍幣
|
發表於: 2010-6-13, AM 5:40 星期日 文章主題: 使用VC++ 2010 編譯時有出現錯誤唷! 也是pow函式 |
|
|
error C2668: 'pow' : 模稜兩可的呼叫多載函式
c:\program files\microsoft visual studio 10.0\vc\include\math.h(583): 可能是 'long double pow(long double,int)'
c:\program files\microsoft visual studio 10.0\vc\include\math.h(535): 或 'float pow(float,int)'
c:\program files\microsoft visual studio 10.0\vc\include\math.h(497): 或 'double pow(double,int)'
Guess[i] = Input / ( 1000 / ( int) pow( 10, i ) );
改成
Guess[i] = Input / ( 1000 / ( int) pow( (long double) 10, i ) );
就沒問題了
或者改成以下這兩個我想應該也沒差吧
Guess[i] = Input / ( 1000 / ( int) pow( (float) 10, i ) );
Guess[i] = Input / ( 1000 / ( int) pow( (double) 10, i ) );
雖然不是什麼大問題
可是卻讓我有不少疑惑呢
不過我想先來閒聊幾句
各位覺得寫程式碼跟閱讀程式碼哪個比較容易呢?
我覺得應該是閱讀比較容易吧?
(說到閱讀程式碼yag可不可以在定義變數的同時就在旁邊註解這個變數的用途呢?這幾篇一系列的文章一路閱讀下來我發現一開始定義的變數我都要讀到後面才會知道它的用途)
我覺得應該是閱讀比較容易吧!
我常常看懂一段程式碼以後
心裡都會這麼想...
"喔~原來可以這樣寫~"
感覺如果是我在寫這些程式的時候都會想不到這些寫法呢
Uh..
閒聊夠了
我想想唷..
在一個變數前面加上括號資料型態
忘記第一次是在哪裡聽到的了
好像有人說是叫做強制轉型是吧?
話說剛剛查了一下發現C++有多四個強制轉型的方式
reinterpret_cast
static_cast
dynamic_cast
const_cast
稍微看了一下它們的說明
說真的我有看沒有懂
所以yag改天來聊聊這四個東東唄!
可以的話順便也聊聊多載函式好了
以下是VC++ 2010math.h裡的
double __CRTDECL pow(_In_ double _X, _In_ int _Y)
float __CRTDECL pow(_In_ float _X, _In_ float _Y)
float __CRTDECL pow(_In_ float _X, _In_ int _Y)
long double __CRTDECL pow(_In_ long double _X, _In_ long double _Y)
long double __CRTDECL pow(_In_ long double _X, _In_ int _Y)
我想這個錯誤是因為編譯器無法判斷數值10是屬於哪一種資料型態吧?
模稜兩可- -
「因為pow的回傳值是double型態」
在VC++ 2010裡似乎不只double一種回傳的型態呢
可見不同編譯器之間的差異
對於程式碼的移植影響還蠻大滴說
我在很上面應該有提到我有很多疑惑吧!
(真的很上面..我廢話似乎有點多呢!難怪我同學都說我很吵- -)
首先是疑惑一
int pow(int, int)
這個樣子應該會比較常用到吧- -"
小數的次方想不到哪裡會需要用到..
再來是疑惑二
yag是這樣寫的
for( i = 0; i < 4; i++ )
並不是寫成
for( int i = 0; i < 4; i++ )
編譯器為什麼對第一個參數數值10的回應是模稜兩可
卻肯定第二個參數的型態是int呢?
我後來還試著把變數i改成數值
結果編譯器還是一樣肯定它的型態是int
明明就還有以下兩種可能不是嗎?
float __CRTDECL pow(_In_ float _X, _In_ float _Y)
long double __CRTDECL pow(_In_ long double _X, _In_ long double _Y)
其實只有第二種可能才對
因為我已經把第一個參數轉形成long double了
我講完了..
說真的我並沒有真的學過C語言或者是C++
要是有講錯的部份
或是很笨的錯誤的話
請多包涵呢 |
|
回頂端 |
|
|
yag Site Admin
註冊時間: 2007-05-02 文章: 689
2704.11 果凍幣
|
發表於: 2010-6-13, AM 9:32 星期日 文章主題: |
|
|
嗯,問得很好,只是你的發問真的好長啊,看到底部都忘了你上面問什麼了
如你所想,(long double)、(float)、(double)都ok
的確是新版編譯器的改動造成的問題
微軟應該是在math.h中新增加了pow的一些多載
我個人習慣看到變數宣告直接跳過,它就只是宣告而已
在旁邊寫上變數用途也不見得在沒看下面code的狀況下能看懂
而且很多變數的用途很難用文字解釋
但既然你都這麼說了,我以後寫新文章時會注意標示一下
閱讀程式碼不見得比較容易
這要視乎該程式碼的註解以及變數的取名而定
如果沒註解,變數又亂取名字,直接從a編到z,同一變數還前後用途不同
那閱讀它只是浪費時間而已
話說回來,有一種反逆向工程的方式就是這樣做的,變數亂取名,然後加上毫無意義的程式碼區段,使得逆向工程的困難度上升
關於那四個強制轉型
我也只有static_cast偶而會用用而已
另外三個也不太熟
我有空時去查查看再寫文章吧
小數的次方事實上比較容易用到
多半是做物理計算時
很多數值不會剛好是整數
應該說絕大部份的情況下都不會是整數
第二個引數是short,它會自動轉型成int,因為都是整數
自動轉型有它內部的規則在
你說把 i 改成數值,編譯器照樣當成int
我不確定你怎麼改的,也不確定你怎麼判斷的,所以也不能確定為何會這樣
但你可以試試rebuild這個專案,或許有用
ps.改成Guess[i] = Input / ( 1000 / ( int) pow( 10.0f, i ) );也是一種選擇
它會使用float那個多載
除非需求精細度很高,不然使用long double會增加記憶體用量、減低效能
雖說影響很小就是了,但還是盡量避免 |
|
回頂端 |
|
|
Alchemist 偶而上來逛逛的過客
註冊時間: 2010-06-10 文章: 7
130.57 果凍幣
|
發表於: 2010-6-13, AM 11:59 星期日 文章主題: 嗯... |
|
|
大概理解了- -
Thanks
話說...
不知道是今天C++的程式碼看太多了
還是怎麼回事...
今天寫到...
If Not M Is Nothing Then
For index = 0 To M.Length - 1
e.Graphics.DrawImage(My.Resources.老鼠, M(index).R_X, M(index).R_Y, 100, 50)
Next
End If
那個Not我差點就打!上去了...
(不是差點..明明就打上去了 還疑惑怎麼會有錯誤哩- -") |
|
回頂端 |
|
|
denisgod 稍嫌羞澀的路人
註冊時間: 2010-06-12 文章: 1
7.71 果凍幣
|
發表於: 2010-6-13, PM 6:52 星期日 文章主題: |
|
|
有個很神奇的現象
我把大大的程式co到dev c++去跑
輸入1586的時候 出現數字重複
我把輸出的數字調出來發現 當i=2 pow(10,i)=99
導致他計算上變成1591 有重複數字
可以請教這是什麼原因 應該怎麼處理嗎? 謝謝 |
|
回頂端 |
|
|
|
|
您 無法 在這個版面發表文章 您 無法 在這個版面回覆文章 您 無法 在這個版面編輯文章 您 無法 在這個版面刪除文章 您 無法 在這個版面進行投票 您 可以 在這個版面附加檔案 您 可以 在這個版面下載檔案
|
|