# C/C++基礎 # 零---補充 ## 一、基本運算子 1. 邏輯 運算符號: \+ \- \* \/ %(取餘數 * 優化c++ * `ios::sync_with_stdio(false), cin.tie(nullptr);` * 前者解除同步;後者輸出終端改成全輸入後再一次輸出。 * 解除同步後小心處理輸入。 | 運算子 | 意義 | | ------ | ------ | | && | AND,且 | | \|\| | OR,或 | | ! | NOT,否 | ## 二、其他常用的運算子 1. 增遞/減遞運算 | 運算子 | 意義 | |:------:|:----------:| | ++ | 變數值加 1 | | - - | 變數值減 1 | * i++:先執行整個敘述後, 再將 i 的值加 1 * ++i:先將 i 的值加 1, 再執行整個敘述 * 特別的在for迴圈中,第三區塊是等所有程式執行完才進行。 因此i++跟 ++i沒有太多差別(無運算式) 差在是否複製舊副本與回傳值,於int影響甚小。 2. | 運算子(?:) | 意義 | | -------- | -------- | | 條件判斷 ? 運算式 1 : 運算式 2|| ``` if(判斷條件) 運算式 1; else 運算式 2; ``` | 實例1 | 實例2 | | ---------------------- | ----- | | a = (x > 100) ? b : c; | abs = (a > 0) ? a : -a; | ``` if (a > 0) abs = a; else abs = -a; ``` --- # 壹、格式化的輸入與輸出 ``` #include <stdio.h>#將檔案<stdio.h>匯入進來 int main(){ printf("Hello world!"); return 0; } ``` [https://web.fg.tp.edu.tw/~earth/vision/study/cprogram06/C_handout.pdf](https://) ## 一、標準輸出指令 `printf("格式字串", N1, N2,...);` `scanf("格式字串",&N1,&N2,...);` ### Printf ``` int main(){ int a = 1 ; printf("Hello \n%d", a); return 0; } ``` ### Scanf ``` int main(){ int a,b; scanf("%d%d",&a,&b); printf("%d,%d",a,b); } ``` * 注意!如果要讀取字元陣列時,`str`它被當作指向字串的指標使用,所以在 scanf 函式中,我們直接使用陣列名稱 str 來讀取字串。因為 str 已經是一個指向陣列的指標,使用 & 運算子會導致類型不匹配 > 字串為字元的陣列 * 各式字串如需要引入一個參數需加上%後面再加上資料型態,如%d、%s,此為修飾子 ## 二、跳脫字元(Escape Sequence) | \n 換行 | \\" 雙引號 | \\' 單引號 | | | --------------------- | -------------------- | ---------- | ------- | | \f 換頁 | \t 跳格 | \b 倒退 | | | \x ASCII 碼 (16 進位) | \d ASCII 碼 (8 進位) | \\\ 反斜線 | \\/ 斜線 | **例子 執行結果** ``` printf("\tThis line begins with tab.\n"); printf("It\'s a \"C Tutorial\".\n"); printf("This is backslash: \\.\n"); printf("\\101 is \101.\n"); printf("\\x41 is \x41.\n"); ``` > This line begins with tab. > It's a "C Tutorial". > This is backslash: \. > \101 is A. > \x41 is A. ## 三、修飾子 可用於引入參數 | -:向左靠齊 | +:印出正負號 | %c:字元 | | -------- | -------- | -------- | | %s:字串 %d:十進位整數 | %f:浮點數 (小數點型式) | %l:長整數,加在 d、u…之前 | | %u:無號十進位整數 | %e:浮點數 (指數 e 型式) | Nope | --- # 貳、流程控制 ## 一、if 指令 ### 巨集 > define name 函式 ``` #define MAX(a, b) (a>b ? a:b) int main(){ int x, y; printf("輸入N1:"); scanf("%d",&x); printf("輸入N2:"); scanf("%d",&y); printf("兩數最大值為:%d\n",MAX(x, y)); } ``` ``` if (條件判斷式) { 指令1; ...2; ...3; ..... } ``` ``` ``` ### 單一條件不用加{} ``` if (條件判斷) 指令1 ; ``` ### ALL ``` int main(){ int a; printf("輸入成績:"); scanf("%d",&a); if (a>=60 && a<=100) printf("及格\n"); else if (a>100) printf("error!\n"); else printf("不及格\n"); } ``` ## 二、switch指令 ### case、default --- ![](https://hackmd.io/_uploads/HJSMQ-39h.png) --- 範例題: > 依據下列表格中的等級表,使用switch完成分數的判斷,且在使用者輸入成績後顯示出等級 | 等第 | 分數 | |:--------:|:--------:| | A | 90~100 | | B | 80~90 | | C | 70~79 | | D | 60~69 | | E | 0~59 | ``` switch (運算式) { case 選擇值 1: 敘述主體 1; break; case 選擇值 2: 敘述主體 2; break; … case 選擇值 n: 敘述主體 n; break; default: 敘述主體; } ``` Answer: ``` int main(){ int a; printf("輸入成績:"); scanf("%d",&a); switch ( a/10 ){ case 10: printf("A"); break; case 9: printf("A"); break; case 8: printf("B"); break; case 7: printf("C"); break; case 6: printf("D"); break; case 0 ... 5: /* gcc擴充套件允許對比特定範圍 (0~5)*\ printf("E"); break; } } ``` ### for 迴圈 ``` for(變數起始1;條件;變數增減){ 程式1; for(變數起始2;條件;變數增減){ 程式2; } } ``` ### while ``` while(條件判斷){ 程式區; } ``` ### do-while (後測試型迴圈) > do裡的程式碼會被先執行一次 ``` do{ : 程式區; } while(條件判斷); ``` ### break、continue * break跳脫迴圈不再執行下列程式 * continue持續執行迴圈並跳過下列程式 # 參、陣列、字串、矩陣、結構與檔案 ## 一、陣列 ### 一維 * 元素個數可未設定(自動) * 超過個數長度時將自動設定為0 ``` int arr[] = { [0] = 3, [1] = 4, [2] = 5, [3] = 6, [4] = 7, }; ``` ``` 資料型態 陣列名稱 [長度]; ``` 或者 ``` 資料型態 陣列名稱 [大小]={初始值1,..2,..3,...}; ``` ### 二維 * 若初始值個數少於設定長度其餘將設定為0 * c/c++以列為主軸 | x | 行[0] | 行[1] | 行[2] | |:-----:|:----:|:----:|:----:| | 列[0] | [0][0] | [0][1] | [0][2] | | 列[1] | [1][0] | [1][1] | [1][2] | ``` 資料型態 陣列名稱 [列個數] [行個數]; ``` ``` 資料型態 陣列名稱 [列個數] [行個數] ={{var1,var2,var3},{var4,var5,var6}}; ``` ## 二、結構 ### struct ``` struct 結構名稱 { 資料型態 成員1; 資料型態 成員2; ...... }s1,s2; ``` * 定義結構變數 `struct name s1,s2;` * 點運算子(用於存取) `結構變數.成員名稱;` * 成績&名子 ``` int main(){ struct rank { char name[10]; int score; } s1,s2 ; strcpy(s1.name,"KATE"); s1.score = 900; s2 = s1; printf("%s%d",s2.name,s2.score); return 0; } ``` ### 巢狀struct ``` struct 結構名稱1 { 資料型態 成員1; 資料型態 成員2; ...... }; struct 結構名稱2 { ...... struct 結構名稱1 變數名稱; }; ``` ### 結構陣列 * 宣告 `struct 結構名稱 陣列名稱 [長度];` * 存取 `陣列名稱 [索引值].陣列成員名稱` ``` struct 結構名稱 { ...... 資料型態 陣列名稱 [個數]; }; ``` `struct 結構名稱 陣列名稱 [個數];` * 存取 `陣列名稱 [索引值].陣列成員名稱[索引值]` * 下圖為建立5個元素的結構陣列 ``` struct student { char name[10]; int score; }; struct student class1[5]; ``` # 容器 vector、map、queue、stack ## map vs unorder_map ### map 基礎建立於樹狀(紅黑樹)存取資料,遍歷時會自動排序好鍵(key),因此在查找與插入為O(log n)。 適合用在大於某個值得最小key等或是區間查詢。 * find() 找尋某個值,回傳該值。 * map->second 回傳鍵值。 * count() 回傳元素有幾個。 ### unorder_map 基礎建立於雜湊&&沒有順序之差。因此在查找與插入為O(1),但需要消耗較多記憶體空間。 適合使用大量查詢判斷是否有該值存在、以及鍵值為何。 Key 數量大則很適用使用。 * 使用at()如果不存在,會直接out of range; * 如果發生碰撞問題會退化O (n) * 不允許重複元素 * reserve可避免rehash問題,影響執行效率。 ### vector 動態的容器,每次push_back()、insert,都會重新分配記憶體。 * reserve()分配記憶體。 * assign(n,value),重構並指定數值。 `visited.assign(n+1, vector<bool>(m+1, false));`(二維靜態) `vector<vector<int>> v(n);`(二維動態) * push_back()放入尾端 * size()陣列元素的總數 * begin()指著第一個元素,end()指著最後元素的下一個 it++ 會保留舊副本; ++ it 則會直接下一個,在map等效率很重視++it * empty()回傳是否為空,true or false ```cpp! for(auto it = v.begin(); it!= v.end();++it){ cout<<*it<<' '; } ``` # 各種有用的 (auto、auto&、string...) ## auto、auto& auto用於複製一份資料。可iterate nums陣列。 ```cpp! nums[4]={0,1,2,3,4}; for(auto x:nums){ x=0; } //nums->0 1 2 3 4 ``` auto& 直接修改值本身 ```cpp! nums[4]={0,1,2,3,4}; for(auto &x:nums){ x=0; } //nums->0 0 0 0 0 ``` ## string `string +=`:添加字串 `string+`:字串相加 `at(i)`:存取索引值\[i\]的字元,存取越界會拋出一個例外 `find(char,start)`:字串搜尋,回傳字串首個位置。 `substr(n,m)`:取得子字串,從n開始往印出m個字母。 `empty()`:回傳是否為空,空則回傳true `size()`:回傳目前長度 `length()`:回傳目前長度 `append()`:添加字串 `size_t`:容器長度的一種類型 `string::npos`:表示容器類型的最大值(常數)與size_t最大值相等,通常用於判斷不存在的位置。 ```cpp! for (auto &c : str) { cout << c << '\n'; } } ``` 取得str的地址開始疊代。 * 如果使用運算式相加會複製一份,可以使用append or += 提升效率 ### stoi 將字串轉為數字 `stoi(string)` ### getline 讀取一行字串(吃空白) `getline(cin,string)` stringstream類別 `#include <sstream>` 可用於將字不斷拆分 ``` stringstream ss(s) while(getline(ss,string,'區分的字元')) ``` ## rotate 將陣列元素往後位移n個,中間begin使前方往後n個元素;反之end使後方往前n個元素。 `rotate(v.begin(),v.begin/end(),n)` ;`#inlcude<algorithm>` ## sort vs qsort函式 結論是c++ stl sort一定比c的qsort快 以下為使用方法 ### sort 一般陣列 : sort(a,b) 指從a位置排到b位置 ,大到小則在sort(a,b,cmp) cmp中加入 `greater<int>()` vector : sort(v.begin(),v.begin()+n) 也為相同道理,只是stl容器需要使用begin()+n來指定起終點。大到小同理`greater<int>()` 或是善用反向迭代 `sort(v.rbegin(),v.rend())` ```cpp! #include <iostream> #include <algorithm> #include<vector> using namespace std; int main(void) { int v[6]={2,7,3,1,4,5}; vector<int> v2={2,7,3,1,4,5}; sort(v,v+6); sort(v2.begin(),v2.end()); for(int i=0;i<6;i++) { cout<<v[i]<<" "; } cout<<"\n"; for(int i=0;i<6;i++) { cout<<v2[i]<<" "; } cout<<"\n"; } ``` struct : 多加一個比較函式,看要針對結構哪個排序。 a.score < b.score 回傳true 表示每個a<b,由小到大排,反之大到小。 ```cpp! #include <iostream> #include <algorithm> using namespace std; struct node { string name; int score; }s[3]; bool cmp(node a,node b) { return a.score < b.score; } int main(void) { s[0].name="Ken";s[0].score=70; s[1].name="Yellow";s[1].score=50; s[2].name="Green";s[2].score=60; for(auto st: s) { cout<<st.name<<" score:"<<st.score<<endl; } sort(s,s+3,cmp); for(auto st: s) { cout<<st.name<<" score:"<<st.score<<endl; } } ``` ### qsort 一般陣列: qsort(陣列,數量,記憶體形式,比較函式)。 指向int指向a,a-b為小到大, b-a為大到小。 ```cpp! #include <iostream> using namespace std; int cmp(const void* a,const void* b) { return (*(int *)a) - (*(int *)b); } int main(void) { int v[5]={3,4,2,1,5}; for(auto st: v) { cout<<st<<" "; } cout<<endl; qsort(v,5,sizeof(int),cmp); for(auto st: v) { cout<<st<<" "; } } ``` struct : cmp中由int位置改成 struct node(結構名稱)。意思為struct node 指向 a 指向score, a-b為小到大, b-a為大到小。 ```cpp! #include <iostream> #include <algorithm> #include<vector> using namespace std; struct node { string name; int score; }s[3]; int cmp(const void* a,const void* b) { return ((struct node *)a)->score - ((struct node *)b)->score; } int main(void) { s[0].name="Ken";s[0].score=70; s[1].name="Yellow";s[1].score=50; s[2].name="Green";s[2].score=60; for(auto st: s) { cout<<st.name<<" score:"<<st.score<<endl; } qsort(s,3,sizeof(struct node),cmp); for(auto st: s) { cout<<st.name<<" score:"<<st.score<<endl; } } ``` # 那些已經被complier優化掉的東西 1. `i++` vs `++i` 在組合語言中已經被調整為相同的東西, 速度差異微乎其微乃至無影響。 2. `n*2^x` vs `n<<x` 組合語言為轉為相同的東西,電腦是二進制當*2^n會與位元位移相同。 3. `a>b?a:b` vs `if(a>b) return a else b` 同理相同的東西