Learning Objectives
- 說出 C++ 從 C with Classes 發展而來的經過,以及 C++98 到 C++23 的標準版本
- 說明 C++ 的特點:編譯式、靜態型別、效能與直接控制記憶體、支援多種程式設計風格
- 舉例說明 C++ 常見的應用領域
- 逐行讀懂第一個 C++ 程式:#include、using namespace std、main、cout、return 0
- 說明原始碼變成執行檔的四個階段,並用 g++ 編譯、執行程式
為什麼要學 C++
你每天用的瀏覽器、玩的遊戲、電腦的作業系統,裡面都有大量以 C++ 寫成的程式。 C++ 能寫出執行很快、佔用資源少的程式,也讓你直接控制記憶體;代價是寫程式時要多注意細節,例如每個變數的型別、記憶體什麼時候釋放。
學 C++ 的收穫不只是一個語言:你會看見程式在電腦裡實際怎麼運作,變數放在哪裡、資料佔幾個位元組、函式呼叫時發生什麼事。 這些觀念學會之後,再看其他語言也會更清楚。
這一課先認識 C++ 的來歷與特點,再讀懂第一個程式,最後看原始碼怎麼變成可以執行的程式。
從 C 到 C++
C++ 的設計者是 Bjarne Stroustrup。他在 1979 年於 AT&T 貝爾實驗室(Bell Labs)開始這項工作, 想用 Simula 語言那種「類別」的方式來組織程式,同時保有 C 語言的執行效率,所以最早的版本叫做 C with Classes(有類別的 C)。
1983 年 C with Classes 改名為 C++,名字是同事 Rick Mascitti 提議的。++ 是 C 語言的遞增運算子(x++ 表示 x 加 1),
C++ 的意思就是「C 的下一步」。1985 年 10 月,第一個商業版本與 Stroustrup 的著作《The C++ Programming Language》第一版同時推出。
之後 C++ 由國際標準組織(ISO)制定標準,各家編譯器依照同一份標準實作:
| 版本 | 年份 | 說明 |
|---|---|---|
| C++98 | 1998 | 第一個 ISO 標準(ISO/IEC 14882:1998) |
| C++03 | 2003 | 修正 C++98 的問題 |
| C++11 | 2011 | 大幅更新:auto、範圍 for 迴圈、lambda、nullptr、智慧指標、多執行緒函式庫等 |
| C++14、C++17、C++20 | 2014、2017、2020 | 之後大約每三年發布一版 |
| C++23 | 2023 | 目前的標準,ISO 在 2024 年 10 月正式出版(ISO/IEC 14882:2024) |
下一版 C++26 正在制定中。從 C++11 開始的寫法常被稱為「現代 C++」。CodeLab 的 C++ 課程以 C++17 為準, 你在網路上看到的舊範例如果寫法和這裡不同,多半是因為它還停在 C++98 的寫法。
C++ 的特點
| 特點 | 意思 |
|---|---|
| 編譯式語言 | 原始碼先由編譯器整個翻譯成機器碼,產生執行檔;執行時不需要原始碼,也不需要編譯器 |
| 靜態型別 | 每個變數宣告時就決定型別(int、double…),之後不能改;型別不合,編譯時就會報錯 |
| 效能與直接控制記憶體 | 可以取得變數的位址、自己配置與釋放記憶體,適合需要精準控制速度與資源的程式 |
| 支援多種寫法 | 可以像 C 一樣用函式一步一步寫(程序式),也可以用類別與物件(物件導向),還可以用模板寫出適用多種型別的程式(泛型程式設計) |
如果你學過 Python,可以這樣對照:
int count = 3; // C++:先宣告型別 int,每一句以分號結尾
count = "three"; // 編譯錯誤:int 不能放字串
Python 的 count = 3 不用寫型別,執行到那一行才知道 count 是什麼;C++ 在編譯時就檢查型別,
錯誤會在程式執行之前被抓出來。
C++ 用在哪裡
C++ 適合「執行速度要快、要直接操作硬體或大量資料」的場合。以下例子都有公開資料可以查:
| 領域 | 例子 |
|---|---|
| 作業系統 | Microsoft 的 Windows 等產品大量使用 C++;macOS 的部分重要元件也是 C++ |
| 瀏覽器 | Google Chrome 的開源基礎 Chromium、Mozilla Firefox |
| 遊戲引擎 | Unreal Engine 以 C++ 撰寫遊戲邏輯;許多遊戲公司的引擎與物理模擬元件 |
| 資料庫 | MySQL Server |
| 編譯器與開發工具 | GCC、LLVM 與 Clang 本身就是用 C++ 寫的 |
| 嵌入式與即時控制 | 船用柴油引擎控制、飛機的任務軟體等,對反應時間有嚴格要求的系統 |
| 科學與高效能運算 | CERN 的資料分析工具 ROOT、粒子模擬工具 Geant4,以及 NASA 火星探測車的自主導航系統 |
這些系統很少只用一種語言,例如 Firefox 也有 JavaScript 與 Rust 的程式碼;但效能最關鍵的部分常常交給 C++。
第一個 C++ 程式
按「下一步」一句一句執行。注意程式不是從第 1 行開始跑:第 1~3 行是給編譯器看的,執行是從 main 開始。每執行一句 cout,下方的輸出區就多一行字。
逐行讀懂第一個程式
| 程式 | 意思 |
|---|---|
// 第一個 C++ 程式… |
註解:// 之後到行尾都會被編譯器略過,只寫給人看。多行註解用 /* … */ |
#include <iostream> |
把標準函式庫的 iostream(輸入輸出)引入,才能使用 cout、cin。# 開頭的是前置處理指令,結尾不加分號 |
using namespace std; |
標準函式庫的名稱都放在 std 這個命名空間裡。寫了這行,std::cout 就可以簡寫成 cout |
int main() { … } |
main 函式:每個 C++ 程式都必須有一個,程式從這裡開始執行。int 表示它結束時會回傳一個整數 |
cout << "Hello, C++!" << endl; |
cout 是輸出到螢幕的物件,<< 把右邊的資料送過去,可以一直串接;endl 代表換行 |
return 0; |
結束 main,把 0 回傳給作業系統,0 表示「正常結束」 |
幾個一開始就要養成的習慣:
- 每一句(陳述式)以分號
;結尾;忘了寫是初學最常見的編譯錯誤 - 大括號
{ }把同一個區塊的程式包起來,裡面的程式縮排 4 格,方便閱讀 - C++ 區分大小寫:
Main、COUT和main、cout是不同的名稱 main最後的return 0;可以省略(標準規定 main 執行到結尾時視同回傳 0),但其他回傳 int 的函式不能省略
using namespace std;在小程式裡很方便,CodeLab 的範例大多會寫。大型專案常改寫成std::cout, 避免自己取的名稱和標準函式庫撞名。下一個範例就是不寫這行的版本。
由上而下執行、std:: 與註解
這個程式沒有 using namespace std;,所以每個 cout、endl 前面都加上 std::。注意第 5~6 行的多行註解,執行時直接被跳過;第 9 行沒有 endl,所以下一句的輸出會接在同一行。
程式從 main 開始
greet 寫在 main 前面,但程式還是從 main 開始執行。第 11 行呼叫 greet() 時,右邊多出一個 greet 的框架,執行完它的內容後回到 main。greet 被呼叫兩次,就執行兩次。
想一想:C++ 的來歷與特點
C++ 在 1983 年改名之前叫做什麼?
C++ 名稱裡的 ++ 代表什麼?
說 C++ 是「編譯式、靜態型別」的語言,下列哪一個描述正確?
從原始碼到執行檔
電腦的處理器只看得懂機器碼,hello.cpp 這種文字檔不能直接執行。用 g++ 編譯時,會依序經過四個階段:
| 階段 | 做什麼 | 產生的檔案 |
|---|---|---|
| 1. 前置處理(preprocessing) | 處理 # 開頭的指令:把 #include 的標頭檔內容整個貼進來、刪掉註解 |
.ii(展開後的原始碼) |
| 2. 編譯(compilation) | 檢查語法與型別,把 C++ 翻譯成組合語言 | .s(組合語言) |
| 3. 組譯(assembly) | 把組合語言轉成機器碼 | .o(目的檔,object file) |
| 4. 連結(linking) | 把目的檔和標準函式庫(例如 cout 的實作)接在一起 |
執行檔 |
平常只要下一個指令 g++ hello.cpp -o hello,g++ 就會自動跑完四個階段,中間檔案不會留下來。
語法錯誤會在第 2 階段被發現;如果用了某個函式卻找不到它的實作,錯誤會在第 4 階段(連結)出現。
本頁的逐步執行動畫是 CodeLab 的直譯器在瀏覽器裡模擬執行,方便你看見每一步。真正的 C++ 程式要先經過上面的編譯流程。
用 g++ 編譯與執行
以下是在 Linux(Ubuntu)終端機的操作。Ubuntu 可以用 sudo apt install g++ 安裝 g++;Windows 可以在 WSL 裡的 Ubuntu 操作,指令相同。
-
1
確認有 g++,看一下原始碼
which g++會顯示 g++ 安裝在哪裡;沒有任何輸出代表還沒安裝。cat把檔案內容印出來,這就是剛才的第一個程式。 -
2
一個指令編譯,再執行
-o hello指定執行檔的名稱,不寫的話會叫a.out。-std=c++17指定用 C++17 標準編譯(g++ 不指定時的預設標準依版本而不同),-Wall開啟常見的警告,建議每次都加上。編譯成功時 g++ 不會印出任何東西。執行檔要寫成./hello,表示「目前資料夾裡的 hello」。 -
3
把四個階段拆開來跑
-E只做前置處理,-S做到產生組合語言為止,-c做到產生目的檔為止,最後不加這些選項就會連結出執行檔。打開hello.ii會發現它有好幾萬行:#include <iostream>把整個標頭檔都貼進來了。 -
4
編譯錯誤長什麼樣子
hello_typo.cpp是把第 6 行結尾的分號刪掉的版本。g++ 的錯誤訊息格式是「檔名:行:欄: error: 說明」,這裡說的是「在 cout 之前應該有分號」:缺分號時,編譯器要讀到下一行開頭才發現不對,所以要往前一行找。錯誤訊息後面還會印出那一行程式,並用符號標出位置(以「…」省略)。編譯失敗不會產生執行檔;echo $?顯示上一個指令的結束狀態,不是 0 就代表失敗。
想一想:程式與編譯
下面的程式會印出什麼?
#include <iostream>
using namespace std;
int main() {
cout << "2 * 5 = " << 2 * 5;
return 0;
}
下面的程式執行結果是什麼?
#include <iostream>
int main() {
cout << "Hi" << std::endl;
return 0;
}
程式呼叫了一個函式,原始碼語法都正確,但這個函式的實作不存在。g++ 會在哪個階段報錯?
動手寫:自我介紹
完成 main,讓程式印出下面三行。第三行的 21 要用 3 * 7 讓程式算出來,不要直接寫 21。
姓名:王小明
科系:資訊管理學系
3 x 7 = 21
重點回顧
- C++ 由 Bjarne Stroustrup 在 1979 年開始設計,最早叫 C with Classes,1983 年改名 C++;
++是 C 的遞增運算子 - 第一個 ISO 標準是 C++98,C++11 是一次大幅更新,之後約每三年一版,目前是 C++23;本課程以 C++17 為準
- C++ 是編譯式、靜態型別的語言,執行效率高、可以直接控制記憶體,支援程序式、物件導向與泛型等寫法
- 作業系統、瀏覽器、遊戲引擎、資料庫、編譯器、嵌入式系統與科學運算都大量使用 C++
- 程式從
main開始執行;#include <iostream>才能用cout;每一句以分號結尾;C++ 區分大小寫 - 原始碼經過前置處理、編譯、組譯、連結四個階段變成執行檔;
g++ -std=c++17 -Wall hello.cpp -o hello一次完成