# C++ 基礎 [TOC] ## 1. 開發環境 ### 1.1 整合開發環境(IDE) - Dev-C++ - Code::Blocks - Visual Studio Code - Xcode (MacOS) ### 1.2 輕量級選擇:文字編輯器 + 編譯器 #### 文字編輯器 - Sublime Text - Vi/Vim - Emacs - Nano #### 編譯器 Clang **Clang** 是一個基於 LLVM 架構的開源編譯器,支援 C、C++、Objective-C 等語言。Clang 以其高效的編譯速度、清晰易懂的錯誤與警告訊息,以及模組化設計而聞名,常用於開發者工具中。它和 GCC 一樣廣泛支援多平台,但比起 GCC 更加靈活,尤其在編譯大型專案時表現優秀。Clang 也能與 LLVM 工具鏈一起使用,提供高效的優化和靜態分析,並且經常被用作 macOS 和 iOS 開發的默認編譯器。 #### 編譯器 GCC **GCC**(GNU Compiler Collection)是由 GNU 開發的開源編譯器集合,支援多種編程語言,包括 C、C++、Objective-C、Fortran、Ada 等。最初是為了編譯 C 語言而設計的,但隨著時間推移,擴展為多語言、多平台的強大工具。GCC 具有高效的優化功能,能夠生成性能優異的程式碼,因此在軟體開發、嵌入式系統等領域廣泛使用。 它的多平台特性使其能夠在多種作業系統(如 Windows、macOS、Linux)上使用,並且是 Linux 開發環境中的主要編譯器之一。 #### 技巧:下一個指令同時編譯和執行(使用 Shell) 用文字編輯器,將下列 Shell 函式加入你的 Shell 設定檔(`~/.bashrc` 或 `~/.zshrc`) ```bash= cr() { # Check if a filename is provided if [ -z "$1" ]; then echo "Please provide a file name to compile" return 1 fi # Compile and generate the executable clang++ -std=c++11 "$1.cpp" -o "$1" # If the compilation succeeds, execute the file if [ $? -eq 0 ]; then echo "Executing $1..." ./"$1" else echo "Compilation failed" fi } ``` 加入之後,重新載入 Shell 設定檔 ```bash source ~/.bashrc # 如果用 zsh,則執行 source ~/.zshrc ``` ### 1.3 線上開發環境 - [Programiz](https://www.programiz.com/cpp-programming/online-compiler/) - [W3School](https://www.w3schools.com/cpp/trycpp.asp?filename=demo_compiler) ![W3School](https://hackmd.io/_uploads/HkqO8BJekl.png) ## 2. 編譯語言概念 C++ 程式的開發過程可以分為六個主要階段,每個階段都有其獨特的作用,從撰寫原始碼到最終執行程式。以下是這六個階段的簡介: #### 1. **編輯(Editing)** 編輯階段是程式開發的第一步,開發者在文字編輯器或 IDE 中撰寫 C++ 程式碼,並將其儲存為 `.cpp` 或 `.h` 檔案。這些原始碼包含了程式的邏輯、變數、函式等部分。 #### 2. **前置處理(Preprocessing)** 在前置處理階段,編譯器會處理 C++ 原始碼中的指令,例如 `#include`、`#define` 等指令。這些指令由**前置處理器**進行處理,將外部的標頭檔案包含進來,替換巨集定義,並且處理條件式編譯等。在這個階段,程式還未進行語法分析或轉換成機器碼,只是進行簡單的文本處理。 輸出文件為預處理後的代碼,通常使用 `.i` 或 `.ii` 擴展名。 #### 3. **編譯(Compiling)** 編譯階段將前置處理過的 C++ 原始碼轉換成**中間表示**,再進一步將其轉換成**組合語言**(Assembly Code)。這個階段主要進行語法分析、語義分析,確保程式語法正確無誤。編譯的輸出結果是組合語言檔案,通常是 `.s` 檔案。 #### 4. **組譯(Assembling)** 組譯階段會將生成的組合語言轉換為**目標碼**(Object Code),目標碼是一種機器碼,但它尚未被完整連結,不能單獨執行。生成的目標碼通常以 `.o`(Linux/macOS)或 `.obj`(Windows)為副檔名。 #### 5. **連結(Linking)** 連結階段會將多個目標碼檔案以及需要的函式庫(例如標準 C++ 函式庫)進行**整合**,並解決符號之間的相互引用,例如函式和變數的定義和呼叫。連結器會生成最終的**可執行檔案**。如果有外部函式庫沒有正確連結,會在這個階段報錯。 輸出的可執行文件在 Windows 上通常為 `.exe`,在 Unix 系統上為沒有副檔名的檔案。 #### 6. **載入(Loading)** 當執行程式時,操作系統會將可執行檔案載入到**記憶體**中。這個過程稱為載入,操作系統負責分配記憶體並設置程式執行所需的環境,包括載入程式依賴的動態函式庫(例如 `.dll` 或 `.so` 檔案)。 #### 7. **執行(Execution)** 最後,在執行階段,程式會從進入點(通常是 `main()` 函式)開始運行。程式會按照程式碼的邏輯執行,並進行 I/O 操作、計算,直到結束執行。執行過程中,可能會遇到例外狀況或錯誤,這些錯誤會導致程式中斷或異常退出。 這六個階段構成了 C++ 程式從撰寫到執行的完整流程,理解這些階段有助於開發者有效地進行程式除錯和效能優化。 ## 3. 程式範例 1 - Hello world ```c++= #include <iostream> int main() { std::cout << "Hello world!\n"; // Print "Hello world!" return 0; } ``` - `#include <iostream>` 前置處理指令 (preprocessor directive) - `<iostream>` 輸入輸出串流標頭檔 (input/output stream header file) - `<<` 串流插入運算子 (stream insertion operator) - [C++ Outputs](https://www.w3schools.com/cpp/cpp_output.asp) - [C++ Comments](https://www.w3schools.com/cpp/cpp_comments.asp) ## 4. 程式範例 2 - 整數加法 ```c++= #include <iostream> int main() { // 變數宣告 int number1; int number2; int sum; std::cout << "Enter first integer: "; // prompt std::cin >> number1; std::cout << "Enter second integer: "; // prompt std::cin >> number2; sum = number1 + number2; std::cout << "The sum is " << sum << std::endl; return 0; } ``` - `>>` 串流擷取運算子(stream extraction operator) - [C++ User Inputs](https://www.w3schools.com/cpp/cpp_user_input.asp) - [C++ Data Types](https://www.w3schools.com/cpp/cpp_data_types.asp) - [C++ Operators](https://www.w3schools.com/cpp/cpp_operators.asp) - 算術運算子 - 指派運算子 - 比較運算子 - 邏輯運算子 - 位元運算子 使用 `using namespace std;` 可以避免重複輸入 `std::` ```c++= #include <iostream> using namespace std; int main() { // 變數宣告 int number1; int number2; int sum; cout << "Enter first integer: "; // prompt cin >> number1; cout << "Enter second integer: "; // prompt cin >> number2; sum = number1 + number2; cout << "The sum is " << sum << endl; return 0; } ``` ## 5. 控制結構 在 C++ 中,控制結構用來改變程式的執行流程,常見的控制結構有 `if-else` 條件語句、`for` 和 `while` 迴圈等等。以下是各種控制結構的簡單介紹和範例: ### 5.1 **`if` 和 `else` 條件語句** `if-else` 讓程式根據條件來決定執行哪一段程式碼。 ```cpp #include <iostream> using namespace std; int main() { int x = 10; if (x > 5) { cout << "x is greater than 5" << endl; } else { cout << "x is not greater than 5" << endl; } return 0; } ``` - **`if`**:如果條件為真,執行 `{}` 中的程式碼。 - **`else`**:如果 `if` 條件不成立,則執行 `else` 中的程式碼。 ### 5.2 **`else if` 條件語句** `else if` 可以處理多個條件,讓程式根據不同情況執行不同程式碼。 ```cpp #include <iostream> using namespace std; int main() { int x = 10; if (x > 10) { cout << "x is greater than 10" << endl; } else if (x == 10) { cout << "x is equal to 10" << endl; } else { cout << "x is less than 10" << endl; } return 0; } ``` ### 5.3 **`for` 迴圈** `for` 迴圈用來重複執行一段程式碼固定次數。 ```cpp #include <iostream> using namespace std; int main() { for (int i = 0; i < 5; i++) { cout << "Iteration " << i << endl; } return 0; } ``` - 初始化 (`int i = 0`):在迴圈開始前執行一次。 - 條件檢查 (`i < 5`):每次執行迴圈前都會檢查,如果為真則執行迴圈。 - 增量 (`i++`):每次迴圈執行後增加 `i`。 ### 5.4 **`while` 迴圈** `while` 迴圈會根據條件持續執行,直到條件為假。 ```cpp #include <iostream> using namespace std; int main() { int i = 0; while (i < 5) { cout << "Iteration " << i << endl; i++; // 別忘記增加變數 i } return 0; } ``` - **`while`**:只要條件為真,迴圈就會持續執行。 ### 5.5 **`do-while` 迴圈** `do-while` 迴圈至少會執行一次,然後檢查條件來決定是否繼續執行。 ```cpp #include <iostream> using namespace std; int main() { int i = 0; do { cout << "Iteration " << i << endl; i++; } while (i < 5); return 0; } ``` - **`do-while`**:先執行 `do` 內的程式碼,再檢查 `while` 條件。 ### 5.6 `switch` 結構 在 C++ 中,`switch` 是一種多重選擇控制結構,可以根據變數的值執行對應的程式碼塊。`switch` 常用來替代多個 `if-else if` 條件,當你的程式需要根據某一個變數的不同值來做不同的事情時,`switch` 可以讓程式碼更清晰和簡潔。 #### 基本語法: ```cpp switch (expression) { case value1: // 當 expression 等於 value1 時執行這裡的程式碼 break; case value2: // 當 expression 等於 value2 時執行這裡的程式碼 break; // 可以有多個 case default: // 如果 expression 不符合任何 case,則執行這裡的程式碼 } ``` #### 注意事項 - `expression` 通常是一個整數或字元變數,不能是浮點數。 - `case` 表示對應的值,當 `expression` 的值和 `case` 的值匹配時,程式會執行對應的程式碼。 - `break` 用來結束 `switch`,否則程式會繼續執行下面的 `case`(稱為「貫穿」)。 - `default` 是可選的,當沒有任何 `case` 匹配時,執行 `default` 區塊內的程式碼。 #### 範例 ```cpp #include <iostream> using namespace std; int main() { int day = 3; switch (day) { case 1: cout << "Monday" << endl; break; case 2: cout << "Tuesday" << endl; break; case 3: cout << "Wednesday" << endl; break; case 4: cout << "Thursday" << endl; break; case 5: cout << "Friday" << endl; break; default: cout << "Invalid day" << endl; } return 0; } ``` #### 說明 - 當 `day` 等於 `3` 時,程式會匹配 `case 3`,然後印出 "Wednesday"。 - `break` 是必要的,否則如果沒有 `break`,程式會繼續執行下一個 `case` 的程式碼。 #### 貫穿現象 如果沒有使用 `break`,`switch` 會出現貫穿現象,即使條件已經滿足,仍會繼續執行後續的 `case`。 ```cpp #include <iostream> using namespace std; int main() { int day = 2; switch (day) { case 1: cout << "Monday" << endl; case 2: cout << "Tuesday" << endl; case 3: cout << "Wednesday" << endl; default: cout << "Invalid day" << endl; } return 0; } ``` 這個程式會印出: ``` Tuesday Wednesday Invalid day ``` 原因是 `switch` 在執行 `case 2` 後,沒有 `break`,因此繼續執行 `case 3` 和 `default` 區塊。 #### 總結: - `switch` 提供了一種更清晰的方式來處理多重選擇情境,適用於值較固定的情況。 - 別忘了使用 `break` 來避免貫穿。 - `default` 區塊用來處理不符合任何 `case` 的情況。 這就是 C++ 中 `switch` 語句的基本使用方式。 ## 程式練習 1 - 九九乘法表 ```c++= #include <iostream> using namespace std; int main() { for (int i = 1; i < 10; i++) { for (int j = 1; j < 10; j++) { cout << i << '*' << j << '=' << i * j << endl; } } return 0; } ``` ## 程式練習 2 - 根據等第判斷成績範圍 ```c++= #include <iostream> using namespace std; int main() { char level; cout << "Enter your grade level: "; cin >> level; switch (level) { case 'A': cout << "Your grade is between 90 and 100"; break; case 'B': cout << "Your grade is between 80 and 90"; break; case 'C': cout << "Your grade is between 70 and 80"; break; case 'D': cout << "Your grade is between 60 and 70"; break; case 'F': cout << "Your grade is below 60"; break; default: cout << "Invalid grade level!"; break; } return 0; } ``` ## 程式練習 3 - 閏年判斷 如果年份「可被400整除」,或者「可被4整除,但不被100整除」,就是閏年;否則是平年。 ```c++= #include <iostream> using namespace std; int main() { int year; cout << "Enter a year: "; cin >> year; if ((year % 400 == 0 ) || (year % 4 == 0 && year % 100 != 0)) { cout << "It's a leap year." << endl; } else { cout << "It's a normal year." << endl; } return 0; } ``` ## 附錄 A. 補充資料 ### 命名空間 在 C++ 中,**命名空間**(namespace)是一個用來組織程式碼的方式,避免名稱衝突。特別是在大型專案或有多個函式庫時,不同的程式碼可能會有相同的函式或變數名稱,這時可以使用命名空間來區分它們。 C++ 標準函式庫的 `std` 就是一個命名空間。你可以透過 `std::` 來存取標準庫中的函式和物件,例如 `std::cin` 和 `std::cout`。 #### 範例說明: ```cpp #include <iostream> int main() { int age; std::cout << "Enter your age: "; // 使用 std 命名空間中的 cout std::cin >> age; // 使用 std 命名空間中的 cin std::cout << "You are " << age << " years old." << std::endl; return 0; } ``` 在這個範例中,`std::cout` 和 `std::cin` 是分別位於 `std` 命名空間內的輸出和輸入函式。若不想每次使用 `std::`,你可以引入命名空間: ```cpp #include <iostream> using namespace std; // 引入命名空間 int main() { int age; cout << "Enter your age: "; // 不需要 std:: cin >> age; cout << "You are " << age << " years old." << endl; return 0; } ``` #### 為什麼需要 namespace? 當程式越來越大或引用多個函式庫時,可能會出現相同名稱的函式或變數。命名空間可以幫助將它們隔離,防止名稱衝突。例如: ```cpp #include <iostream> namespace A { void display() { std::cout << "Namespace A" << std::endl; } } namespace B { void display() { std::cout << "Namespace B" << std::endl; } } int main() { A::display(); // 呼叫 A 命名空間的 display B::display(); // 呼叫 B 命名空間的 display return 0; } ``` 在這個範例中,兩個命名空間 `A` 和 `B` 都有一個 `display` 函式,使用命名空間來區分它們,以避免函式名稱衝突。 ## 附錄 B. 學習資源 以下是一些優質的 C++ 學習資源,適合從初學到進階不同階段的學習者: ### 線上教學網站 1. **[Cplusplus.com](http://www.cplusplus.com/doc/tutorial/)** - 提供詳細的 C++ 教程,從基礎語法到 STL(標準模板庫)的使用,適合初學者和中級學習者。 2. **[LearnCPP](https://www.learncpp.com/)** - 這個網站涵蓋了 C++ 的所有核心概念,從簡單的語法介紹到進階的物件導向編程、指標、範本等,適合循序漸進地學習。 3. **[Codecademy C++](https://www.codecademy.com/learn/learn-c-plus-plus)** - 互動式教學平台,通過實時練習來學習 C++,對於新手非常友好。 4. **[GeeksforGeeks C++](https://www.geeksforgeeks.org/c-plus-plus/)** - 涵蓋廣泛的 C++ 主題,從基礎知識到演算法和資料結構,並且有大量的範例程式碼和問題解答。 5. **[Udemy C++ Courses](https://www.udemy.com/topic/c-plus-plus/)** - Udemy 提供許多不同層次的 C++ 課程,包括基礎入門和高級專題,可以根據自己的需求選擇適合的課程。 ### 書籍 1. **《[C++ Primer](https://www.tenlong.com.tw/products/9789865021726)》**(Stanley B. Lippman, Josée Lajoie, Barbara E. Moo) - 一本非常經典且全面的 C++ 入門書籍,適合剛接觸 C++ 的初學者以及希望加深基礎的學習者。 2. **《[Effective C++](https://www.tenlong.com.tw/products/9787121123320)》**(Scott Meyers) - 這本書涵蓋了許多 C++ 的最佳實踐,幫助你撰寫更高效、更可靠的程式碼,適合中級到進階的開發者。 - [PDF](https://qjdxmy.com/download/effective-c++.pdf) 3. **《[The C++ Programming Language](https://www.books.com.tw/products/0010676898?srsltid=AfmBOoqhKTFdzDu0tJqRILiwo7GF_8U-XEwMioefk1ZM_kdCgmmUz6rO)》**([Bjarne Stroustrup](https://www.stroustrup.com/)) - 由 C++ 創始人撰寫的書籍,深入介紹了語言的各個方面,適合進階學習者。 4. **《[C++ 標準庫-學習教本與參考工具](https://www.tenlong.com.tw/products/9789863474395)》**([Nicolai M. Josuttis](https://www.josuttis.com/)) - 主要介紹 C++ 標準庫的使用,適合進階學習者或開發者。 5. **《[C++ 程式設計藝術](https://www.tenlong.com.tw/products/9789862803196)》**([P. Deitel & H. Deitel](https://deitel.com/)) - 適合初學者和中級開發者深入理解 C++ 的核心概念與實踐應用。 ### 練習平台 1. **[LeetCode](https://leetcode.com/problemset/all/)** - 提供各類演算法與資料結構問題,你可以使用 C++ 來解決問題並進行測試,適合提升程式設計技巧。 2. **[HackerRank](https://www.hackerrank.com/domains/tutorials/10-days-of-cpp)** - 10 Days of C++:專為 C++ 設計的挑戰,可以幫助你在 10 天內掌握 C++ 的基礎。 3. **[Exercism.io C++ Track](https://exercism.io/tracks/cpp)** - 一個開源的編程練習平台,提供 C++ 的實踐挑戰和社區代碼審查。 ### 線上論壇與社群 1. **[Stack Overflow](https://stackoverflow.com/questions/tagged/c++)** - 當你遇到 C++ 問題時,這裡是尋求幫助的好地方,社群中有大量的 C++ 專家可以解答問題。 2. **[Reddit C++](https://www.reddit.com/r/cpp/)** - C++ 的討論區,分享資源、問題討論和最新技術動向。 透過這些資源,無論是初學者還是進階學習者都能找到合適的教材,並逐步提升 C++ 開發技巧。如果你有更具體的學習目標,我也可以推薦更針對性的資源!