回到「給新手的C++教學 (上冊)」
回到「13. 額外語法 (Extra syntax)」
上一頁
註: 小莫使用的程式碼上色網站掛掉了,因此本頁的程式碼都沒有顏色,等該網站恢復之後會再進行程式碼上色的動作,暫時造成不便敬請見諒。若您發現該網站已經恢復運作,本頁程式碼卻還沒更新,也歡迎以各種方式提醒小莫哦~ ^_^
遞迴?甚麼是遞迴呢?
其實數學也有遞迴哦~
不知道也沒關係,因為小莫高一學程式的時候也完全沒聽過「遞迴」這個字眼XD
反正就是個專有名詞嘛XD,概念其實不難~
舉個例子,現在小莫創造一個函式$f$,當$n=1$時會回傳$1$,$n=2$時也會回傳$1$:
#include<cstdio>
int f(int n)
{
if(n==1||n==2) return 1;
printf("無法處理這個n\n");
return -1;
}
int main()
{
printf("f(1)=%d\n",f(1));
printf("f(2)=%d\n",f(2));
return 0;
}
那$n=3$呢?小莫想要讓它回傳「$f(1)+f(2)$」
甚麼意思?
$f(1)=1$,$f(2)=1$,所以$f(3)=f(1)+f(2)=1+1=2$啦
也就是當$n=3$時會回傳$2$,只是這個$2$是被計算出來的
那$n=4$呢?小莫想要讓它回傳「$f(2)+f(3)$」
$f(2)=1$,$f(3)=2$,所以$f(4)=f(2)+f(3)=1+2=3$啦
也就是當$n=4$時會回傳$3$,只是這個$3$是被計算出來的
那$n>4$呢?相信您已經猜到了,只要$n>2$,小莫想要讓它回傳的就是$f(n-2)+f(n-1)$!
那$f$這個函式的程式碼要怎麼寫呢?不用怕,電腦學過C++,很聰明的,直接寫出來,看看電腦能不能看懂!
2017年3月2日 星期四
2016年12月23日 星期五
給新手的C++教學 (上冊) - 13 - 10. 轉型
回到「給新手的C++教學 (上冊)」
回到「13. 額外語法 (Extra syntax)」
上一頁
註: 小莫使用的程式碼上色網站掛掉了,因此本頁的程式碼都沒有顏色,等該網站恢復之後會再進行程式碼上色的動作,暫時造成不便敬請見諒。若您發現該網站已經恢復運作,本頁程式碼卻還沒更新,也歡迎以各種方式提醒小莫哦~ ^_^
顧名思義,「轉型」就是「轉換型別」
「將某個小數無條件捨去到整數位」就是「從float『轉型』到int」的一個例子
因為小數位不見會變成整數 (int) 嘛XD
不過,「轉型」並不是把某個變數a的型別從float「變成」int,而是依據a的值「產生」一個int變數,其值接近型別為float的a (為甚麼說接近? 因為你不可能把一個int變數的值設定成2.4,頂多設定成2或3)
那麼,要怎麼將一個變數「轉型」呢?
回到「13. 額外語法 (Extra syntax)」
上一頁
註: 小莫使用的程式碼上色網站掛掉了,因此本頁的程式碼都沒有顏色,等該網站恢復之後會再進行程式碼上色的動作,暫時造成不便敬請見諒。若您發現該網站已經恢復運作,本頁程式碼卻還沒更新,也歡迎以各種方式提醒小莫哦~ ^_^
顧名思義,「轉型」就是「轉換型別」
「將某個小數無條件捨去到整數位」就是「從float『轉型』到int」的一個例子
因為小數位不見會變成整數 (int) 嘛XD
不過,「轉型」並不是把某個變數a的型別從float「變成」int,而是依據a的值「產生」一個int變數,其值接近型別為float的a (為甚麼說接近? 因為你不可能把一個int變數的值設定成2.4,頂多設定成2或3)
那麼,要怎麼將一個變數「轉型」呢?
2016年10月26日 星期三
給新手的C++教學 (上冊) - 13 - 25. 「新版C++」--「C++11」的好用功能
回到「給新手的C++教學 (上冊)」
回到「13. 額外語法 (Extra syntax)」
上一頁
C++看起來已經是一個很完整、功能強大的程式語言了
但事實上,世界各地的專家們仍然致力於改善C++,讓原本已經很強的C++的功能更加強大!
因此,C++也是有版本的哦~
別擔心,您在較舊版本C++使用的語法,在新版C++也能無縫接軌的使用!
注意:請先依據這篇設定好編譯參數,才能使用本頁所教的語法
請注意,此篇語法有些適合搭配STL (Standard Library 標準函式庫) 使用,STL的介紹和用法將在下冊講到
「auto」的用法:
先備知識:1~12章的某些基本語法
功能:可以讓電腦在宣告變數的時候自動判斷該變數的型別
簡單舉例:
再舉一個例子:
回到「13. 額外語法 (Extra syntax)」
上一頁
C++看起來已經是一個很完整、功能強大的程式語言了
但事實上,世界各地的專家們仍然致力於改善C++,讓原本已經很強的C++的功能更加強大!
因此,C++也是有版本的哦~
別擔心,您在較舊版本C++使用的語法,在新版C++也能無縫接軌的使用!
注意:請先依據這篇設定好編譯參數,才能使用本頁所教的語法
請注意,此篇語法有些適合搭配STL (Standard Library 標準函式庫) 使用,STL的介紹和用法將在下冊講到
「auto」的用法:
先備知識:1~12章的某些基本語法
功能:可以讓電腦在宣告變數的時候自動判斷該變數的型別
簡單舉例:
#include<cstdio> int main() { double a=8.3; //double b=a; auto b=a;//這一行和上面那一行被註解掉的程式碼是等價的 printf("a=%f, b=%f\n",a,b); return 0; }
| 執行結果 |
再舉一個例子:
2016年9月27日 星期二
給新手的C++教學 (上冊) - 13 - 9. 進階的檔案處理
回到「給新手的C++教學 (上冊)」
回到「13. 額外語法 (Extra syntax)」
上一頁
讀取檔案「in.txt」並同時輸出其內容:
輸入3個檔名,依據輸入的檔名讀取那3個檔案並同時輸出其內容:
將「1+2+3+......+100」的完整計算過程寫入到檔案「out.txt」:
回到「13. 額外語法 (Extra syntax)」
上一頁
讀取檔案「in.txt」並同時輸出其內容:
#include<cstdio> int main() { FILE *file_reader=fopen("in.txt","r"); for(char c;fscanf(file_reader,"%c",&c)==1;) { printf("%c",c); } return 0; }
![]() |
| 輸出結果:可以發現黑色視窗上輸出的內容就是「in.txt」的內容 |
輸入3個檔名,依據輸入的檔名讀取那3個檔案並同時輸出其內容:
#include<cstdio> void ReadFile() { char file_name[1000]; scanf("%s",file_name); FILE *file_reader=fopen(file_name,"r"); for(char c;fscanf(file_reader,"%c",&c)==1;) printf("%c",c); printf("Finish reading %s!\n",file_name); } int main() { ReadFile(); ReadFile(); ReadFile(); return 0; }
![]() |
| 可以隨時決定要從哪一個檔案讀取資料 (fscanf) 或者直接從黑色視窗取得輸入 (scanf) |
將「1+2+3+......+100」的完整計算過程寫入到檔案「out.txt」:
2016年9月19日 星期一
給新手的C++教學 (上冊) - 13 - 8. 簡易的檔案處理
回到「給新手的C++教學 (上冊)」
回到「13. 額外語法 (Extra syntax)」
上一頁
您是否有想過要用C++來讀取和寫入檔案呢?
真的可以嗎?需不需要解析磁碟機NTFS、FAT32之類的格式啊?
當然不需要!
放心,這件事連小莫都不知道怎麼做
所謂「站在巨人的肩膀上」,這種事情不用再由我們自己處理了!
在先人的努力之下,「C++」這個偉大的程式語言,已經讓電腦可以只依據簡單的幾行程式碼,就可以執行許多複雜卻需要經常執行的工作
事實上,C++讀寫檔案的方法比你想的任何方式都簡單!
更具體地來講
我們在撰寫C++程式碼的時候,只需要告訴電腦「讀取哪個檔案」和 (或)「寫入哪個檔案」就好了!
要怎麼告訴電腦「恩,我要從這個檔案名稱為『in.txt』的檔案讀取」呢?
(請先到檔案總管設定顯示附檔名以取得完整的檔案名稱 (點我查看教學))
寫下一行程式碼就夠了:
回到「13. 額外語法 (Extra syntax)」
上一頁
您是否有想過要用C++來讀取和寫入檔案呢?
真的可以嗎?需不需要解析磁碟機NTFS、FAT32之類的格式啊?
當然不需要!
放心,這件事連小莫都不知道怎麼做
所謂「站在巨人的肩膀上」,這種事情不用再由我們自己處理了!
在先人的努力之下,「C++」這個偉大的程式語言,已經讓電腦可以只依據簡單的幾行程式碼,就可以執行許多複雜卻需要經常執行的工作
事實上,C++讀寫檔案的方法比你想的任何方式都簡單!
更具體地來講
我們在撰寫C++程式碼的時候,只需要告訴電腦「讀取哪個檔案」和 (或)「寫入哪個檔案」就好了!
要怎麼告訴電腦「恩,我要從這個檔案名稱為『in.txt』的檔案讀取」呢?
(請先到檔案總管設定顯示附檔名以取得完整的檔案名稱 (點我查看教學))
寫下一行程式碼就夠了:
2016年9月16日 星期五
給新手的C++教學 (上冊) - 13 - 7. 中文字元字串的處理
回到「給新手的C++教學 (上冊)」
回到「13. 額外語法 (Extra syntax)」
上一頁
先前有網友提問,他想寫一個程式:
輸入「是」會輸出「真是識相!\n」
輸入「不是」會輸出「你眼光有問題!\n」
我們先來看看這個程式的英文版本怎麼寫:
輸入「YES」會輸出「真是識相!\n」
輸入「NO」會輸出「你眼光有問題!\n」
請注意,「answer」的長度只有4,因此一旦輸入的長度超過3 (注意,還有一個表示字串結尾的「'\0'」) 就有可能導致程式出錯
那麼,要怎麼改成中文版本呢?是這樣嗎?
很遺憾的,事情並沒有這麼簡單
回到「13. 額外語法 (Extra syntax)」
上一頁
先前有網友提問,他想寫一個程式:
輸入「是」會輸出「真是識相!\n」
輸入「不是」會輸出「你眼光有問題!\n」
我們先來看看這個程式的英文版本怎麼寫:
輸入「YES」會輸出「真是識相!\n」
輸入「NO」會輸出「你眼光有問題!\n」
#include<cstdio> int main() { printf("我是天才? YES or NO\n"); char answer[4]; scanf("%s",answer); if(answer[0]=='Y'&&answer[1]=='E'&&answer[2]=='S'&&answer[3]=='\0') { printf("真是識相!\n"); } if(answer[0]=='N'&&answer[1]=='O'&&answer[2]=='\0') { printf("你眼光有問題!\n"); } return 0; }
| 輸入「YES」 |
| 輸入「NO」 |
請注意,「answer」的長度只有4,因此一旦輸入的長度超過3 (注意,還有一個表示字串結尾的「'\0'」) 就有可能導致程式出錯
那麼,要怎麼改成中文版本呢?是這樣嗎?
#include<cstdio> int main() { printf("我是天才? YES or NO\n"); char answer[3]; scanf("%s",answer); if(answer[0]=='是'&&answer[1]=='\0') { printf("真是識相!\n"); } if(answer[0]=='不'&&answer[1]=='是'&&answer[2]=='\0') { printf("你眼光有問題!\n"); } return 0; }
很遺憾的,事情並沒有這麼簡單
2016年9月15日 星期四
給新手的C++教學 (上冊) - 13 - 6. 更彈性的取得和釋放記憶體
回到「給新手的C++教學 (上冊)」
回到「13. 額外語法 (Extra syntax)」
上一頁
名詞解釋:
「釋放」記憶體:將某塊記憶體標記為「使用完畢」,讓這塊記憶體之後或讓其他程式可以再被利用
在之前的字元字串章節,我們使用了一個很大很大 (大小為100萬) 的陣列來儲存「一個名字」
除非要處理特別多的資料,否則應該是不會需要這麼大的陣列啦XD
但接下來,您會發現,當陣列太大的時候,會發生問題的
為了節省版面,這裡以字元字串章節的最後一份程式碼為例:
假如哪天你發現某人的名字太長了 (?),大小100萬的陣列不夠用 (!),想要宣告大小1000萬的陣列 (......)
好啊!欣然同意啊XD
咦?怎麼程式掛了?!
根本都還沒開始輸入名字呀!
回到「13. 額外語法 (Extra syntax)」
上一頁
名詞解釋:
「釋放」記憶體:將某塊記憶體標記為「使用完畢」,讓這塊記憶體之後或讓其他程式可以再被利用
在之前的字元字串章節,我們使用了一個很大很大 (大小為100萬) 的陣列來儲存「一個名字」
除非要處理特別多的資料,否則應該是不會需要這麼大的陣列啦XD
但接下來,您會發現,當陣列太大的時候,會發生問題的
為了節省版面,這裡以字元字串章節的最後一份程式碼為例:
#include<cstdio> int main() { char name[1000001]; scanf("%s",name); printf("Hello, %s!\n",name); return 0; }
| 輸入名字「Motivation」,會輸出「Hello, Motivation!」 |
假如哪天你發現某人的名字太長了 (?),大小100萬的陣列不夠用 (!),想要宣告大小1000萬的陣列 (......)
好啊!欣然同意啊XD
#include<cstdio> int main() { char name[10000001]; scanf("%s",name); printf("Hello, %s!\n",name); return 0; }
| 欸欸等等我甚麼是都還沒做耶! |
咦?怎麼程式掛了?!
根本都還沒開始輸入名字呀!
2016年9月12日 星期一
給新手的C++教學 (上冊) - 13 - 5. 陣列就是指標,指標就是陣列
回到「給新手的C++教學 (上冊)」
回到「13. 額外語法 (Extra syntax)」
上一頁
當您讀到「陣列」這個單元時,大可將「宣告一個大小為n的陣列」理解成「讓電腦幫你一次宣告n個變數」
但是,事實真的是如此嗎?
當您宣告一個變數的時候,電腦會挪出一塊記憶體給你用,啊然後咧?
這就像您想要網購某個東西,在購物網站上點了「購買」按鈕、付了錢,然後就以為沒事了(?)
不對啊,你總要知道要去哪裡取貨吧!
因此,程式向電腦「訂購」了記憶體之後,電腦就要告訴程式「取貨地址」--也就是「記憶體編號」,或稱「指標」
當程式執行到「int a;」這一行,程式會向電腦訂購32位元記憶體,然後電腦會告訴程式這32位元記憶體的「指標」。程式會小心翼翼地把這個「指標」記好,這樣才能隨時去使用它訂購的32位元記憶體
然而,有一件殘酷的事實是:
「記住指標」這個動作會讓程式需要額外的static記憶體 (之後會學到甚麼是static記憶體)
那這樣的話,把「宣告一個大小為$n$的陣列」理解成「讓電腦幫你一次宣告$n$個變數」會有甚麼問題嗎?
注意到,與其「訂購$32$位元記憶體$n$次,並記住$n$個指標」,還有另一種更有效率的方式:
直接一次性地訂購$32n$位元記憶體 (恩對一條街被買下來了(?)),然後依照「門牌號碼」分配給每個變數各$32$位元記憶體
差別在哪?這時程式只需要記住1個指標 (而非$n$個),也就是那「$32n$位元記憶體」的指標
當程式要取得第$i$ ($1\leq i\leq n$) 個變數的指標時,只要將第1個變數的指標加上$32(i-1)$位元就好了
怎麼證明電腦真的在做這件事?
信不信,陣列的名稱可以直接當指標來用XD (?!)
當然,小莫還要示範給你看!
回到「13. 額外語法 (Extra syntax)」
上一頁
當您讀到「陣列」這個單元時,大可將「宣告一個大小為n的陣列」理解成「讓電腦幫你一次宣告n個變數」
但是,事實真的是如此嗎?
當您宣告一個變數的時候,電腦會挪出一塊記憶體給你用,啊然後咧?
這就像您想要網購某個東西,在購物網站上點了「購買」按鈕、付了錢,然後就以為沒事了(?)
不對啊,你總要知道要去哪裡取貨吧!
因此,程式向電腦「訂購」了記憶體之後,電腦就要告訴程式「取貨地址」--也就是「記憶體編號」,或稱「指標」
當程式執行到「int a;」這一行,程式會向電腦訂購32位元記憶體,然後電腦會告訴程式這32位元記憶體的「指標」。程式會小心翼翼地把這個「指標」記好,這樣才能隨時去使用它訂購的32位元記憶體
然而,有一件殘酷的事實是:
「記住指標」這個動作會讓程式需要額外的static記憶體 (之後會學到甚麼是static記憶體)
那這樣的話,把「宣告一個大小為$n$的陣列」理解成「讓電腦幫你一次宣告$n$個變數」會有甚麼問題嗎?
注意到,與其「訂購$32$位元記憶體$n$次,並記住$n$個指標」,還有另一種更有效率的方式:
直接一次性地訂購$32n$位元記憶體 (恩對一條街被買下來了(?)),然後依照「門牌號碼」分配給每個變數各$32$位元記憶體
差別在哪?這時程式只需要記住1個指標 (而非$n$個),也就是那「$32n$位元記憶體」的指標
當程式要取得第$i$ ($1\leq i\leq n$) 個變數的指標時,只要將第1個變數的指標加上$32(i-1)$位元就好了
怎麼證明電腦真的在做這件事?
信不信,陣列的名稱可以直接當指標來用XD (?!)
當然,小莫還要示範給你看!
2016年9月2日 星期五
給新手的C++教學 (上冊) - 13 - 4. 指定輸出格式
回到「給新手的C++教學 (上冊)」
回到「13. 額外語法 (Extra syntax)」
上一頁
您是否有想過要固定輸出數字的寬度呢?
當您想要讓輸出的格式類似表格,行行之間的數字要對齊,就會想要這樣做
這時候該怎麼辦呢?
用代表「Tab」的「\t」來排版吧!
可是數字太長會讓「\t」跳到下一個位置,反而加大行行數字間的差距甚至造成混淆......
先想辦法算出數字有幾個位數,再決定每個數字前面各要補幾個空格?
功能完美!可是似乎有點麻煩......
哈哈,相信您已經猜到了--本章將教您如何簡單的設定輸出格式!
本方法僅限「scanf」和「printf」的使用者哦~
如果您是使用「cin」和「cout」,小莫真的不知道怎麼辦呢......XD
類似表格的東西喔......就拿九九乘法表來當例子吧!
首先,我們先把輸出九九乘法表9*9個數字的程式碼寫出來~
我們來把每個數字的寬度都固定成2吧~
您可能會想問,可以用一個「變數」來指定數字的寬度嗎?
沒問題!
回到「13. 額外語法 (Extra syntax)」
上一頁
您是否有想過要固定輸出數字的寬度呢?
當您想要讓輸出的格式類似表格,行行之間的數字要對齊,就會想要這樣做
這時候該怎麼辦呢?
用代表「Tab」的「\t」來排版吧!
可是數字太長會讓「\t」跳到下一個位置,反而加大行行數字間的差距甚至造成混淆......
先想辦法算出數字有幾個位數,再決定每個數字前面各要補幾個空格?
功能完美!可是似乎有點麻煩......
哈哈,相信您已經猜到了--本章將教您如何簡單的設定輸出格式!
本方法僅限「scanf」和「printf」的使用者哦~
類似表格的東西喔......就拿九九乘法表來當例子吧!
首先,我們先把輸出九九乘法表9*9個數字的程式碼寫出來~
#include<cstdio> int main() { for(int row=1;row<=9;row=row+1) { for(int column=1;column<=9;column=column+1) { if(column>1)printf(" ");//除了第一個數字之外,其餘的數字前面都要有一個空格 printf("%d",row*column); } printf("\n"); } return 0; }
| 九九乘法表~寫錯了也沒關係,可以直接貼程式碼哦 |
#include<cstdio> int main() { for(int row=1;row<=9;row=row+1) { for(int column=1;column<=9;column=column+1) { if(column>1)printf(" ");//除了第一個數字之外,其餘數字的前面都要有一個空格 printf("%2d",row*column); } printf("\n"); } return 0; }
| 耶~所有數字的寬度都固定成2了 |
耶~所有數字的寬度都固定成2了 \(^o^)/
等等,為甚麼?程式碼看起來沒差呀
其實真的沒差多少,我們只是把輸出數字的「%d」改成「%2d」而已 (在「printf("%d",row*column);」那裡)
就這麼簡單!
當然,如果想要把數字的寬度固定成3,把「%2d」改成「%3d」即可您可能會想問,可以用一個「變數」來指定數字的寬度嗎?
沒問題!
給新手的C++教學 (上冊) - 13 - 3. 進階型別的介紹
回到「給新手的C++教學 (上冊)」
回到「13. 額外語法 (Extra syntax)」
上一頁
請注意,這裡只介紹小莫用過的型別,但應該已經非常非常夠用了!
為了方便介紹,小莫將其分為兩大類:「浮點數」和「整數」(註13)
回到「13. 額外語法 (Extra syntax)」
上一頁
請注意,這裡只介紹小莫用過的型別,但應該已經非常非常夠用了!
為了方便介紹,小莫將其分為兩大類:「浮點數」和「整數」(註13)
- 浮點數
- 特色:支援小數、有微小誤差、數字範圍大
- 溢位會怎樣:超過最大值會變成inf,超過最小值會變成-inf
- 除以0會怎樣:正數除以0會變成inf、負數除以0會變成-inf,0除以0會變成nan (「not a number」的縮寫)
-
型別有哪些:
斜體標示 (針對__float128):如果要使用此型別,請先參考前一頁並設定好編譯參數,以使用C++11的功能名稱 float double long double __float128 占用記憶體 32位元 64位元 96位元 128位元 誤差和數字範圍 占用記憶體越大,誤差越小、數字範圍越大 輸入(scanf) %f %lf 不支援 不支援 輸出(printf) %f %f 不支援 不支援 - 整數
- 特色:不支援小數、沒有誤差、數字範圍小
- 溢位會怎樣:超過最大值會變成最小值,超過最小值會變成最大值
- 除以0會怎樣:會造成不可預知的錯誤,通常是程式出錯並停止運作
-
型別有哪些:
粗體標示 (針對char):輸入輸出皆以字元的方式進行 (而不是數字)名稱 bool char short int long long __int128 占用記憶體 8位元 8位元 16位元 32位元 64位元 128位元 數字範圍 $0\sim 1$ $(-2^{占用記憶體-1})\sim (2^{占用記憶體-1}-1)$ 輸入(scanf) 不支援 %c 不支援 %d %lld 不支援 輸入(scanf) 不支援 %c 不支援 %d %lld 不支援
斜體標示 (針對__int128):如果要使用此型別,請先參考前一頁並設定好編譯參數,以使用C++11的功能 - 另外,整數還可以用「unsigned」修飾而有更多玩法!意者請Google搜尋「C++ unsigned 用法」
2016年8月29日 星期一
給新手的C++教學 (上冊) - 13 - 2. 編譯參數的建議設定
回到「給新手的C++教學 (上冊)」
回到「13. 額外語法 (Extra syntax)」
上一頁
你在寫程式的時候,是否曾經犯過白癡錯誤呢?
有些錯誤是人可以很輕易識別的,但是卻恰好符合C++的語法而不會讓電腦察覺異狀
例如:
我知道你要做出小數加法,但是程式可以正常執行卻輸出錯誤答案 (不知道的趕快去試試看!輸入「0.1 0.1」,保證輸出不是「0.2」)
又偏偏錯誤在寫程式當下很難被察覺,可能直到整個幾十行甚至幾百行的程式碼寫完之後,才發現程式執行起來不對勁 (你發現哪裡寫錯了嗎?XD)
這樣一來要回去檢查整個程式碼就是非常麻煩的一件事
偷偷告訴你,這種白癡錯誤其實可以偷懶叫學過C++的電腦幫你找哦~
保證萬無一失!
方法如下:
回到「13. 額外語法 (Extra syntax)」
上一頁
你在寫程式的時候,是否曾經犯過白癡錯誤呢?
有些錯誤是人可以很輕易識別的,但是卻恰好符合C++的語法而不會讓電腦察覺異狀
例如:
#include<cstdio> int main() { float a,b; scanf("%d%d",&a,&b); printf("%d\n",a+b); return 0; }
我知道你要做出小數加法,但是程式可以正常執行卻輸出錯誤答案 (不知道的趕快去試試看!輸入「0.1 0.1」,保證輸出不是「0.2」)
| 怎麼會這樣?Orz |
又偏偏錯誤在寫程式當下很難被察覺,可能直到整個幾十行甚至幾百行的程式碼寫完之後,才發現程式執行起來不對勁 (你發現哪裡寫錯了嗎?XD)
這樣一來要回去檢查整個程式碼就是非常麻煩的一件事
偷偷告訴你,這種白癡錯誤其實可以偷懶叫學過C++的電腦幫你找哦~
保證萬無一失!
方法如下:
給新手的C++教學 (上冊) - 13 - 1. 「註解」的使用
回到「給新手的C++教學 (上冊)」
回到「13. 額外語法 (Extra syntax)」
上一頁 (這是第一頁)
如果有一天你想不開 (X) 去看了一年前寫的程式碼,很大的機會你是無法在短時間內就看懂的 (如果不相信的話可以實驗看看^_^)
真的嗎?
那就在寫程式的當下記錄這份程式碼在幹嘛的就好啦~
可是好麻煩喔,這樣有些想解釋更清楚的地方不就要再數數看在第幾行嗎?
例如以下程式碼:
你可能會想 (用Microsoft Word或記事本之類的) 寫個說明存成另一個檔案之類的:
這個程式會輸出「Hello world!\n」
第1行會引入標頭檔「cstdio」
第2行的「int」是主函式的型別
第2行的「main」是主函式的名字
第4行的「printf」是用來輸出的函式名字,兩個「"」裡面是要輸出的字串,所以這一行會讓程式輸出「Hello world!\n」
第5行是讓程式回傳0來正常結束
但是這樣的說明不論修改或閱讀都很麻煩 (因為還要指定或找到第幾行)
而且說明檔和程式碼是分開的,很不好管理
這時,「註解」就很好用了,它可以讓你直接把說明寫在程式碼裡面
用法如下:
這樣就可以很清楚的知道程式的每個地方在做甚麼事了!
當然,註解並不會對原本程式碼的功能產生任何影響,你想寫甚麼就寫甚麼、想寫多少就寫多少!
甚麼,你說你還是覺得另外開檔案存說明比較好?
回到「13. 額外語法 (Extra syntax)」
上一頁 (這是第一頁)
如果有一天你想不開 (X) 去看了一年前寫的程式碼,很大的機會你是無法在短時間內就看懂的 (如果不相信的話可以實驗看看^_^)
真的嗎?
那就在寫程式的當下記錄這份程式碼在幹嘛的就好啦~
可是好麻煩喔,這樣有些想解釋更清楚的地方不就要再數數看在第幾行嗎?
例如以下程式碼:
#include<cstdio> int main() { printf("Hello world!\n"); return 0; }
你可能會想 (用Microsoft Word或記事本之類的) 寫個說明存成另一個檔案之類的:
這個程式會輸出「Hello world!\n」
第1行會引入標頭檔「cstdio」
第2行的「int」是主函式的型別
第2行的「main」是主函式的名字
第4行的「printf」是用來輸出的函式名字,兩個「"」裡面是要輸出的字串,所以這一行會讓程式輸出「Hello world!\n」
第5行是讓程式回傳0來正常結束
但是這樣的說明不論修改或閱讀都很麻煩 (因為還要指定或找到第幾行)
而且說明檔和程式碼是分開的,很不好管理
這時,「註解」就很好用了,它可以讓你直接把說明寫在程式碼裡面
用法如下:
/*灰色的字都是註解 它們可以讓你輕易地看出這份程式碼在幹嘛 例如這份程式碼會讓程式輸出「Hello world!\n」*/ #include<cstdio>//引入標頭檔「cstdio」 int main()//int是主函式的型別,main是主函式的名字 { printf("Hello world!\n");//輸出「Hello world!\n」 /*「printf」是用來輸出的函式名字 兩個「"」裡面是要輸出的字串*/ return 0;//讓程式回傳0來正常結束 }
這樣就可以很清楚的知道程式的每個地方在做甚麼事了!
當然,註解並不會對原本程式碼的功能產生任何影響,你想寫甚麼就寫甚麼、想寫多少就寫多少!
甚麼,你說你還是覺得另外開檔案存說明比較好?
2016年6月2日 星期四
2016年6月1日 星期三
給新手的C++教學 (上冊) - B. 遇到困難了? (There are troubles?)
回到「給新手的C++教學 (上冊)」
上一章 (這要怎麼定義上一章啦喂)
本章列出新手在進行本教學的練習時可能會遇到的困難
並提供解決方案
希望能幫助新手們更快、更方便的利用本教學學習新知識!
Q:
看不懂教學在說甚麼QAQ
A:
請問您真的有動手把程式碼用鍵盤打出來,並按下F11執行過嗎?
程式的學習非常依賴自己動手試試看
就像做實驗一樣,在一個字一個字慢慢的打出程式碼的過程中,您才會去觀察規律、猜測其規則、自己修改程式碼來驗證看看,相信很快 (10分鐘內) 就可以掌握觀念!
(真的,不動手試試看就永遠學不會)
這些規則其實是很簡單的,類似小學在玩的益智遊戲,但如果每一個規則都要弄成文字說明,大概在看懂之前就會眼花了吧XD
如果還是不懂,或者理解時間超過10分鐘,歡迎在文章下面留言~
您看不懂,一定還有另一個人也看不懂,所以請別吝嗇,在文章下面勇敢留言說出您遇到的困難吧!
Q:
電腦一直說我的程式寫錯,我不知道為甚麼?
我寫出來的程式就是無法依照預期的方式運作,我不知道為甚麼?
A:
這種情況對新手來說通常不是自己能解決的事,請先參考本教學提供的範例程式碼,並嘗試照著打一遍看看
如果還是出錯,請直接用「複製」、「貼上」的方式,將程式碼貼到你的「Dev-C++」裡面,再按一次鍵盤上的「F11」試試看
當電腦成功讀懂你的程式碼,並且程式是正確執行出預期的結果之後
請再回去比對你原本會出錯的程式碼,找出出錯的原因
這很重要,可以幫助你以後更不會再犯相同的錯誤
如果還是出錯......就代表非常緊急的狀況發生了--本教學的程式碼也錯了!!!
請務必盡快通知小莫,將盡快做修正,感激不盡!
電子信箱:fsps60312@yahoo.com.tw
Q:
怎麼教學看到一半就突然出現看不懂的專有名詞?
怎麼教學看到一半就突然出現還沒教到的東西?
A:
代表小莫沒有考慮好初學者的立場,不小心用到了很專業的術語或進階的語法
請務必盡快通知小莫,將盡快做修正,感激不盡!
電子信箱:fsps60312@yahoo.com.tw
Q:
怎麼辦?上面都沒有提到我的問題QAQ
A:
唉呀呀,你遇到了甚麼問題?
趕快在本章下面留言,或者用任何方式和管道通知小莫
小莫會盡快提供解答,並更新本章內容!
感激不盡!
電子信箱:fsps60312@yahoo.com.tw
下一章 (這要怎麼定義下一章啦喂)
感謝:
(版權所有 All copyright reserved)
上一章 (這要怎麼定義上一章啦喂)
本章列出新手在進行本教學的練習時可能會遇到的困難
並提供解決方案
希望能幫助新手們更快、更方便的利用本教學學習新知識!
Q:
看不懂教學在說甚麼QAQ
A:
請問您真的有動手把程式碼用鍵盤打出來,並按下F11執行過嗎?
程式的學習非常依賴自己動手試試看
就像做實驗一樣,在一個字一個字慢慢的打出程式碼的過程中,您才會去觀察規律、猜測其規則、自己修改程式碼來驗證看看,相信很快 (10分鐘內) 就可以掌握觀念!
(真的,不動手試試看就永遠學不會)
這些規則其實是很簡單的,類似小學在玩的益智遊戲,但如果每一個規則都要弄成文字說明,大概在看懂之前就會眼花了吧XD
如果還是不懂,或者理解時間超過10分鐘,歡迎在文章下面留言~
您看不懂,一定還有另一個人也看不懂,所以請別吝嗇,在文章下面勇敢留言說出您遇到的困難吧!
Q:
電腦一直說我的程式寫錯,我不知道為甚麼?
我寫出來的程式就是無法依照預期的方式運作,我不知道為甚麼?
A:
這種情況對新手來說通常不是自己能解決的事,請先參考本教學提供的範例程式碼,並嘗試照著打一遍看看
如果還是出錯,請直接用「複製」、「貼上」的方式,將程式碼貼到你的「Dev-C++」裡面,再按一次鍵盤上的「F11」試試看
當電腦成功讀懂你的程式碼,並且程式是正確執行出預期的結果之後
請再回去比對你原本會出錯的程式碼,找出出錯的原因
這很重要,可以幫助你以後更不會再犯相同的錯誤
如果還是出錯......就代表非常緊急的狀況發生了--本教學的程式碼也錯了!!!
請務必盡快通知小莫,將盡快做修正,感激不盡!
電子信箱:fsps60312@yahoo.com.tw
Q:
怎麼教學看到一半就突然出現看不懂的專有名詞?
怎麼教學看到一半就突然出現還沒教到的東西?
A:
代表小莫沒有考慮好初學者的立場,不小心用到了很專業的術語或進階的語法
請務必盡快通知小莫,將盡快做修正,感激不盡!
電子信箱:fsps60312@yahoo.com.tw
Q:
怎麼辦?上面都沒有提到我的問題QAQ
A:
唉呀呀,你遇到了甚麼問題?
趕快在本章下面留言,或者用任何方式和管道通知小莫
小莫會盡快提供解答,並更新本章內容!
感激不盡!
電子信箱:fsps60312@yahoo.com.tw
下一章 (這要怎麼定義下一章啦喂)
感謝:
(版權所有 All copyright reserved)
給新手的C++教學 (上冊) - 12. 指標 (Pointer)
回到「給新手的C++教學 (上冊)」
上一章
這一章,我們將更深入地探討電腦的運作原理
進而更完善的掌控電腦的記憶體
你會發現,你將會有能力控制更多平常想不到的東西!
你有想過嗎?
當我們宣告一個變數的時候,電腦會撥出一些記憶體來讓程式儲存變數的資訊
但是,電腦的記憶體是有限的,總不可能每一塊記憶體都只有使用一次吧?
這樣的話每一台電腦的記憶體大概都只夠用10分鐘了XD
因此,允許記憶體被重複使用的機制是必要的
為了讓記憶體能夠被重複使用,我們必須確認用過的記憶體中,哪些現在還在使用中、哪些已經使用完畢了
那麼,我們的程式當然只能使用那些已經使用完畢的記憶體,否則修改到其他程式正在使用的記憶體,進而導致其他程式出錯
觀念簡單,但是要有效率地做到這件事 (避免每次找記憶體前都要老老實實的把每一塊記憶體都確認一遍),需要許多先進的演算法知識,而且程式碼寫起來非常的麻煩
放心,這件事連我都不知道怎麼做
所謂「站在巨人的肩膀上」,這種事情不用再由我們自己處理了!
在先人的努力之下,「C++」這個偉大的程式語言,已經讓電腦可以只依據簡單的幾行程式碼,就可以執行許多複雜卻需要經常執行的工作
換句話說
我們在撰寫C++程式碼的時候,只需要告訴電腦「需要使用多少記憶體」和「哪些記憶體已經使用完畢」就好了!
我們在宣告變數的時候,就等於是告訴電腦「恩,我需要這麼多記憶體來儲存這一個變數的資訊」,然後電腦就會把你要的記憶體給你
但是,我們有告訴過電腦「哪些記憶體已經使用完畢」嗎?
有的!
還記得在第十章的時候,我們有提到「每一個變數都有它自己的作用範圍」嗎?
那麼,當正在執行的程式碼位於一個變數的作用範圍外,這個變數所使用的記憶體該怎麼辦呢?
這時我們不會需要用這個變數來儲存任何資訊
也就是,這個變數目前不需要使用記憶體來儲存資訊
電腦學過C++,想當然會利用這個「作用範圍外記憶體就不需要了」的特性
不管是甚麼變數,只要程式執行到它的「作用範圍」之外
電腦就會「自動」把這個變數使用的記憶體當作「使用完畢」
也就是說,現在,這塊記憶體可能是閒置的,或者被其他的程式拿去使用了
如果我們再去修改這塊記憶體,會造成不可預知的錯誤
當然,你還沒開始學習本章的內容,不會知道怎麼在作用範圍外修改這塊記憶體
在作用範圍外修改記憶體?
有需要嗎?
是的,有時候會很需要,請看本章範例
感覺很危險?
是的,使用不當會造成其他程式的錯誤,甚至讓整台電腦當機,必須將電腦插頭拔掉再重新開機
因此,在學習本章前,請務必將重要資料存檔,以避免造成不必要的損失
嘿嘿嘿~
現在
我們開始吧XD
上一章
這一章,我們將更深入地探討電腦的運作原理
進而更完善的掌控電腦的記憶體
你會發現,你將會有能力控制更多平常想不到的東西!
你有想過嗎?
當我們宣告一個變數的時候,電腦會撥出一些記憶體來讓程式儲存變數的資訊
但是,電腦的記憶體是有限的,總不可能每一塊記憶體都只有使用一次吧?
這樣的話每一台電腦的記憶體大概都只夠用10分鐘了XD
因此,允許記憶體被重複使用的機制是必要的
為了讓記憶體能夠被重複使用,我們必須確認用過的記憶體中,哪些現在還在使用中、哪些已經使用完畢了
那麼,我們的程式當然只能使用那些已經使用完畢的記憶體,否則修改到其他程式正在使用的記憶體,進而導致其他程式出錯
觀念簡單,但是要有效率地做到這件事 (避免每次找記憶體前都要老老實實的把每一塊記憶體都確認一遍),需要許多先進的演算法知識,而且程式碼寫起來非常的麻煩
放心,這件事連我都不知道怎麼做
所謂「站在巨人的肩膀上」,這種事情不用再由我們自己處理了!
在先人的努力之下,「C++」這個偉大的程式語言,已經讓電腦可以只依據簡單的幾行程式碼,就可以執行許多複雜卻需要經常執行的工作
換句話說
我們在撰寫C++程式碼的時候,只需要告訴電腦「需要使用多少記憶體」和「哪些記憶體已經使用完畢」就好了!
我們在宣告變數的時候,就等於是告訴電腦「恩,我需要這麼多記憶體來儲存這一個變數的資訊」,然後電腦就會把你要的記憶體給你
但是,我們有告訴過電腦「哪些記憶體已經使用完畢」嗎?
有的!
還記得在第十章的時候,我們有提到「每一個變數都有它自己的作用範圍」嗎?
那麼,當正在執行的程式碼位於一個變數的作用範圍外,這個變數所使用的記憶體該怎麼辦呢?
這時我們不會需要用這個變數來儲存任何資訊
也就是,這個變數目前不需要使用記憶體來儲存資訊
電腦學過C++,想當然會利用這個「作用範圍外記憶體就不需要了」的特性
不管是甚麼變數,只要程式執行到它的「作用範圍」之外
電腦就會「自動」把這個變數使用的記憶體當作「使用完畢」
也就是說,現在,這塊記憶體可能是閒置的,或者被其他的程式拿去使用了
如果我們再去修改這塊記憶體,會造成不可預知的錯誤
當然,你還沒開始學習本章的內容,不會知道怎麼在作用範圍外修改這塊記憶體
在作用範圍外修改記憶體?
有需要嗎?
是的,有時候會很需要,請看本章範例
感覺很危險?
是的,使用不當會造成其他程式的錯誤,甚至讓整台電腦當機,必須將電腦插頭拔掉再重新開機
因此,在學習本章前,請務必將重要資料存檔,以避免造成不必要的損失
嘿嘿嘿~
現在
我們開始吧XD
2016年5月25日 星期三
給新手的C++教學 (上冊) - 11. 結構 (Structure)
回到「給新手的C++教學 (上冊)」
上一章
在第九章,我們學到了函式
函式真的好好用耶!
咦?真的嗎?
現在,給你三個直線方程式
請你輸出這三條直線相交所形成的三角形的三個頂點座標 (也就是直線的三個交點的座標)
輸入格式:有三行,第$i$行包含第$i$條直線的資訊:三個數字$a_i$、$b_i$、$c_i$,代表第$i$條直線的方程式$a_i x+b_i y=c_i$ (保證三條直線可以形成一個三角形)
輸出格式:有三行,第$i$行包含第$i$個頂點 (交點) 的座標資訊:兩個數字$x_i$、$y_i$,代表第$i$個頂點座標$(x_i,y_i)$ (請輸出小數,頂點順序隨意)
提示:兩條直線$a_{1}x+b_{1}y=c_{1}$、$a_{2}x+b_{2}y=c_{2}$的交點求法:
$\begin{cases}
&a_{1}x+b_{1}y=c_{1}\\
&a_{2}x+b_{2}y=c_{2}
\end{cases}\cdots\cdots ①$
$①\Rightarrow
\begin{cases}
&a_{1}a_{2}x+b_{1}a_{2}y=c_{1}a_{2}\cdots\cdots ②\\
&a_{1}a_{2}x+a_{1}b_{2}y=a_{1}c_{2}\cdots\cdots ③
\end{cases}$
p.s. 以上推導過程看不懂也沒關係
上一章
在第九章,我們學到了函式
函式真的好好用耶!
咦?真的嗎?
現在,給你三個直線方程式
請你輸出這三條直線相交所形成的三角形的三個頂點座標 (也就是直線的三個交點的座標)
輸入格式:有三行,第$i$行包含第$i$條直線的資訊:三個數字$a_i$、$b_i$、$c_i$,代表第$i$條直線的方程式$a_i x+b_i y=c_i$ (保證三條直線可以形成一個三角形)
輸出格式:有三行,第$i$行包含第$i$個頂點 (交點) 的座標資訊:兩個數字$x_i$、$y_i$,代表第$i$個頂點座標$(x_i,y_i)$ (請輸出小數,頂點順序隨意)
提示:兩條直線$a_{1}x+b_{1}y=c_{1}$、$a_{2}x+b_{2}y=c_{2}$的交點求法:
$\begin{cases}
&a_{1}x+b_{1}y=c_{1}\\
&a_{2}x+b_{2}y=c_{2}
\end{cases}\cdots\cdots ①$
$①\Rightarrow
\begin{cases}
&a_{1}a_{2}x+b_{1}a_{2}y=c_{1}a_{2}\cdots\cdots ②\\
&a_{1}a_{2}x+a_{1}b_{2}y=a_{1}c_{2}\cdots\cdots ③
\end{cases}$
$②-③\Rightarrow (b_{1}a_{2}-a_{1}b_{2})y=c_{1}a_{2}-a_{1}c_{2}$
$\Rightarrow y=\frac{c_{1}a_{2}-a_{1}c_{2}}{b_{1}a_{2}-a_{1}b_{2}}$
$①\Rightarrow
\begin{cases}
&a_{1}b_{2}x+b_{1}b_{2}y=c_{1}b_{2}\cdots\cdots ④\\
&b_{1}a_{2}x+b_{1}b_{2}y=b_{1}c_{2}\cdots\cdots ⑤
\end{cases}$
\begin{cases}
&a_{1}b_{2}x+b_{1}b_{2}y=c_{1}b_{2}\cdots\cdots ④\\
&b_{1}a_{2}x+b_{1}b_{2}y=b_{1}c_{2}\cdots\cdots ⑤
\end{cases}$
$④-⑤\Rightarrow (a_{1}b_{2}-b_{1}a_{2})x=c_{1}b_{2}-b_{1}c_{2}$
$\Rightarrow x=\frac{c_{1}b_{2}-b_{1}c_{2}}{a_{1}b_{2}-b_{1}a_{2}}$
p.s. 以上推導過程看不懂也沒關係
$\therefore$兩線交點為$(\frac{c_{1}b_{2}-b_{1}c_{2}}{a_{1}b_{2}-b_{1}a_{2}},\frac{c_{1}a_{2}-a_{1}c_{2}}{b_{1}a_{2}-a_{1}b_{2}})$
啊不就三條直線兩兩求交點就好?
懶得寫三遍同樣的東西,就弄成一個函式吧~
引數 (甚麼是引數?) 就傳遞兩條線的資訊,然後函式就可以進行計算,最後回傳交點的座標......
等等,怎麼回傳「一個座標」?
float、int、char,都不對呀
我們需要一次回傳「兩個float」
懶得寫三遍同樣的東西,就弄成一個函式吧~
引數 (甚麼是引數?) 就傳遞兩條線的資訊,然後函式就可以進行計算,最後回傳交點的座標......
等等,怎麼回傳「一個座標」?
float、int、char,都不對呀
我們需要一次回傳「兩個float」
2016年5月19日 星期四
給新手的C++教學 (上冊) - 10. 變數的作用範圍 (Scope)
回到「給新手的C++教學 (上冊)」
上一章
不知你在之前的章節中做練習時,電腦是否莫名其妙地說你寫錯呢?
舉個例子
還記得第6章的練習題嗎?
題目是這樣的:
「輸入一個整數n,然後輸入n個數字
最後把這n個數字的順序顛倒,然後輸出」
當你學完陣列之後,馬上就興高采烈地寫下了程式碼:
然後,電腦就說你寫錯了
哦~原來我前面已經宣告了一個名字為「i」的變數了,不能再宣告一個名字一模一樣 (同樣是「i」) 的變數
p.s. 雖然之前的章節完全沒有提到,這個命名不能重複的規則請讀者務必了解,仔細想想應該就會發現這個規則合情合理,否則最後會分不清楚「i」到底是指哪一個變數
如果你還是搞不懂發生了甚麼事,將上面的程式碼和第6章提供的程式碼解答比對一下,看看有甚麼差別吧~
現階段,這種問題是很好解決的
以目前的知識,可以想到的方法有2種:
上一章
不知你在之前的章節中做練習時,電腦是否莫名其妙地說你寫錯呢?
舉個例子
還記得第6章的練習題嗎?
題目是這樣的:
「輸入一個整數n,然後輸入n個數字
最後把這n個數字的順序顛倒,然後輸出」
當你學完陣列之後,馬上就興高采烈地寫下了程式碼:
#include<cstdio> int main() { int n; scanf("%d",&n); int a[n]; int i=0; while(i<n) { scanf("%d",&a[i]); i=i+1; } int i=n-1; while(i>=0) { printf("%d ",a[i]); i=i-1; } printf("\n"); }
然後,電腦就說你寫錯了
| 然後,電腦就說你寫錯了XD |
p.s. 雖然之前的章節完全沒有提到,這個命名不能重複的規則請讀者務必了解,仔細想想應該就會發現這個規則合情合理,否則最後會分不清楚「i」到底是指哪一個變數
如果你還是搞不懂發生了甚麼事,將上面的程式碼和第6章提供的程式碼解答比對一下,看看有甚麼差別吧~
現階段,這種問題是很好解決的
以目前的知識,可以想到的方法有2種:
2016年5月17日 星期二
給新手的C++教學 (上冊) - 9. 函式 (Function)
回到「給新手的C++教學 (上冊)」
上一章
依照慣例,在本章依然要給你一個題目,然後你就會發覺需要學習某種新技能,才能有效率的寫出可以解決問題的程式碼 (?)
給你一個正整數$n$,請求出第$n$個卡特蘭數
提示:
第$n$個卡特蘭數$=C^{2n}_{n}-C^{2n}_{n+1}$
其中$C^{a}_{b}=\frac{a!}{b!(a-b)!}$
其中「$x!$」念作「$x$階層」,$x!=1*2*3*...*x=\prod_{k=1}^{x}k$
當然,$n$不能太大,否則會發生溢位 (overflow) 的情況 (甚麼是溢位?)
所以$n$的範圍會在$1\sim 6$內
卡特蘭數列:1、2、5、14、42、132、......
你的程式答對前6項了嗎?
參考程式碼:
程式碼中,變數a、b、c分別表示$C^{a}_{b}$中的$a!$、$b!$、$(a-b)!$
你可能會覺得:程式碼好冗長呀~
然後心裡想說:等一下一定會教一個更簡單的方法
咦?XD
沒錯!
接下來,本章會教你,怎麼利用「函式 (Function)」來化簡你的程式碼!
上一章
依照慣例,在本章依然要給你一個題目,然後你就會發覺需要學習某種新技能,才能有效率的寫出可以解決問題的程式碼 (?)
給你一個正整數$n$,請求出第$n$個卡特蘭數
提示:
第$n$個卡特蘭數$=C^{2n}_{n}-C^{2n}_{n+1}$
其中$C^{a}_{b}=\frac{a!}{b!(a-b)!}$
其中「$x!$」念作「$x$階層」,$x!=1*2*3*...*x=\prod_{k=1}^{x}k$
當然,$n$不能太大,否則會發生溢位 (overflow) 的情況 (甚麼是溢位?)
所以$n$的範圍會在$1\sim 6$內
卡特蘭數列:1、2、5、14、42、132、......
你的程式答對前6項了嗎?
參考程式碼:
#include<cstdio> int main() { int n; scanf("%d",&n); int a=1,b=1,c=1; int i=1; while(i<=2*n) { a=a*i; i=i+1; } i=1; while(i<=n) { b=b*i; i=i+1; } i=1; while(i<=2*n-n) { c=c*i; i=i+1; } int first=a/(b*c); a=1,b=1,c=1,i=1; while(i<=2*n) { a=a*i; i=i+1; } i=1; while(i<=n-1) { b=b*i; i=i+1; } i=1; while(i<=2*n-(n-1)) { c=c*i; i=i+1; } int second=a/(b*c); printf("%d\n",first-second); return 0; }
程式碼中,變數a、b、c分別表示$C^{a}_{b}$中的$a!$、$b!$、$(a-b)!$
| 當輸入$n=6$時,程式輸出第6個卡特蘭數「132」了! |
然後心裡想說:等一下一定會教一個更簡單的方法
咦?XD
沒錯!
接下來,本章會教你,怎麼利用「函式 (Function)」來化簡你的程式碼!
2016年5月10日 星期二
給新手的C++教學 (上冊) - 8. 字元 & 字串 (Charactor & String)
回到「給新手的C++教學 (上冊)」
上一章
不知你是否嘗試過讓程式做到以下事情:
輸入一個名字 (大小寫字母組成),輸出「Hello, 名字!」
1. 土法煉鋼法
我們已經學過怎麼輸入一個字元 (甚麼是字元?),也知道怎麼用直接的方式 (用兩個「'」框起來) 表示一個字元
所以,我們可以一個一個的輸入字元,然後一一判斷是不是大小寫字母,當輸入的字元不是大小寫字母時,我們就知道一個「名字」已經輸入完了 (因為名字只能由大小寫字母組成)
套用上一章所學的簡化方式,我們就可以實現這個輸入名字的功能 (以下程式碼已經假設名字的長度$\leq$一百萬):
我才不信你會想這樣搞咧,對吧?
52個字母一一判斷實在是太累了,而且容易打錯
很幸運地,對電腦來說,「一個字元」其實就是「一個數字」
例如:「M」對應到「77」、「o」對應到「111」
如果你硬要把這些字元用整數的「%d」輸出 (而不是字元的「%c」),你看到的就是這些字元所對應到的數字
我用以下程式碼做示範:
沒意外的話,程式會輸出「77 111」
因此,你甚至可以把每個直接表示的字元用數字來取代
把上面輸入一個名字的程式改寫成這樣:
測試看看,程式依然是同樣的邏輯!
上一章
不知你是否嘗試過讓程式做到以下事情:
輸入一個名字 (大小寫字母組成),輸出「Hello, 名字!」
1. 土法煉鋼法
我們已經學過怎麼輸入一個字元 (甚麼是字元?),也知道怎麼用直接的方式 (用兩個「'」框起來) 表示一個字元
所以,我們可以一個一個的輸入字元,然後一一判斷是不是大小寫字母,當輸入的字元不是大小寫字母時,我們就知道一個「名字」已經輸入完了 (因為名字只能由大小寫字母組成)
套用上一章所學的簡化方式,我們就可以實現這個輸入名字的功能 (以下程式碼已經假設名字的長度$\leq$一百萬):
#include<cstdio> int main() { char name[1000001]; int i=0; while(1==1) { scanf("%c",&name[i]); char c=name[i]; if(c!='a'&&c!='b'&&c!='c'&&c!='d'&&c!='e'&&c!='f'&&c!='g'&&c!='h'&&c!='i'&&c!='j'&&c!='k'&&c!='l'&&c!='m'&&c!='n'&&c!='o'&&c!='p'&&c!='q'&&c!='r'&&c!='s'&&c!='t'&&c!='u'&&c!='v'&&c!='w'&&c!='x'&&c!='y'&&c!='z'&&c!='A'&&c!='B'&&c!='C'&&c!='D'&&c!='E'&&c!='F'&&c!='G'&&c!='H'&&c!='I'&&c!='J'&&c!='K'&&c!='L'&&c!='M'&&c!='N'&&c!='O'&&c!='P'&&c!='Q'&&c!='R'&&c!='S'&&c!='T'&&c!='U'&&c!='V'&&c!='W'&&c!='X'&&c!='Y'&&c!='Z')break; i=i+1; } printf("Hello, "); int j=0; while(j<i) { printf("%c",name[j]); j=j+1; } printf("!\n"); return 0; }
我才不信你會想這樣搞咧,對吧?
52個字母一一判斷實在是太累了,而且容易打錯
很幸運地,對電腦來說,「一個字元」其實就是「一個數字」
例如:「M」對應到「77」、「o」對應到「111」
如果你硬要把這些字元用整數的「%d」輸出 (而不是字元的「%c」),你看到的就是這些字元所對應到的數字
我用以下程式碼做示範:
#include<cstdio> int main() { printf("%d %d\n",'M','o'); }
沒意外的話,程式會輸出「77 111」
| 程式輸出「77 111」了! |
因此,你甚至可以把每個直接表示的字元用數字來取代
把上面輸入一個名字的程式改寫成這樣:
#include<cstdio> int main() { char name[1000001]; int i=0; while(1==1) { scanf("%c",&name[i]); char c=name[i]; if(c!=97&&c!=98&&c!=99&&c!=100&&c!=101&&c!=102&&c!=103&&c!=104&&c!=105&&c!=106&&c!=107&&c!=108&&c!=109&&c!=110&&c!=111&&c!=112&&c!=113&&c!=114&&c!=115&&c!=116&&c!=117&&c!=118&&c!=119&&c!=120&&c!=121&&c!=122&&c!=65&&c!=66&&c!=67&&c!=68&&c!=69&&c!=70&&c!=71&&c!=72&&c!=73&&c!=74&&c!=75&&c!=76&&c!=77&&c!=78&&c!=79&&c!=80&&c!=81&&c!=82&&c!=83&&c!=84&&c!=85&&c!=86&&c!=87&&c!=88&&c!=89&&c!=90)break; i=i+1; } printf("Hello, "); int j=0; while(j<i) { printf("%c",name[j]); j=j+1; } printf("!\n"); return 0; }
測試看看,程式依然是同樣的邏輯!
| 輸入「Mobius」,程式依然是輸出「Hello, Mobius!」 (順帶一提,「Mobius」是我們「105級高雄中學科學班成果發表會」的名稱,歡迎大家前去看看~~~) |
2016年5月8日 星期日
給新手的C++教學 (上冊) - 7. 讓程式碼看起來更簡單 (Simplify the code)
回到「給新手的C++教學 (上冊)」
上一章
上一章有提到,利用前6章所學,已經可以完全掌控電腦了
現在,我想問你,能否寫出一個程式,完成以下任務呢?
輸入一個字元,請判斷這個字元是不是「小寫母音字母 (a、e、i、o、u)」,是的話輸出「Yes」,否則輸出「No」(甚麼是字元?複習一下第2章吧~)
題外話:
怎麼表示「一個字元 (charactor),代表小寫字母『a』」呢?
不知道沒關係,因為之前的章節沒有提到XD
答案是「'a'」(用兩個「'」框住你想表達的字元)
這個表示法雖然是隨手提及,但是非常重要,之後的教學只要看到兩個「'」框起來的東西,你都要把它當作「一個字元」來看待
參考解答:
你可能會發現:程式好冗長啊
有沒有簡單一點的寫法呢?
會這樣問,答案當然是「有」啊
可以發現,有很多的大括號 (「{」和「}」) 包起來的部分都只有一行程式碼 (「printf("Yes\n");」或「printf("No\n");」)
在這種情況下,大括號其實是可以省略的
所以,程式碼也可以這樣寫,完全不影響程式的功能:
另外,一個「if」和一個「else」是配對起來的,不可分離
因此,我們甚至也可以省略將它們包起來的大括號!
廣泛的說,如果大括號裡面包起來的程式碼是不可分離的
那麼這個大括號就可以省略
這樣一來,程式碼又可以簡化成以下這樣了:
上一章
上一章有提到,利用前6章所學,已經可以完全掌控電腦了
現在,我想問你,能否寫出一個程式,完成以下任務呢?
輸入一個字元,請判斷這個字元是不是「小寫母音字母 (a、e、i、o、u)」,是的話輸出「Yes」,否則輸出「No」(甚麼是字元?複習一下第2章吧~)
題外話:
怎麼表示「一個字元 (charactor),代表小寫字母『a』」呢?
不知道沒關係,因為之前的章節沒有提到XD
答案是「'a'」(用兩個「'」框住你想表達的字元)
這個表示法雖然是隨手提及,但是非常重要,之後的教學只要看到兩個「'」框起來的東西,你都要把它當作「一個字元」來看待
參考解答:
#include<cstdio> int main() { char a; scanf("%c",&a); if(a=='a') { printf("Yes\n"); } else { if(a=='e') { printf("Yes\n"); } else { if(a=='i') { printf("Yes\n"); } else { if(a=='o') { printf("Yes\n"); } else { if(a=='u') { printf("Yes\n"); } else { printf("No\n"); } } } } } return 0; }
你可能會發現:程式好冗長啊
有沒有簡單一點的寫法呢?
會這樣問,答案當然是「有」啊
可以發現,有很多的大括號 (「{」和「}」) 包起來的部分都只有一行程式碼 (「printf("Yes\n");」或「printf("No\n");」)
在這種情況下,大括號其實是可以省略的
所以,程式碼也可以這樣寫,完全不影響程式的功能:
#include<cstdio> int main() { char a; scanf("%c",&a); if(a=='a') printf("Yes\n"); else { if(a=='e') printf("Yes\n"); else { if(a=='i') printf("Yes\n"); else { if(a=='o') printf("Yes\n"); else { if(a=='u') printf("Yes\n"); else printf("No\n"); } } } } return 0; }
另外,一個「if」和一個「else」是配對起來的,不可分離
因此,我們甚至也可以省略將它們包起來的大括號!
廣泛的說,如果大括號裡面包起來的程式碼是不可分離的
那麼這個大括號就可以省略
這樣一來,程式碼又可以簡化成以下這樣了:
訂閱:
文章 (Atom)

