Learn More →
本著作係採用創用 CC 姓名標示-非商業性 3.0 台灣 授權條款授權.
在服用所有團隊內容前先看這篇,不然 @yen0224 會打你屁屁,不遵守請立即離開,凡已觀看其他內容則視為已同意申明內條款
last updated: 2021, Mar 27th
新增陣列、函數
課本解答參閱程式範例解答(目前編輯中進度約10%)
相關練習題在GitHub\
以上都大同小異啦,但Visual studio通常都有自己的“功能”,上手需一段時間
微電腦最原始的語言—即僅以代表數位高低電位的1、0組成,並以不同的10組合代表不同的機器指令集,這種語言除了可讀性差之外,不同種的機器也有不同種的機器語言,然而這種語言絕對有必要存在,因為電腦只看得懂這種語言,因此為解決上述的兩種問題,勢必得找出機器語言的替代方案。
組合語言即為解決上述問題的其中一種方案,以一些特別的輔助記憶碼mnemonic來代替機器指令集,並建置出一個「組譯器」負責將記憶碼與該機器上的指令集進行轉換,以此方式,使得組譯語言可在不同的平台進行移植,並且大大的簡化了程式的開發過程。
add 2, 3, result
高階語言與低階語言僅是相對的概念,通常把machine code, assembly code歸類於低階語言;現在大多學到的語言全數屬於高階語言,如C++, Python, Java, Visual Basic等
C相較於C++介於低階語言與高階語言間,較“貼近硬體的運作”,因此寫法較C++對人而言比較生澀難懂;C++則是赴著在C上的語言,C的header亦可用在C++中,且C++支援物件導向(object-oriented programming, OOP),對人較好理解
C series語言的source code在進行編碼至機器碼的執行檔(windows是.exe, Unix系統是.out)前,皆會進行前處理,編譯,組譯,連結四部份,而C++語言本身只含核心關鍵字,故我們在打c++ source code時,需同時去引入(#include)相關的標頭,使前處理器可正常的運行
預先處理過程就是將#include、#define、#if、特殊符號等取出需要的東西後去除,且與原檔同義,只有常數、字符串、變數定義、以及C關鍵字等yen0224
命令列編譯指令:
(前往cpp檔位置)
cd [address]
(name.cpp為檔名,請替換,a.out為預設名稱,不可更改)
g++ name.cpp && ./a.out
(optional parameter 自訂輸出檔名稱
g++ -o name name.cpp
預處理器指示詞(例如 #define 和 #ifdef )通常用來讓來源程式在不同的執行環境中變得更容易變更和編譯。 來源檔案中的指示詞會指示前處理器採取特定動作。 例如,前置處理器可以取代文字中的語彙基元、將其他檔案的內容插入原始程式檔,或是透過移除文字區段來隱藏編譯檔案的一部分。 在巨集展開之前,會辨識並執行前置處理器程式行。
目前學習階段用到的前處理器keyword有:
//語法
#define 識別符 值
#define PI 3.14 //定義PI為常數3.14
#define multiply( a1 , a2 ) ( a1 * a2)//定義multiply()
//語法
#undef 識別符
#undef PI
//語法
#include <標頭檔名>
#include "路徑"
#include <iostream> //宣告引入“iostream”標頭
#include <algorithms>//宣告引入“algorithms”標頭
#include "usr/lib/head.h"//宣告引入head.h自訂標頭
#include <iostream>
using namespace std;
int main(/*int argc, char const *argv[]*/)
{
return 0;
}
告訴前處理器,此source code會用到名為iostream的headers
int main(void){}
或
int main(){}
//我是單行註解
/*我是多行註解
*(通常會加一個符號在前,其功能有二,一為對齊,二為告知在看的人此行為註解
*/
括號內為函式的主要工作內容,return為回傳的關鍵字,在main函式中的return值較為特別,如同前面所說的“作業系統呼叫的是main函數”,同理main函數亦是回傳給作業系統直為電腦系統的,代表的是程式的執行狀態,0表示程式順利結束,其他非0零的值則由電腦做定義,通常為錯誤碼
若為自訂函式,可回傳變數、字串、矩陣、運算式值或空值,一樣要記得宣告函式時要同時宣告回傳值型別
void hello(){
cout<<"hello world";
}
int returnMAX(int a1,int a2){
return (a1>a2)?a1:a2;
}
關鍵字keyword:為啟用相關功能、被定義於編譯器的字,可以解釋為“當這些字被打出來時,會有些預先定義的功能被發動”,根據微軟developer docs寫道“關鍵字是具有特別意義的預先定義保留識別項,他們無法當作程式中的識別字做使用”。
識別字identifier:依程式需求自行定義的名稱,舉凡程式中所用的各種名稱都屬於識別字,標準程式庫、第三方程式庫中亦是。
最新資訊
由於c++可以說是由c語言「改良」後的新語言,故c語言的函式庫c++皆可使用 (base on C.)
cout物件,定義於iostream中,其可以將字串或數字輸出到標準輸出裝置上,和C語言中printf()相同,但使用法不同
cout物件可以接收由*串接運算子<<*所組成的字串,這些字串會依序組合成為一個更長的字串,再藉由cout物件輸出至螢幕上。
//語法
cout<<變數1或字串<<變數2或字串<<...<<變數n或字串;
//example
cout<<"hello world";
//(同義)
cout<<"hello"<<" "<<"world";
//亦可與變數同時輸出
cout<<"After calculation, your BMI is"<<BMI;
cout<<........<<endl;
相較於cout的輸出,cin則是用來從鍵盤中輸入各種資料,利用資料流截取運算子>>,即可讀取自鍵盤的輸入,供執行中的程式使用
cin>>[變數1](>>[變數2]>>...>>[變數n]);
printf( const char* format, ... );
參數 | 用途 |
---|---|
% | 格式化符 |
%% | 輸出%符號 |
%d | 整數int |
%f | 浮點數float |
%.nf | n為一數字,僅輸出到該位數 |
%s | 字串(可省) |
Example:
printf("abc");//輸出abc
printf("%d",10);//輸出10
printf("%d",a);//輸出(整數)變數a值
printf("%f",0.1234)//輸出0.123400(預設輸出6位)
printf("%.2f",0.1234)//輸出0.12
//多重參數
printf("今天的日期是:%d年%d月%d日,台幣兌美元匯率為%f\n",year, mounth, day, exchange);
more info: https://en.cppreference.com/w/cpp/io/c/fprintf
看完上面的解說,我們應該已經成功在電腦上輸出資料,但有兩個問題:我們要如何輸入東西到電腦?還有上面一直提到的「變數」是什麼呢??
變數在電腦中的定義就和他字面上的意義相同,為一可變的數,若結合計算機硬體的概念來看,即是將待用資料存入於記憶體的其一位址中,待需要使用時再將其呼叫此位址然後將值載入暫存器中,但因為位址很難記,打程式時Coders會利用一個標示符來代替他,以提高效率及程式碼的理解性。
我們已知,電腦的資源是有限的,故我們要依資料的不同類去分配不同型態的變數來儲存,例如人數一定是整數,重量可能帶有小數位,因此用浮點數較為適合 C和C++一樣皆有下列這些變數型態(型態最大的不同在於在記憶體內的空間):
可以把變數當成是一個一個不同大小的盒子,可以裝入不同大小的東西,但東西的大小一定要比盒子小,雖然可以硬塞,但會讓裡面的東西壞掉(overflow溢位)
宣告的概念即是向作業系統索取盒子的過程,必須明確告知作業系統這個盒子的大小,是整數、浮點數、或字元值
接著,在作業系統給予我們這個盒子時,作業系統也會在這個盒子上坐上一個標籤,稱為記憶體位址,是一個獨一無二的id,然而因為現在的記憶體都是動輒好幾GB的,他的位址也是0x123…不易讓人記憶,因此,高階語言也允許我們自訂這個盒子的名稱,使我們方便編程(記憶體位置仍然不變,只是多了一個“給人看”的標籤而已)
資料型態 | 型態說明 | 位元數 | 數值有效範圍 |
---|---|---|---|
整數類型 | |||
long long | 長長整數 | 8 | -9,223,372,036,854,775,808 至 9,223,372,036,854,775,807 |
long int | 長整數 | 4 | -2147483648~2147483647 |
int | 整數 | 4 | -2147483648~2147483647 |
short int | 短整數 | 2 | -32768~32767 |
unsigned long long | 無號長長整數 | 8 | 0 到 18,446,744,073,709,551,615 |
unsigned long int | 無號長整數 | 4 | 0~4292967295 |
unsigned int | 無號整數 | 4 | 0~4292967295 |
unsigned short int | 無號短整數 | 2 | 0~65535 |
浮點數類型 | |||
float | (單精度)浮點數 | 4 | 1.2e-38~3.4e38 |
double | (雙精度)浮點數 | 8 | 2.2e-308~1.8e308 |
字元類型 | |||
char | 字元 | 1 | ASCII CODE |
(C++11)char16_t | UTF-16 | 16 | unicode 編碼 |
(C++11)char32_t | UTF-32 | 32 | unicode 編碼 |
布林類型 | |||
bool | 布林值 | 1 | true, false |
C++11 標準新增char16_t, char32_t來表示UTF-16, UTF-32字元, 亦新增long long的整數型態
//只宣告型別,代賦值
type name1,[name2,name3,...];
int a,b,c;
//型別、值同時宣告
type name=number;
int a=1,b=2;
//亦可混合,部分賦值、部份不賦值
int a,b=33,c;
變數,即為一可變的數,恰與亙久不變的常數相反,然而不管變數變了多少次,其在記憶體內的佔用空間不會改變(,故資料精度大於記憶體分配空間則會造成資料數值錯誤的溢位overflow現象);宣告的概念,會指定實體的唯一名稱,以及其型別和其他特性的相關資訊。
即程式中直接寫出來的數值value
宣告(declare)布林型態的變數使用關鍵字(keyword) bool,其字面值有true和false,分別代表1,0,邏輯上的真與假
一般而言,最常用的是int型態,沒有特別指定的整數字面常數皆會被視為int型態,欲使用long型態,則需在字面常數的尾巴加上l或L
int a=1;
long b=1234l;
最常使用的是double,沒有特別指定的浮點數字面常數會被視為double,欲使用float型態,需在字面常數尾加上f或F
double a=2458.3721;
float p=22.0f;
宣告字元型態的變數使用關鍵字char,字元型態的字面常數為單引號圍起來的單一字元,或是單引號圍起來反斜線加上四位的十六位元數字
char e='a';
char f='2';
char i='\n';
char k='\a';
char變數的字面常數是「文字」 沒錯,但事實上它是依據ASCII表將文字轉為數字來進行儲存的,每個字母有其專屬的數字代碼,大小有不同,故其亦有有效範圍且一樣是由數字決定 可參閱ASCII表\
在上面程式碼區塊的3,4行可以看到由反斜線\加上一個英文字母的組合文字,其稱作跳脫字元,即一些不可見字元(換行、對其、雙引號(因已被定義))
跳脫序列 | 代表意義 | ASCII CODE(dex) |
---|---|---|
\a | 警告音 | 7 |
\b | 倒退一格 | 8 |
\n | 換行 | 10 |
\r | 歸位 | 13 |
\t | 跳格 | 9 |
\0 | 字串結束字元 | 0 |
\|反斜線 | 92 | |
\' | 單引號 | 39 |
\" | 雙引號 | 34 |
變數若不是基本內建型態則代表他是個物件,看得懂的話可以先看指標與參考
C++中的每一行,都為一陳述,而每行陳述,可以運算式組成,並以分號作為運算式的結尾
優先順序 | 運算子 | 功能 | 用法 |
---|---|---|---|
1 | + | 單元的加法 | + expr |
1 | - | 單元的減法 | - expr |
2 | * | 乘法 | expr * expr |
2 | / | 除法 | expr / expr |
2 | % | 取餘數 | expr % expr |
3 | + | 加法 | expr + expr |
3 | - | 減法 | expr - expr |
Image Not Showing
Possible Reasons
|
包括加減乘除取餘數,分別符號為+-*/%,使用概念和數學的概念一模模一樣樣,唯有一個要注意的就是除法時要注意捨去的問題:
int a=1,b=3;
cout<<a/b;//輸出值應該要是0.333
實際結果:,然而浮點數除法就不會有此問題,因此要將原函式改為:
float a=1,b=3;
cout<<a/b;//輸出值應該要是0.333
//或
int a=1,b=3
cout<<(float)a/b;
第二種寫法稱為型別的強制轉換,原本a是整數,但在進行運算時加上了(float)的前綴使其暫時轉為浮點數進行運算,故其小數值不會被捨掉。
大家可以試試看此段程式碼,看結果有何不同:
int a=1,b=3;
cout<<float(a/b);
結果
答案應該也是0對吧 原因是因為在電腦中的運算,和數學中的運算一樣也有先後順序 在這邊的步驟是:先算出1/3,但因為是整數除法得值為0,再將0轉為浮點數,得值依舊為0
種類 | 使用法 | 例子 |
---|---|---|
遞增運算子++(先遞增再運算) | ++[變數] | 如++a |
遞增運算子++(先運算再遞增) | [變數]++ | 如a++ |
遞減運算子–(先遞減再運算) | –[變數] | 如–a |
遞減運算子–(先運算再遞減) | [變數]– | 如a– |
判斷是「先運算再進行遞增減」還是「先遞增減再運算」的方法,是看他們在變數的哪個位置上,在前面就是先遞增減,在後面就是後遞增減 yen0224
遞增減運算子的優先順序高於算數運算子,可以思考一下下列程式碼輸出的值為多少:
b = 10 ;
cout << 10 * (++b) ;
110,到cout那行時,b會先+1,再進行運算。
再思考一下如果對字元變數進行遞增會輸出什麼呢:
若對字元變數進行遞增並且輸出,可以輸出全部的字母
優先性 | 結合性 | 運算子 | 功能 | 用法 |
---|---|---|---|---|
1 | R | ! | 邏輯NOT | !expr |
2 | L | > | 大於 | expr > expr |
2 | L | < | 小於 | expr < expr |
2 | L | >= | 大於等於 | expr >= expr |
2 | L | <= | 小於等於 | expr <= expr |
3 | L | == | 相等 | expr == expr |
3 | L | != | 不等 | expr != expr |
4 | L | && | 邏輯AND | expr && expr |
5 | L | || | 邏輯OR | expr || expr |
即為數學「且」與「或」的概念,只有在兩個運算元皆為true時,AND運算的結果才會是true,若是or的話,任一運算元為true時即為true。
AND與OR永遠會進行短路運算(short-circuit evaluation),即右運算元只在左運算元無法決定結果時才會進行運算:
邏輯運算子NOT回傳其運算真假值的相反,即expr的結果為true,!expr的結果為false
關係運算子的概念很簡單、和數學中的概念是相同的,唯二要注意的是相等是==,=為指派運算子,另外若要有連續判斷大小,如i<j<k的情況,需借助邏輯運算子來寫判斷式,否則結果會出錯。
if( i<j && j<k )
原因:若k大於1,因關係運算子為左結合的,k會跟i<j的結果進行判斷,即使k並無大於J,其與布林值的比較會是恆成立的(true=1, false=0)
指派運算子是右結合的,其左運算元需為一個可改變的lvalue;
複合指派,即是把“一個運算子套用在一個物件上,然後結果指派在相同的物件”這個動作合一
以下為用法:
運算子 | 原本 | 複合指派 |
---|---|---|
+= | a=a+1 | a+=1 |
-= | b=b-a | b-=a |
*= | c=c*a | c*=a |
/= | d=d/1000 | d/=1000 |
%= | e=e%3 | e%=3 |
cond ? exp1 : exp2 ;
cond是用來表示條件的運算式,而exp1和exp2為相同型別的運算,其執行方式為:當cond為true時,exp1會被執行,若否,exp2執行
int score = 60 ;
cout << ((score > 60 ? ) pass : fail);
//result :fail
優先序 | 運算子 | 功能 | 用法 |
---|---|---|---|
1 | ~ | 位元NOT | ~expr |
2 | << | 左移位 | expr1 << expr2 |
2 | >> | 右移位 | expr1 >> expr2 |
3 | & | 位元AND | expr1 & expr2 |
4 | ^ | 位元XOR | expr1 ^ expr2 |
5 | | | 位元OR | expr1 | expr2 |
建議將位元運算子只用於unsigned型別,才不會使正負號錯誤
NOT運算:將每個bit反轉
AND,XOR,OR:將expr1與expr2進行邏輯運算
回傳型別所佔大小
int a;
sizeof a;
程式的結構主要三種,其共同的特徵是,只有一個進入點,也只有一個出口
最簡單的條件判斷式:一個if加上一個運算式,若運算式的結果為非零,則進行if括號內的述句,若為零,則進行else的內容,若無else則不進行任何動作; 在多個條件運算式中,則以else if關鍵字進行第二條件的判斷,else if可有可無,務必注意的是,連續多個條件的判斷,應將最小範圍的條件排在最前,否則可能會有淺在的bug產生。
巢狀if:if中又有if
if(/*condiotion1*/){
statement1;
}
else if(/*condition2*/){
statement2;
}
~
~
else if(/*conditionN*/){
statementN;
}
else{
statementN_1;
}
for example #1 閏年的判斷:
已知判斷閏年的條件有:
#include <iostream>
using namespace std;
int main(){
int year;
cin >> year;
if ((year % 4) == 0){
if ((year % 100) == 0){
if ((year % 400) == 0){
cout << year << "年是閏年(400倍數)" << endl ;
}
else{
cout << year << "年非閏年(100年)" << endl ;
}
}
else{
cout << year << "年是閏年(非100倍數4倍數)" << endl ;
}
}
else{
cout << year << "年非閏年(非4倍數)" << endl ;
}
return 0;
}
可觀察一下4, 100, 400出現位置、及改變他們的位置可能會有什麼影響;若硬要改變位置,相對應的寫法要怎麼寫呢?為何要用巢狀,不用多個if和邏輯運算式呢??
Q1:4,100,400出現位置:
Q2:通靈能力已全部耗盡,我猜不到你想啥,略
Q3:使用多個if和邏輯條件組合呢?
switch(variables){
case const1:
states;
break;
case const2:
states;
break;
default:
states;
}
while(/*condition*/){
statement;
}
do{
statement;
}while(/*condition*/)
for ( init-statement ; condition ; iteration_expression ){
statements;
}
for迴圈由三部分組成:
由上面三個部分,可以得知for loop 等效於:
{
init_statement
while ( condition ) {
statement;
iteration_expression ;
}
}
對於for語法還不熟悉的可以先嘗試用while去寫,尤其是做有計數器的題目可以更快掌握for的用法yen020224
break敘述可以讓程式強迫跳離回圈,當程式執行到break敘述時,即會離開回圈,繼續執行回圈之外的下一行敘述(若違巢狀迴圈,則跳至此回圈外之下行敘述)
for(;;;){
staA;
staB;
staC;
...
break;
staN;
}
statements;//break之後會執行這行
continue敘述,
陣列array是由一群相同型態的變數所組成的變數型態,他們以一個共同的名稱表示,陣列中的個別元素(element)則以註標(index)來標示其存放的位置;一陣列的複雜程度可分為一維、多維陣列
宣告法1:純宣告
type name[amount];
//[資料型態] 陣列名稱[元素數量];
//e.g.
int score[6]; //宣告一個陣列名為score,內有6個元素
float temp[7]; //宣告一個陣列名為temp,內有7個元素
char name[12]; //宣告一個陣列名為name,內有12個元素
宣告法2:陣列初值設定
//[資料型態] 陣列名稱[n]={初值0,1,2,.....,n-1};
//注意
//注意
//注意
//註標只有到n-1,因為是從0開始的
type name[n]={0,1,2,3,...,n-1}
//e.g
int monthDay[12]={31,28,31,30,31,30,31,31,30,31,30,31};
//也可這樣寫
int day[]={31,28,31,30,31,30,31,31,30,31,30,31};
編譯器錯誤訊息
若所宣告大小與實際情形不符:
宣告數>輸入數:其餘以0補齊
宣告數<輸入數:出現錯誤訊息**"太多初始設定式值"**
(以macos為例:)
若是要讓使用者輸入值可以怎麼寫?
ANS:用for迴圈
#include <iostream>
using std::cin;
using std::cout;
using std::endl;
int main(void){
int amount;
cout << "要輸入幾個數?" << endl ;
int inputNum[amount];
for(int i=0;i<amount;i++){
// ^注意初值,以及條件
cout << "請輸入第" << i+1 <<"個數:";
cin >> inputNum[i];
// ^
}
return 0;
}
## 函數
指標pointer儲存變數的記憶體位址address,參考reference為變數的別名alias。
宣告指標變數使用*運算子,範例如下
#include <iostream>
using namespace std;
int main(void){
//設值n 為整數11
int n = 11;
cout << n << endl;
//設置np為指向n的指標,&為取址運算子
int *np = &n;
cout << np << endl;
//設定t取得從np位址指向的值,*為反參考運算子
int t = *np;
cout<< t << endl;
return 0;
}
設置完n的值為11後,
int *np = &n ;
cout<< np << endl;
用np取得n的記憶體位址,*np中的*用來宣告指標,&運算子可取得變數之位址。這段程式碼的完整解釋為:*宣告np變數為指標形式後,由取址運算子&取得n之位址,儲存於np中。 前面的int 為指標指向的變數位址所對應的變數的型態,在這邊,即是指n的型態,所以是int,若n為float,則程式為:
float n = 11.0f;
float *np = &n;
int t = *np;
cout << t << endl;
同樣是*,但這次出現在等號的右邊,則變成了反參考運算子,由t取得np所儲存記憶體位址變數的值;這裡的np儲存的為n的記憶體位址,所以t藉由*np反參考取得n之值,(#2行)輸出11
好麻煩,指標的概念好複雜,一樣是移動變數,為何要分那麼多步驟還有位址移來移去,直接複製變數不就好啦??
答案是:效率的考量 當今天有100個變數要複製等處理,每個變數所佔空間是好幾個Byte,所以移動100個就是等於要對好幾百個Byte作處理,相較之下,一個記憶體位址只有一個位址的空間,相較之下,效率不就更好了?
若是對於更複雜的資料結構,指標的優點就更顯而易見了。
bug有兩種,其中一個幾乎可以依靠編譯器的錯誤碼去判斷,另一種則需去檢視程式碼去找出錯誤
->建議解法:小鴨除錯法(Rubber Duck Debugging),找一個人或物,向他一行一行解釋程式碼的功用與執行過程,藉此找到矛盾及激發靈感(但因為電腦工程師很多都是邊緣人所以只能跟小鴨講 (;´༎ຶД༎ຶ`))