<style>
.reveal .slides {
text-align: left;
font-size: 30px;
}
</style>
# 2025 系學會程式設計補強
WEEK 2 . 2025 / 09 / 22
----
## 你會學到什麼?
- Introduction to C Language
- Variables
- Basic Input / Output
- Operators
- Selection Structures
- 一些其他的雜七雜八
- ~~我們課可能會上不完~~
---
# Introduction to C Language
這個章節我沒有要幹古。
----
<div style="text-align:center;" >
## 一個 C 語言程式碼的結構會長怎樣?

</div>
----
```c= [|1-2|4-9|11,18|12-13|15|17]
#include <......>
#define OOO XXX
int global_N = 0;
double global_X = 0.0;
int foo(......){
......
}
int main(){
int local_N = 0;
double local_X = 0.0;
......
return 0;
}
```
- 最開始的地方會將該程式所需標頭檔給引入,或是使用 **巨集(define)** 定義一些常數,善用巨集功能能增加程式的可讀性。
- 在 `main` 以外的地方,可以宣告全域變數或其他的函式(未來會教)。一個 C 程式中只能有一個 `main` 函式。
- 在 `main` 或是其他函式中所宣告的變數,稱為區域變數,其他函式不可以用。
- 剩下的部分就是輪到你秀的時候了。
----
## 所以,我需要把上面那坨東西背起來嗎?
- 很多初學者都覺得寫程式就是要背很多東西。確實不能說你錯。
- 但程式語言是 **人類** 開發的,與其他自然科學不同,很多的邏輯與命名其實是直覺的。
- 你 **不應該** 是像背英文單字一樣把範例程式一字不漏背起來。
- 平時養成寫題目的習慣,久而久之你自然能把常用標頭檔背起來、理解程式的基礎架構。
- 你真的應該背的東西:
- C 的 Library 提供許多函式(功能),例如 `abs()`(開絕對值)、`qsort()`(內建的快速排序) 在 `stdlib.h`,`sqrt()`(開根號)在 `math.h`。你可能需要特別記一些好用但不常用到的函式在哪個標頭檔,以備不時之需。
- 期中期末考日期。
- ~~教授的名字。~~
----
<div style="text-align:center;" >
## C 與 C++ 又差在哪裡?

</div>
----
## 我們先來看看兩段簡單的程式碼
<div style="display: flex;">
<div style="flex: 0 0 50%">
```c=
// "Hello World" & IO in C
#include <stdio.h>
int main(){
int n = 0;
scanf("%i", &n);
printf("Hello World!, %i\n",n);
return 0;
}
```
</div>
<div style="flex: 0 0 60%;">
```cpp=
// "Hello World" & IO in C++
#include <iostream>
using namespace std;
int main(){
int n = 0;
cin >> n;
cout << "Hello, World!, " << n << endl;
return 0;
}
```
</div>
</div>
----
<div style="text-align:center">
## 有發現甚麼不同嗎?

</div>
----
## 回到剛剛的程式,發現......
<div style="display: flex;">
<div style="flex: 0 0 50%">
```c=
// "Hello World" & IO in C
#include <stdio.h>
int main(){
int n = 0;
scanf("%i", &n);
printf("Hello World!, %i\n",n);
return 0;
}
```
</div>
<div style="flex: 0 0 60%;">
```cpp=
// "Hello World" & IO in C++
#include <iostream>
using namespace std;
int main(){
int n = 0;
cin >> n;
cout << "Hello, World!, " << n << endl;
return 0;
}
```
</div>
</div>
----
## 引入的標頭檔不同
<div style="display: flex;">
<div style="flex: 0 0 50%">
```c= [2]
// "Hello World" & IO in C
#include <stdio.h>
int main(){
int n = 0;
scanf("%i", &n);
printf("Hello World!, %i\n",n);
return 0;
}
```
</div>
<div style="flex: 0 0 60%;">
```cpp= [2]
// "Hello World" & IO in C++
#include <iostream>
using namespace std;
int main(){
int n = 0;
cin >> n;
cout << "Hello, World!, " << n << endl;
return 0;
}
```
</div>
</div>
----
## C 語言尚未有"命名空間"的概念
<div style="display: flex;">
<div style="flex: 0 0 50%">
```c= [3]
// "Hello World" & IO in C
#include <stdio.h>
// ???
int main(){
int n = 0;
scanf("%i", &n);
printf("Hello World!, %i\n",n);
return 0;
}
```
</div>
<div style="flex: 0 0 60%;">
```cpp= [3]
// "Hello World" & IO in C++
#include <iostream>
using namespace std;
int main(){
int n = 0;
cin >> n;
cout << "Hello, World!, " << n << endl;
return 0;
}
```
</div>
</div>
----
## 輸入 / 輸入方式有顯著不同
<div style="display: flex;">
<div style="flex: 0 0 50%">
```c= [7,8]
// "Hello World" & IO in C
#include <stdio.h>
int main(){
int n = 0;
scanf("%i", &n);
printf("Hello World!, %i\n",n);
return 0;
}
```
</div>
<div style="flex: 0 0 60%;">
```cpp= [7,8]
// "Hello World" & IO in C++
#include <iostream>
using namespace std;
int main(){
int n = 0;
cin >> n;
cout << "Hello, World!, " << n << endl;
return 0;
}
```
</div>
</div>
<div style="font-size: 25px;">
- 在 C 中,`scanf` 與 `printf` 是由標頭檔 `stdio.h` 提供的一種輸出入 **函式**,一個是透過標準輸入讀取格式化資料,另一個則是將資料格式化之後輸出到標準輸出。
- 在 C++ 中,`cin` 與 `cout` 是由標頭檔 `iostream` 提供、在 Standard Library 下的 **串流物件**,透過重載 `>>` 與 `<<` 運算子來代表輸入串流 (istream) 與輸出串流 (ostream) 來現更為方便的格式化輸出輸入控制。
- ~~endl 是啥有點複雜,你們未來會學到,反正他跟換行的效果類似,但實際做了很多事。~~
</div>
----
<div style="text-align:center;">
## 會不會太複雜?

~~沒關係,我們今天也不會教這麼難~~
</div>
----
## 說了這麼多,我想告訴你什麼?
- C++ 雖然本身經過無數次改進與迭代,但其在基本語法(迴圈、變數的使用與賦值等等)基本上還是與 C 語言高度的相同。
- 即便到了現今,許多語言的語法依然與 C / C++ 有著極高的相似性。
- 如果你能夠在大一上就學好程式設計一,這會對你在資工系未來四年有著莫大的幫助。
- さあ~始めましょう!
---
# Variables
在這個小節中你能學會 C 語言的變數使用與宣告。
----
## 在 C 語言中,變數大體來說可以分為:
- 整數(***int***eger)
- 浮點數(***float***ing-point)
- 字元(***char***)
----
## 整數 Integer
- 老師上課時可能會介紹很多整數的類型,但其實很多都是另外一種型態的等價形式而已。
- 今天上課只會說明你最常見到的類型,通常你也只需要知道這些。
- 請注意,某些型態可能會因編譯器的不同而有不同的位元數,進而導致其值域的有所增減。
- 簡報中所有依據都是 GNU GCC 為標準,若你的編譯器不是採用這個標準,你需要自己去看說明文件確定值域範圍。
----
### 有號整數(有正負之分)
| 型態 | 值域 |
| ----------- | -------------------------------------------------------- |
| `short` | $-2^{15}$ ~ $2^{15}-1$ ($-32768$ ~ $32767$) |
| `int` | $-2^{31}$ ~ $2^{31}-1$ (約等於 $-10^9$ ~ $10^9$) |
| `long long` | $-2^{63}$ ~ $2^{63}-1$ (約等於 $-10^{18}$ ~ $10^{18}$) |
- 請注意, `int` 在某些編譯器下可能只會提供 $16$ 個位元,這時候你可能需要使用 `long` 或是 `long int` 來確保你的值域不受影響。
----
### 無號整數(僅有正數)
| 型態 | 值域 |
| ----------- | -------------------------------------------------------- |
| `unsigned short` | $0$ ~ $2^{16}-1$ ($0$ ~ $65535$) |
| `unsigned int` | $0$ ~ $2^{32}-1$ (約等於 $0$ ~ $10^9$) |
| `unsigned long long` | $0$ ~ $2^{64}-1$ (約等於 $0$ ~ $10^{19}$) |
----
## 浮點數
| 型態 | 有效位數 | 值域 |
| -------- | ------------------- | ------------------------------------------------- |
| `float` | 小數點後 6 ~ 7 位 | 約 $1.1 \times 10^{-38}$ ~ $3.4 \times 10^{38}$ |
| `double` | 小數點後 15 ~ 16 位 | 約 $2.2 \times 10^{-308}$ ~ $1.7 \times 10^{308}$ |
- 電腦儲存浮點數是以類似「科學記號」的方式存於記憶體之中。
- 這導致了電腦在計算浮點數時會產生誤差。
- 具體怎麼存的、以及為什麼會有誤差你們計概會學到。
- 如何避免誤差?
- 只要需要用到浮點數,請一律用 `double` 而不是 `float`。
- 任何的條件式都 **不能用** 浮點數做直接判斷(比如說,拿兩數平方後的結果去比較,而不是拿兩數開完根號後之值去比較)
----
## 字元
`char`: char 的背後其實也是整數(8 位元)在處理,只是之後要輸出時會通過一個名為 [ASCII Code](https://www.geeksforgeeks.org/dsa/ascii-table/) 的表格以查表的形式將數字轉換為特定字符輸出。
- 請注意,C 語言是沒有 `string` (字串)這個東西的(C++才有)。C 語言在處理字串時使用的其實是**字元陣列**進行處理。至於字串怎麼使用、怎麼儲存,之後陣列的章節才會提及,今天只會教各位怎麼輸出字串。
----
## 布林(bool)
- 一個你需要知道,很重要,但 C 語言其實沒有的東西(C++ 才有)。
- 簡單的來說就是 $1$ 與 $0$(`true` 與 `false`)。
- C 語言其實沒有實作真正意義上的 bool 型態。在 C / C++ 中,你可以拿任何一個整數型態當作是 bool 型態,其中 $0$ 代表 `false`,非 $0$ 代表 `true`。
- C 語言有提供一個名為 `stdbool.h` 讓你能夠使用 `bool` 或 `_Bool` 型態創建變數,但背後依然是使用整數在處理。(C23 起無須額外引入此標頭)
----
## 使用與創建變數
- 變數的創建:`[變數型態] [變數名稱];`
```cpp
int homo;
long long year, n;
double pi;
char alphabet;
```
- 如果你今天要創建兩個(或以上)相同型態的變數時,你可以有兩種做法
```cpp
// 方法 1:分開寫。
int n;
int k;
int m;
// 方法 2:寫在同一行,每個變數以半形逗號(,)分隔。
int n, k, m;
```
----
## 使用與創建變數
- 如何命名變數?
- C 語言有一種東西叫[保留字](https://www.ibm.com/docs/zh-tw/debug-for-zos/16.0.x?topic=programs-c-reserved-keywords),你沒辦法將這些關鍵字設為變數名稱或是函式名稱。
- 變數名稱開頭不能是數字或底線(_)
- 只能以大小寫英文字母、阿拉伯數字與底線(_)作為變數名稱。
- 嚴格區分大小寫(比如 `int Owo` 與 `int owo` 就是兩個不同的變數)
----
## 使用與創建變數
- 變數在命名時盡量使用有意義的名稱、或是題目給定的名稱來命名,這樣能夠大大增加程式碼的可讀性與可維護性,例如:
- 題目:第一個輸入為整數 $N$,表測資的數量... $\rightarrow$ `int N;`
- 要將兩數加總存入一個新變數時 $\rightarrow$ `int sum;`
- 你不應該:
- 隨便亂取變數名稱,例如將變數取作 `int owo;`、`char uwu;` 等。
- 一個變數名稱超過 50 個字符。
----
## 使用與創建變數
- 養成好習慣:創建即賦值
- 在 C / C++ 中,你創建完變數後系統只會將記憶體中的一個區塊讓給你,但不會清理。
- 意思是說,若你創建完沒有立即賦值,那變數的值會由記憶體空間中尚未清除的值做決定,而這個值是多少沒有人知道 ─ 此即垃圾值(Garbage Value)
- 這會有什麼問題?
- 如果你今天有一個變數 n 預計要給使用者輸入,並作為某個 for 迴圈的終止值。
- 但你創建完變數後忘記 `scanf` 了
- 導致的結果:你的程式每次啟動時迴圈的執行次數都不一樣。
- 如果今天程式較小可能還好,但若是動輒 100 行起跳的進階演算法題目,或是大型專案,你可能就很難找到問題。
----
## 使用與創建變數
- 回到剛剛的片段程式碼,你可以在創建時就為每個變數賦予一個指定的值
```cpp
int homo = 114514;
long long year = 2025, n = 0;
double pi = 3.14;
char alphabet = 'A';
```
如果你不知道一個變數的初值該設為什麼,設為 $0$ 就對了。
- 你也可以先創建變數,並在事後賦予其值
```cpp
int n; // 先創建一個型態為 int 的變數 n
n = 1; // 將變數 n 的值設為 1
```
- 更好的做法
```cpp
int n = 0, k = 0; // 創建兩個變數 n k,並將初值設為 0
// 讓使用者輸入 n
...
k = 1; // 將 k 的值設為 1
```
---
# Basic Input / Output
在這個小節中你能學會使用 `printf` 與 `scanf`。
----
## 前情提要
- 這個章節就只會講 `scanf` 與 `printf` 這兩種 C 語言的格式化輸入輸出。
- `puts()` 、 `fputs()` 、 `getchar()` 之類更底層的輸入輸出函式在本章節不會提,有興趣請自行至 cppreference 了解。
- C++ 的 `cin` 、 `cout` 本章節也不會提,未來大一下會學到;更進階的輸入輸出方法(如字串串流)之類的這裡也不會提。
- 如果對其他輸入輸出方法有興趣請自行搜尋,也可以找我討論。
----
## 我們先來看一段簡單的片段程式碼
```c=
int n = 0;
scanf("%i", &n);
printf("The Number is : %i\n", n);
```
- 這段程式碼的功能是要求使用者輸入一個整數 $n$,並輸出 `The Number is : n`(n 為使用者輸入之整數)
----
## C 語言如何實現格式化輸入輸出?
- 在 C 語言中,即使我們在宣告變數時就已經一併指定了其型態,我們還是要在輸入輸出時重新告訴 `printf` 與 `scanf` 這個變數是什麼型態。
- 如果我們想要輸出某一個變數時,我們通常會使用類似 `%d` 之類的語法,並在 `printf`後面放上該變數。
- 這就是 C 語言實現格式化輸入輸出的原理 ─ 格式控制符(format control string)
- 通過設定 `printf`、`scanf`內的字串與格式控制符,我們便可以輕易的將欲輸出的字串與變數進行格式化後輸出;或甚至可以指定使用者的輸入格式。
----
## 常見的格式控制符
<div style="font-size: 25px;">
| 格式控制符 | 適用的型態 |
| ------------------ | ----------------------------------- |
| `%d` 或是 `%i` | `int` |
| `%lli` 或是 `%lld` | `long long` |
| `%f` | `float` |
| `%lf` | `double` |
| `%lu` | `unsigned int` 或是 `unsigned long` |
| `%llu` | `unsigned long long` |
| `%o` | 將整數型態轉換為八進制輸出 |
| `%x` 或是 `%X` | 將整數型態轉換為十六進制輸出 |
| `%c` | `char` |
| `%s` | `char[]`(字串 / 字元陣列) |
| `%%` | 輸出 '%' 這個字符 |
| `%p` | 輸出指標變數(記憶體位置) |
</div>
----
## 回到剛剛的程式碼片段
```c= [3]
int n = 0;
scanf("%i", &n);
printf("The Number is : %i\n", n);
```
- 現在,我們可以知道 `printf` 中的 `%i` 其實就是所謂的格式控制符,其告訴 printf 函式那個位置應該會有一個 `int` 型態的變數。
- 而我們在 `printf` 字串結束後的位置放上了一個 `int` 型態的變數 `n`,告訴 `printf` 那個格式控制符應該要輸出 `n` 的值。
----
## `printf` 使用範例
```c=
long long n = 0;
int k = 1, m = 2;
double i = 3.0;
char j = 'A';
printf("The long long integer is %lli ", n);
printf("and the two int integer is %i and %i.\n", k, m);
printf("Finally, the double variables is %lf\n", i);
printf("%c", j);
```
```text
The long long integer is 0 and the two int integer is 1 and 2.
Finally, the double variables is 3.0
A
```
----
## 進階格式化輸出
我們先看一個程式碼片段
```c=
int n = 114514;
printf("%i", n); // -> 輸出 "114514"
```
如果你想要靠右對齊,你可以在 `%` 符號的後方加上一個數字:
```c=
printf("%8i", n); // -> 輸出 " 114514"
```
這會保證輸出的結果至少佔 $8$ 個字元,不足的會在數字前方補空白鍵。
反之,你也可以:
```c=
printf("%-8i", n); // -> 輸出 "114514 "
```
改將空白鍵往數字的後方補。
或者改將不足的部分補上前導 $0$
```c=
printf("%08i", n); // -> 輸出 "00114514"
```
----
## 進階格式化輸出
我們再來看一個程式碼片段
```c=
double pi = 3.1415926535;
printf("%lf", pi); // -> 輸出 "3.141592"
```
小數的輸出預設會輸出到小數點後六位。
你也可以在 `%`後方加上 `.[數字]` 來指定要輸出幾位小數(四捨五入)。
```c=
printf("%.2lf", pi); // -> 輸出 "3.14"
```
也可以做到跟剛剛整數一樣的事情,在前方或後方補上空白,或者在前方補上前導 $0$。
```c=
printf("%6.2lf", pi); // -> 輸出 " 3.14"
printf("%-6.2lf", pi); // -> 輸出 "3.14 "
printf("%06.2lf", pi); // -> 輸出 "003.14"
```
----
## 我們再回到剛剛的範例
```c= [2]
int n = 0;
scanf("%i", &n);
printf("The Number is : %i\n", n);
```
- `scanf` 的情況又有那麼億點點複雜

----
<div style="text-align:center;">
## 有發現哪裡不一樣嗎?

</div>
----
## 最明顯的不同 — 參數形式
```c=
scanf("%i", &n); // -> &n !!!!!!
printf("The Number is : %i\n", n); // -> n ???
```
- 發現了嗎? 在 `scanf` 後方的變數 `n`的前面加了一個 `&` 符號。
- 這其實會牽扯到一點指標的概念。簡單的來說,`scanf`為了讓將使用者的輸入放入你所指定的變數中,你需要告訴 `scanf` 該變數的記憶體位置是多少,這時候我們就需要再變數的前面放上 `&` 符號。
- 而在 `printf` 中,其實你只需要知道該變數的 **值** 就好,,而不用真正到變數所在的記憶體位置去爆破他,所以我們並不用加上 `&` 的符號。
----
## 最明顯的不同 — 參數形式
```c=
scanf("%i", &n); // -> &n !!!!!!
printf("The Number is : %i\n", n); // -> n ???
```
- 當然,凡事都有例外。
- 在未來你學到指標的時候,如果某一個變數中存的資料已經是記憶體位置了(參考),那你在呼叫 `scanf` 時,你自然不需要額外加上 `&` 符號。
- 反之也亦同,如果你真的真的需要將變數所在的記憶體位置打印出來時,那你在呼叫 `printf` 時就需要將變數加上 `&` 符號。
- 但在學到指標前其實你不用關注這麼多,現在你只需要記得 `scanf` 後的變數一定有 `&` 的符號就好。
----
## `scanf` 使用範例
```c=
long long n;
int k, m;
double i;
char j;
scanf("%lli", &n);
scanf("%i, %i", &k, &m); // ??
scanf("%lf", &i);
scanf("%c", &j);
```
----
## `scanf` 的格式化字串行為
注意到剛剛程式碼的第五行
```c=
scanf("%i, %i", &k, &m); // ??
```
- 這樣寫,就代表我們要求使用者必須以 `[整數], [整數]` 的形式來輸入資料,比如說 `10, 20`。
- `scanf` 會自動忽略任意數量的空白字元(空白鍵、\t、\n),所以無論你輸入的是 `10, 20` 還是 `10, 20`,都是合法的輸入。
- 其餘的非空白字元,會被當作是「固定字元」,需要原封不動地出現在使用者的輸入字串中
----
## `scanf` 的格式化字串行為
注意到剛剛程式碼的第五行
```c=
scanf("%i, %i", &k, &m); // ??
```
- 如果使用者沒有按照格式輸入呢?
- 當 `scanf` 遇到不符合格式的地方時,就會停下,將之後的輸入暫時留在一個緩衝的空間中。
- 已經讀入數值的變數會保留,剩餘的變數不會被修改
- 剩下留在緩衝空間中的資料會被下一個 `scanf` 讀到。
- 如果你不希望下一個`scanf`受到緩衝空間內的值所影響,你需要手動將緩衝空間清除。
- `scanf`、`printf` 也是有回傳值的,你可以透過他們的回傳值來判斷是否成功輸入 / 輸出。
----
## 進階格式化輸入
如果今天你想跳過某個輸入值,你可以在 `%` 的後方加上 `*`:
```
int a = 0, c = 0;
scanf("%i %*i %i", &a, &c); // 第二輸入的數字會被 scanf 拋棄。
```
限定可輸入字元的集合(類似正規表示式):
```
char alphabet;
scanf("%[a-z], &alpabet") // 限定只有小寫字母
```
這種方法還有很多其他種限定方法,但要拿字串舉例才會比較好理解。
礙於今天沒有教字串,所以這裡就多提,未來上字串會在額外說明。
---
# Operators
在這個小節中你能學會基本的 C 語言運算子。
----
## 什麼是運算子?
- 程式語言與計算機最初開發的目的就是用於數學計算
- 所以除了輸入輸出與變數之外,我們當然還需要設計一些數學符號來讓電腦進行各種運算,這就是所謂的運算子
- 在這個章節中,你會學到以下幾個運算子
- 算術運算子 (Arithmetic Operators)
- 比較運算子(Relational Operators)
- 邏輯運算子(Logical Operators)
- 位元運算子(Bitwise Operators)
- 賦值運算子(Assignment Operators)
- 條件運算子(三元運算子)(Conditional Operator)
----
## 算術運算子
- 顧名思義,讓你進行各種數學計算的。
- 除熟悉的基本加減乘除以外, C 語言還提供了許多好用的運算子。
| 符號 | 說明 | 使用範例 | 備註 |
| ---- | -------- | ---------------- | --------------------------------- |
| `+` | 算術加法 | `a+b` | 亦可以當成正號使用,如 `a = +a` |
| `-` | 算術減法 | `a-b` | 亦可以當成負號使用,如 `a = -a` |
| `*` | 算術乘法 | `a*b` | - |
| `/` | 算術除法 | `a/b` | 若除數為 $0$ 會發生錯誤使程式中斷 |
| `%` | 模運算 | `a%b` | 若模數為 $0$ 會發生錯誤使程式中斷 |
| `++` | 自身+1 | `a++` 或者 `++a` | - |
| `--` | 自身-1 | `a--` 或者 `--a` | - |
----
## 算術運算子之你一定會踩的雷
- 算術運算子的優先級與數學相同,先乘、除、模後加減,亦可以用括號 `()` 來優先計算。
- 模運算 `%` 只能用在整數,**浮點數不能用**!
- 若整數跟浮點數一起計算時,最後的運算結果會是浮點數。
- 若「整數 / 整數」或 「整數 % 整數」,當其中一個整數是負數時,從 C99 起有明確定義:
- 涉及負數的除法,採用 **截斷到 0**(捨去分數部分),而不會向上或向下取整,例如:`-9/7`結果為 `-1`、`7/-3` 結果為 `-2`
- 涉及負數的模運算,比如 `a % b`,其結果的正負由 `a` 決定。例如`-9%7`為`-2`、`9%-7`為`2`。
- 對於遵守更早期標準的編譯器,這是一個未定義行為,會發生 [Undefined Behavior](https://en.cppreference.com/w/cpp/language/ub.html)。
----
## 算術運算子之你一定會踩的雷
- `1/2` 結果不是 `0.5`...?
- 當進行「整數 / 整數」時,C 語言的除法會無條件捨去小數部分。因為 1 與 2 都是整數,所以運算的結果也必然是整數,小數部分就會被無條件捨去掉了。
- 若結果想要得到 `0.5` ,你需要將除數或者被除數擇一換成浮點數。
----
## 算術運算子之你一定會踩的雷
- 加加、減減與其先後順序
- 若你想對一個變數 `a`進行運算或是丟給某一個函式,`a++` 代表先將 `a` 的原始值回傳後再對其加值;`++a`則代表先將 `a` 加值後再回傳其值。
- 大家可以試著執行看看這個簡單的範例程式,驗證看看結果。
```c=
#include <stdio.h>
int main() {
int i = 0;
printf("%d\n", i); // Output: 0
printf("%d\n", i++); // Output: 0
printf("%d\n", i); // Output: 1
printf("%d\n", ++i); // Output: 2
printf("%d\n", i); // Output: 2
}
```
----
## 算術運算子之你一定會踩的雷
- 加加、減減與其先後順序
- 既然前一頁已經介紹過了先`++/--`與後`++/--`的差別
- 試著用五秒鐘思考一下這個問題,若 `a` 的初值為 `0`,那`a++ + ++a` 的結果會是多少?
<!-- .element: class="fragment" data-fragment-index="0" -->
- 答案是 $2$ ?是 $3$?
<!-- .element: class="fragment" data-fragment-index="1" -->
- 都不是,正確答案是 **沒有答案**。
<!-- .element: class="fragment" data-fragment-index="2" -->
- 這其實是一個典型的未定義行為([Undefined Behavior](https://en.cppreference.com/w/cpp/language/ub.html))
- 在 C 語言中,並沒有規範 ++ 與 -- 實際上應該在哪個步驟去被執行。
- 這會導致不同編譯器可能對這種操作產生不同的解讀,造成不同的結果。
- 我們在撰寫程式時,應該極力避免這種表達式被寫出來。
<!-- .element: class="fragment" data-fragment-index="3" -->
----
## 比較運算子
- 比較運算子能夠判斷兩個變數之間的關係,並回傳 `true` 或是 `false`
| 符號 | 說明 | 使用範例 |
| ---- | -------- | -------- |
| `==` | 等於 | `a == b` |
| `!=` | 不等於 | `a != b` |
| `>` | 大於 | `a > b` |
| `>=` | 大於等於 | `a >= b` |
| `<` | 小於 | `a < b` |
| `<=` | 小於等於 | `a <= b` |
----
## 邏輯運算子
- 邏輯運算子能夠判斷兩個條件之間的關係。
| 符號 | 說明 |
| --------------- | ---- |
| `&&` 或 `and` | 且運算(AND),兩個條件都要同時成立才為 `true` |
| `\|\|` 或 `or` | 或運算(OR),兩個條件只要其一成立就為 `true` |
| `!` | 反運算(NOT),`true` 變 `false`、`false` 變 `true` |
- 大家可以執行看看下面這個範例程式看看結果
```c=
#include <stdio.h>
int main() {
printf("%d\n", 1 + 1 == 2 && 3 > 0); // Output: 1
printf("%d\n", 9 < 0 || 10 > 9); // Output: 1
printf("%d\n", !1); // Output: 0
printf("%d\n", !0); // Output: 1
}
```
----
## 位元運算子
- 位元運算會將兩個變數轉換成二進位後在進行運算。
<div style="font-size:21px;">
| 符號 | 名稱 | 說明 | 使用範例 |
| ---- | ----------- | ----------------------------------------------- | -------- |
| `&` | AND(與) | 逐位進行 AND 運算(注意不要跟邏輯運算的且搞混) | `a&b` |
| `\|` | OR(或) | 逐位進行 AND 運算(注意不要跟邏輯運算的或搞混) | `a\|b` |
| `^` | XOR(異或) | 逐位進行 XOR 運算(不是開次方!) | `a^b` |
| `~` | NOT(非) | 逐位進行 NOT 運算,相當於對二進位取一補數。 | `~a` |
| `<<` | 左移 | 將所有位元向左移 $n$ 位,相當於乘以 $2^n$ | `a<<1`(等價`a*2`) |
| `>>` | 右移 | 將所有位元向右移 $n$ 位,相當於除以 $2^n$ | `a<<3`(等價`a/8`) |
</div>
----
## 賦值運算子
- C 語言提供了賦值運算子,允許你將某些操作簡化成二至三個運算子。
- 基礎型態
| 符號 | 說明 | 使用範例 |
| ---- | ---------------------- | ---------------------- |
| `=` | 將某一個變數賦予特定值 | `x = 666` |
- 注意**不是邏輯上的等於**,邏輯上的等於是兩個等於`==`,不要誤用。
- ==(這個是表情符號uwu)
----
## 賦值運算子
- 衍生型態
| 符號 | 等價形式 | 使用範例 |
| ----- | ------------ | -------- |
| `+=` | `x = x + y` | `x += y` |
| `-=` | `x = x - y` | `x -= y` |
| `*=` | `x = x * y` | `x *= y` |
| `/=` | `x = x / y` | `x /= y` |
| `%=` | `x = x % y` | `x %= y` |
| `&=` | `x = x & y` | `x &= y` |
| `\|=` | `x = x \| y` | `x \|= y` |
| `^=` | `x = x ^ y` | `x ^= y` |
| `<<=` | `x = x << y` | `x <<= y` |
| `>>=` | `x = x >> y` | `x >>= y` |
----
## 條件運算子
- 條件運算子跟我們等等要講的選擇結構很相似。
- 相較於 `if-else` 結構其實不是這麼容易閱讀,因此大部分情況不常用。
- 與`if-else` 結構幾乎沒有速度上的差異。
- 條件運算子的語法如下:
```c=
[某條件] ? [如果條件成立,做 A] : [反之,做 B]
```
- 其等價於
```c=
if (某條件){
做A;
}else{
做B;
}
```
- 一個簡單的範例
```c=
int n = 1;
int x = n > 9 ? 1 : 0;
printf("%i", x) // Output: 0
```
----
## 運算子們的優先順序
<div style="font-size:20px">
- 請注意,本表只列出目前有教到的運算子。
- 運算子的結合性:當多個相同優先級的運算子同時出現時,要從左邊算還是從右邊算。
| 優先級(越小越高) | 運算子 | 結合性 |
| ------------------ | ------------------------------------------------------------------ | ----------- |
| 1 | `()`、函式呼叫、`++`(後置)、`--` (後置) | 左 → 右 |
| 2 | `++`(前置)、`--` (前置) 、`+`、`-`、`!`、`~`、強制轉型 | **右 → 左** |
| 3 | `*`、`/`、`%` | 左 → 右 |
| 4 | `+`、`-` | 左 → 右 |
| 5 | `<<`、>>` | 左 → 右 |
| 6 | `<`、`<=`、`>`、`>=` | 左 → 右 |
| 7 | `==`、`!=` | 左 → 右 |
| 8 | `&` | 左 → 右 |
| 9 | `^` | 左 → 右 |
| 10 | `\|` | 左 → 右 |
| 11 | `&&` | 左 → 右 |
| 12 | `\|\|` | 左 → 右 |
| 13 | `? :`(條件運算子) | **右 → 左** |
| 14 | `=`、`+=`、`-=`、`*=`、`/=`、`%=`、`&=`、`\|=`、`^=`、`<<=`、`>>=` | **右 → 左** |
</div>
---
# Selection Structures
在這個小節中你能學會選擇與巢狀選擇結構。
----
## C 語言中,提供了三種實現條件選擇語法:
- 條件運算子(三元運算子) $\rightarrow$ 已經教過了ㄌ
- `if-else` 結構
- `switch` 結構(不是任天堂那個 = =)
----
## `if-else` 結構
- 我們先來看一段簡單的中文:
**如果今天下雨,我就翹課;否則,我就去上課。**
<!-- .element: class="fragment" data-fragment-index="0" -->
- 好了,你已經會 `if-else` 結構了(?)。
<!-- .element: class="fragment" data-fragment-index="1" -->
----
## `if-else` 結構
- 沒錯,`if-else` 結構就是這麼簡單。
- 如果我們將剛才的句子轉成程式碼,大概會長這樣:
```c=
if ( today.weather() == "rainy" ){
self.sleep();
}else{
self.goToSchool();
}
```
- `if-else` 的語法:
```c=
if (條件) {
如果條件成立,做 A。
}else{
如果條件不成立,做 B。
}
```
----
## `if-else` 結構
- 我們來看一個簡單的範例:判斷數字的奇偶性。
```c=
#include <stdio.h>
int main(){
int n = 0;
scanf("%i", &n);
if ( n%2 == 0 ){
printf("%i is even.\n", n);
}else{
printf("%i is odd.\n", n);
}
return 0;
}
```
----
## `if-else` 結構
- 如果你還有更多條件想判斷怎麼辦?
- 你可以使用 `else if` 來判斷更多條件。
- 一個 `if-else` 結構可以有不只一個的 `else if`,但一定只能有一個 `if` 與一個 `else`。
```c=
if (條件A) {
如果條件A成立,做事情 i。
}else if(條件 B){
如果條件B成立,做事情 j。
}else{
如果都不成立,做事情 k。
}
```
----
## `if-else` 結構
<div style="font-size:25px;">
- 我們來看一個簡單的範例:依照分數將劃分等第。
```c= [8-12]
#include <stdio.h>
int main() {
int score;
printf("Your Score: ");
scanf("%i", &score);
if (score >= 90) printf("A\n");
else if (score >= 80) printf("B\n");
else if (score >= 70) printf("C\n");
else if (score >= 60) printf("D\n");
else printf("E"); // score less then 60
return 0;
}
```
- 發現了嗎?我在這裡似乎沒有使用大括號 `{}` 將每個選擇結構包起來。
- 這其實是 C / C++ 提供給程式設計師們的一個偷懶小撇步,當你的 `if-else`區塊內若只有一行程式碼,那麼這時在使用大括號將就會顯得程式相對冗長。
- 因此,若你的選擇結構你只有一行時,C / C++ 是允許你可以不使用括號的。
</div>
----
## `if-else` 結構
- 如果你想在一個條件內在判斷另一個條件時該怎麼辦呢?
- 這時,你的直覺想法應該會是在某個條件中在寫一個 `if-else`。
- 沒錯,你是對的,恭喜你學會了巢狀選擇結構!
```c=
if (條件A) {
//如果條件A成立
if (條件B){
如果條件A成立且條件B成立,做事情 i。
}else{
如果條件A成立但條件B不成立,做事情 j。
}
}else{
如果條件A不成立,做事情 k。
}
```
----
## `if-else` 結構
- 我們現在為前面的範例加上一點小小的變化:
```c=
#include <stdio.h>
int main(){
int n = 0;
scanf("%i", &n);
if ( n > 0 ){
if ( n%2 == 0 ){
printf("%i is postive, and also even.\n", n);
}else{
printf("%i is postive, but odd.\n", n);
}
}else{
printf("%i is NEGATIVE!!!\n", n);
}
return 0;
}
```
- 現在,你應該已經差不多熟悉 `if-else` 結構了,
----
## `switch` 結構
- switch 是一個類似`if-else`的結構。但不同的是,他從頭到尾只會判斷同一個對象,且只要滿足某個條件,就會一直做下去。
- 在某些情況下,使用 `switch` 結構會比使用 `if-else` 結構來的簡潔且易讀。
- switch 語法大概長這樣:
```c=
switch(條件){
case 判斷條件1:
......
case 判斷條件2:
......
case 判斷條件3:
......
case 判斷條件4:
......
default:
......
}
```
- 可能還是有點抽象跟難懂,我們直接看範例
----
## `switch` 結構
<div style="font-size:25px">
- 這是一個根據分數分配禮物的片段程式。
- 如果你的分數低於 60 分,你將會被請出教室。
- 如果你的分數介於 60 ~ 69分,你可以獲得禮物A。
- 如果你的分數介於 70 ~ 79分,你可以獲得禮物A跟禮物B。
- 以此類推。這個程式將輸出你能夠獲得甚麼禮物
</div>
```c=
int score;
scanf("%d", &score);
printf("Your gift:\n");
switch (score / 10) {
case 10: // 100 分
printf("- Gift E\n");
case 9: // 90~99 分
printf("- Gift D\n");
case 8: // 80~89 分
printf("- Gift C\n");
case 7: // 70~79 分
printf("- Gift B\n");
case 6: // 60~69 分
printf("- Gift A\n");
break; // 讓有禮物的人不要輸出 default 訊息
default: // 0~59 分
printf("GET OUT!\n");
}
```
----
## `switch` 結構
<div style="font-size:25px">
- 如果我想要讓 `switch` 跟 `if-else` 一樣不會繼續往下執行該怎麼辦?
- 這時候,你只需要為每個 case 底下都加上 `break` 就可以了,如同剛剛的 L16。
- 還是剛剛的程式碼範例,不過為每個 case 都加上了 `break` , 讓每個人最多只能拿一個禮物。
</div>
```c=
int score;
scanf("%d", &score);
printf("Your gift:\n");
switch (score / 10) {
case 10: // 100 分
printf("- Gift E\n");
break;
case 9: // 90~99 分
printf("- Gift D\n");
break;
case 8: // 80~89 分
printf("- Gift C\n");
break;
case 7: // 70~79 分
printf("- Gift B\n");
break;
case 6: // 60~69 分
printf("- Gift A\n");
break; // 讓有禮物的人不要輸出 default 訊息
default: // 0~59 分
printf("GET OUT!\n");
}
```
----
## `switch` 結構
- 在某些時候,我們可能想要判斷某一個數字的值是不是存在於某個區間中。
- 如果按照標準的寫法,我們的程式可能會看起來很冗長。
- 比如說,如果我們想要判斷一個變數 $n$ 是不是存在於 $10 \le n \le 20$ 這個區間中,我們應該會寫成 `n >= 10 && n <= 20`。
- 其實,我們有一種能夠讓程式碼看起來更優雅的寫法: [range case syntax](https://www.geeksforgeeks.org/cpp/using-range-switch-case-cc/)
- 我們可以在 `switch` 結構中使用 `a ... b` 這樣的語法,來達到一樣的效果(請注意其中 `b`之值必大於`a`)。
- 這樣的寫法雖然不在 C 語言官方的標準內,但受主流編譯器 GNU GCC 與 Clang 的支援。
----
## `switch` 結構
回到我們最一開始以分數劃分等第的範例,其實完全可以用 `switch` 結構和 range case syntax 達成更簡潔且可讀性更高的寫法。
```c= [8-23]
#include <stdio.h>
int main() {
int score;
printf("Your Score: ");
scanf("%i", &score);
switch (score){
case 90 ... 100:
printf("A\n");
break;
case 80 ... 89:
printf("B\n");
break;
case 70 ... 79:
printf("C\n");
break;
case 60 ... 69:
printf("D\n");
break;
default:
printf("E");
}
return 0;
}
```
----
<div style="text-align:center; font-size:80px;">
**沒了,就醬**

</div>
---
<div style="text-align:center;">
## 今天的課程就到這裡 :D
因為你家講師連講義都差點編不完,根本沒時間找題目。

所以今天沒有題目,但還是請大家回去多多練習老師上課出的題目。

</div>
----
## 一些題目資源
除了上課的題目與我們放在 vjudge 上的題目之外,你也可以到這裡找題目來寫
- [ZeroJudge](https://zerojudge.tw/)
- [Uva Online Judge](https://onlinejudge.org/)
- [TIOJ](https://tioj.ck.tp.edu.tw/)
- [CSES](https://cses.fi/)
- [CodeForces](https://codeforces.com/)
- [AtCoder](https://atcoder.jp/)
----
# QA 時間
問都問,都給問(限程式題目)。
{"title":"2025 系學會程式設計補強 week2","description":"qwertyuiop","slideOptions":"{\"transition\":\"slide\"}","contributors":"[{\"id\":\"3d52dec2-fae6-4cd0-b9d8-07145517e0ba\",\"add\":36099,\"del\":9568,\"latestUpdatedAt\":1758507099788}]"}