---
title: Python C C++ bestmark520
autoHeadingNumber: true
---
[TOC]
<!-- 按 Ctrl+Shift+P → Markdown: Create Table of Contents-->
# Python C C++
## for 迴圈 (要加冒號)
### for 迴圈 + range
```python
for i in range(stop) # 同 range(0, stop, 1)
for i in range(start, stop) # 同 range(start, stop, 1)
for i in range(start, stop, step) # 完整版
for _ in range(3): # 若迴圈的變數之後不會用到,可以用 `_`
for i in range(0, n): # 0到 n-1,數量不會減少,輸出n個數字
for j in error:
# 迴圈中把每一個項目都掃一次 `for A in B`
# `error` 可以是str, list, tuple, dict, set
for (int j : error) #c++
```
#### C++ 跟 Python 迴圈的整數範圍:
C++ 的中間可以自己決定是 `<=` 還是 `<`,
Python 的中間隨遞增/遞減固定是 `<` 或 `>`。
```python
for j in range(1, 10):
for (int j = 1; j < 10; j = j + 1) #c++
# 滿足 j < 10 的最小整數,即 j = 9
for j in range(1, 10):
for (int j = 1; j <= 9; j = j + 1) #c++
# 滿足 j < 10 的最小整數,即 j = 9
for j in range(10, -1, -1):
for (int j = 10; j > -1; j = j - 1) #c++
```
### 迴圈中的迴圈
```python
for i in range(4): # i 遍歷 0 到 3(half-1)
for j in range(2): # j 遍歷 0 到 1(col-1)
for k in range(3): # k 遍歷 0、1、2(三個通道:紅、綠、藍)
print(f"i={i}, j={j}, k={k}")
```

```python
for i in range(0, 4, 1):
for j in range(3, -1, -1):
```
如圖,`col` = `0`,`row` = `3`、`2`、`1` → `col` = `1`,`row` = `3`、`2`、`1`,`i` 是外圈,`j` 是內圈:

#### Bubble Sort 寫法一:由 i 配合 j (較直觀)
```python
def bubble_sort(nums: list[int]):
n = len(nums)
print(nums)
for i in range(n - 1, 0, -1):
for j in range(i):
if nums[j] > nums[j + 1]:
nums[j], nums[j + 1] = nums[j + 1], nums[j]
print(nums)
else:
print(f'{nums}, nochange')
```
#### Bubble Sort 寫法二:由 j 配合 i(Jack 寫法)
```python
def bubble_sort_Jack(nums: list[int]):
n = len(nums)
print(nums)
for i in range(0, n-1, 1):
for j in range(0, n-1-i, 1):
if nums[j] > nums[j + 1]:
nums[j], nums[j + 1] = nums[j + 1], nums[j]
print(nums)
else:
print(f'{nums}, nochange')
```
### Iteration and Recursive 迴圈與遞迴
iteration迭代:
- 迭代就是迴圈
- For迴圈:指定次數
- while迴圈:指定條件

recursive遞迴:
- 遞迴透過函式呼叫自身來解決問題,可以參考merge sort程式的流程圖
- 遞:程式不斷深入地呼叫自身
- 迴:觸發終止條件後,程式從最深層的遞迴函式開始逐層返回
- 遞迴與堆疊的先入後出原則異曲同工
```python
def quick_sort(arr):
if len(arr) <= 1:
return arr # 遞迴要給最底層回傳的東西
pivot = arr[len(arr) // 2]
left = [x for x in arr if x < pivot]
middle = [x for x in arr if x == pivot]
right = [x for x in arr if x > pivot]
return quick_sort(left) + middle + quick_sort(right)
```
```cpp
void insertion_sort_recursive(int nums[], int n) {
if (n <= 1) return; // 遞迴要給最底層回傳的東西
// 先遞迴排序前面 n-1 個
insertion_sort_recursive(nums, n - 1);
int base = nums[n - 1];
int j = n - 2;
while (j >= 0 && nums[j] > base) {
nums[j + 1] = nums[j];
j--;
}
nums[j + 1] = base;
}
```
## while 迴圈 (要加冒號)
### while 數值範圍漸漸縮小
```c
// i 跟 j 的範圍逐漸縮小
while (left <right){
// func
left++;
right--;
}
```

```c
// 當list.next不是null就work
// 直到list.next是null為止
while (ListArray[found_entry].NextPtr != NULL)
```
### while 條件達成時執行
```python
while rest >= c: # 達成 rest>=c時,不斷地run #不斷迴圈直到rest < c
sum += rest // c
rest = rest // c + rest % c
print(sum) # 廣達面試題目,if迴圈是有指定次數;while迴圈是指定條件
```
CPE 11689
```python
t = int(input())
for case in range(1, t + 1):
e, f, c = map(int, input().split())
soda = 0
bottles = e + f
while bottles >= c: # 如果瓶子數 >=兌換數,就繼續換
soda += bottles // c
bottles = bottles // c + bottles % c
print(soda)
while B: #表示只要B不是0(真數)時,就繼續run
```
### While Ture:,搭配if break確保不會無限循環
```python
while True:
user_input = input("請輸入 'quit' 來退出迴圈: ")
if user_input == 'quit':
break
print("迴圈已退出")
```
CPE 11428,20230919例題2
找x^3 - y^3的xy解
x^3 - y^3 =(x-y)(x^2 +xy+y^2 ) <10^5 →x-y <100 →x < y+100
```python
while True:
N = int(input())
if N == 0:
break # 使這個while停止
a = 0
for j in range(1, 100):
for i in range(j, 100 + j):
if i**3 - j**3 == N:
print(i, j)
a = 1
break # 上面for迴圈停止,不代表這個while停止
if a == 0:
print("No solution")
break # 如果沒加這個,輸入0 while也不會停止,可以一直輸入值
```
### while True + try/except EOFError
20231205 第二題,except EOFError:,放在整個程式最後面
無限輸入迴圈:
```python
while True:
try:
#程式碼
except EOFError:
break
```
### C:do while
```c
do {
// 程式碼區塊
// 會先執行一次程式區域,在看有沒有符合條件
}
while (condition);
```
群聯 UFS 考古第一題
```c
int i,x,y;
i=x=y=0;
do{
++i;
if(i%2 != 0) x = x + i++;
y = y + i++;
}
while(i<=7);
printf("%d %d\n", x, y); // 1 20
// x = 0 + 1
// y = 0 + 2 + 4 + 6 + 8
```
## if(要加冒號),條件判斷
### if、elif、else 用法
```python
if x > 5:
print("x 大於 5")
elif x == 5:
print("x 等於 5")
else:
print("x 小於 5")
```
CPE 11942 正確寫法(if + elif + else)
CPE 11942 錯誤寫法(if + if + else)
如果是 if + if + else,那第二個if會跟else變成一組
要讓程式1、2、3合在一起,就要if + elif +else
C++:
```cpp
if (nums[i] == val) // 要有括號
{
nums.erase(nums.begin() + i);
}
```
### 條件式
| 類別| 意義| C| C++| Python| Verilog|
| - | - | - | - | - | - |
| **if 結構** | 條件判斷 | `if (cond) {}` | `if (cond) {}` | `if cond:` | `if (cond) begin end` | | | | | | |
| **else** | 否則 | `else {}` | `else {}` | `else:` | `else begin end` | | | | | | |
| **AND** | 且 | `&&` | `&&` | `and` | `&&` | | | | | | |
|**OR**| 或|`||`| `||` | `or` | `||` |
| **NOT** | 非 | `!` | `!` | `not` | `!` | | | | | | |
| **等於** | equal | `==` | `==` | `==` | `==` | | | | | | |
| **不等於** | not equal | `!=` | `!=` | `!=` | `!=` | | | | | | |
| **大於** | greater than | `>` | `>` | `>` | `>` | | | | | | |
| **小於** | less than | `<` | `<` | `<` | `<` | | | | | | |
| **大於等於** | ≥ | `>=` | `>=` | `>=` | `>=` | | | | | | |
| **小於等於** | ≤ | `<=` | `<=` | `<=` | `<=` | | | | | | |
| **True 值** | 布林真 | 非 0 | `true` | `True` | `1'b1` | | | | | | |
| **False 值** | 布林假 | `0` | `false` | `False` | `1'b0` | | | | | | |
| **三元運算** | 條件指派 | `a ? b : c` | `a ? b : c` | `b if a else c` | `a ? b : c` | | | | | | |
```python
if a > 0 or b < 5:
```
```c
if (a > 0 || b < 5)
```
#### 條件的等於與不等於
條件的等於 `if t == 1:`;條件的不等於 `if t !=0:`
c++數學式
```c++
if (nums_dict.find(val) != nums_dict.end()) // != .end() 不在.end()表示存在
if val in self.nums_dict: // python
if (nums_dict.find(val) == nums_dict.end()) // == .end() 表示不存在
if val not in self.nums_dict: // python
```
## OOP 物件導向,struct、class
### python class
```python
# class定義不放input
class mathperson:
def __init__(self):
self.age = None
self.name = None
p1 = mathperson()
p1.age = 18
p1.name = Jack
# class定義放input
class mathperson: # class將相同類型的程式放在一起
def __init__(self, name, age):
# __init__ 是當class被直接呼叫時,會做的動作
# 如person1 = mathperson("Alice", 30)
# self是物件引用初始值
# 當class被呼叫後,self變成person1
self.name = name
self.age = age
self.count = 0
def n_years_pass(self, n): # 過了n年後,年紀是多少
self.count += 1
self.age += n
def add(self, a, b): # 計算新輸入的a, b相加
self.count += 1
return a + b
def get_count(self):
return self.count
person1 = mathperson("Alice", 30)
person2 = mathperson ("Bob", 25)
```
```python
from test import mathperson # 引入test.py中的mathperson這個class
from test import * # 引入test.py中的所有class
from method.test import * # method資料夾內的test.py
person1 = mathperson("Alice", 30) # 創建一個新的 Person 物件
#__init__ 使 Person("Alice", 30) 等同 __init__(1, "Alice", 30),但不能真的直接呼叫__init__
print(person1.name) #在創建過程中,__init__ 方法被呼叫,並且設定了 name 和 age 屬性
print(person1.age)
answer1 = person1.add(5, 3) # 使用class中的函式語法,直接加 .func
print(f"計算次數:, {person1.get_count()} and 兩數字相加:, { {answer1}") # 獲取計算次數
person2 = mathperson ("Bob", 25) # __init__ 再次work
print(person2.name)
print(person2.age)
```
### C C++ OOP名詞
| 名稱 | 說明 |
| --- | --- |
| Encapsulation(封裝)|函式有回傳值叫`Getter`,僅寫入叫`Setter`|
| Inheritance(繼承) |子類別使用父類別的函式,如蘋果繼承水果,不會繼承父類別的private、建構子、解構子|
| Polymorphism(多型) | 配合virtual,動態繫結 (dynamic binding)。使用父類別指標宣告,那個指標隨時可以改成別的子類別,main裡執行時期才決定要呼叫哪個子版本的 speak() |
| Abstraction(抽象) | 定義一個父類別不能被實例化(new),只能被繼承,如人類、形狀、水果這種父類別,通常透過 抽象類別或 純虛擬函式`virtual void draw() = 0;`實現|
| Interface | 純虛擬類別,提供統一介面,抽象只要有一個純虛函式即可,Interface一定要全部都是純虛函式 |
| Virtual Function(虛擬函式) | 允許子類別覆寫父類別函式,父類別語法,`virtual void draw() = 0;` 支援 動態繫結(dynamic binding),也就是 執行期決定呼叫哪個版本的函式,可以實現多型跟抽象 |
| Pure Virtual Function | 強制子類別實作,用於抽象類別 |
| Override(複寫)| 子類別重新定義父類別虛擬函式,子類別使用`override`這個語法|
| Overloading(多載) | 相同名稱,參數型態或數量不同,呼叫同個函式很多次|
| Access Specifiers(private/public/protected) |private:不能被修改且不能被繼承,protected:不能被修改但可以被繼承,class 預設 private,struct 預設 public |
| Constructor / Destructor | 建立與銷毀物件的初始化與清理,一個class裡面可以有很多種建構子,C沒有建構子 |
| this 指標 | 怕有名稱衝突,指向當前創建的物件的變數 |
|Function Hiding(函式遮蔽)|父與子都有同名的函式(沒有 virtual)時,子類別的版本會蓋掉父類別的版本|
### Encapsulation(封裝)
透過 Getter / Setter 隱藏內部資料,函式名稱不一定要取名叫get / set
```cpp
class Person {
private:
int age;
public:
void setAge(int a) { age = a; }
int getAge() { return age; }
};
int main() {
Person p;
p.setAge(25);
cout << p.getAge();
}
```
### Interface、Abstraction、Pure Virtual Function、Virtual Function、Inheritance、Override、Polymorphism
- Interface(介面):純虛擬類別,提供統一介面
- Abstraction(抽象):定義介面或純虛擬函式
- Pure Virtual Function(純虛擬函式):強制子類別實作,用於抽象類別
- Virtual Function(虛擬函式):子類別可以修改
- Inheritance(繼承):子類別可使用父類別的方法
- Override(複寫):子類別重新定義父類別虛擬函式
- Polymorphism(多型):配合 `virtual`,執行時期才會決定呼叫哪個子版本的函式
```cpp
class Animal {
public:
virtual void speak() = 0; // 純虛擬函式 → 抽象
int age = 25; // 有非純虛函式,不是interface
};
// Animal a; // 會報錯,只能被繼承 // 是抽象不是interface
class Animal2 { // class裡面只有純虛函式,是抽象也是interface
public:
virtual void speak() = 0;
virtual void breathe() = 0;
};
class Animal3 {
public:
virtual void speak() { cout << "Animal sound" << endl; }
};
// Animal3 a; // 不會報錯 // 不是抽象
class Dog : public Animal { // Inheritance語法
public:
void speak() override { cout << "Woof!" << endl; } // override寫在子類別裡面
};
class Cat : public Animal {
public:
void speak() override { cout << "Meow!" << endl; }
};
int main() {
Dog d; // 這不是多型,Dog d已經寫死是dog了
d.speak(); // 編譯前就知道一定是Dog
Animal* a = new Dog();
delete a; // 釋放 Dog
a = new Cat(); // 指向 Cat
a->speak(); // 執行時才知道是Cat // a可以隨時變換
}
```
### Overloading(多載)
相同名稱但參數型態或數量不同
```cpp
class Math {
public:
int add(int a, int b) { return a + b; }
double add(double a, double b) { return a + b; }
};
int main() {
Math m;
cout << m.add(1, 2) << endl;
cout << m.add(1.5, 2.5) << endl;
}
```
#### Access Specifiers(存取權限)
控制可見範圍;`class` 預設 private,`struct` 預設 public
```cpp
class Example {
private:
int secret = 10;
public:
void show() { cout << secret; }
};
int main() {
Example e;
e.show(); // OK
// e.secret; // 錯誤: private
}
```
### Constructor / Destructor(建構子 / 解構子)
建立與銷毀物件時自動執行初始化與清理,一個class裡面可以有很多種建構子
```cpp
class MyVector {
public:
MyVector() : n(0), m(nullptr) {}
private:
int n;
int* m;
};
class MyVector2 {
public:
int n;
int* m;
};
int main() {
MyVector v; // 有自動初始化
MyVector2 v; // 沒有自動初始化,n跟m會是未知值
}
```
### this 指標
指向當前物件,用於區分變數或鏈式操作
```cpp
class Counter {
private:
int value;
public:
Counter(int v) { this->value = v; }
Counter& add(int x) { value += x; return *this; }
void show() { cout << value << endl; }
};
int main() {
Counter c(5);
c.add(3).add(2).show(); // 鏈式呼叫
}
```
### Function Hiding(函式遮蔽)
父與子都有同名的函式(沒有 virtual)時,子類別的版本會蓋掉父類別的版本
```cpp
class Base {
public:
void show() { cout << "Base" << endl; }
};
class Derived : public Base {
public:
void show() { cout << "Derived" << endl; }
};
int main() {
Derived d;
d.show(); // 呼叫 Derived 版本
}
```
# C C++
## C C++宣告
| 資料型態 | 中文說明 | 範例值 | 記憶體大小(常見) |
| ------------------ | ------------ | ----------------- | ----------- |
| **char** | 單一字元 | 'A', 'b', '3' | 1 byte |
| **unsigned char** | 不含負數的字元 / 整數 | 0~255 | 1 byte |
| **short** | 短整數(含負) | -32768 ~ 32767 | 2 bytes |
| **unsigned short** | 無號短整數 | 0 ~ 65535 | 2 bytes |
| **int** | **整數** | -21 億 ~ 21 億 | 4 bytes |
| **unsigned int** | 無號整數 | 0 ~ 42 億 | 4 bytes |
| **long** | 長整數 | 視系統而定 | 4 或 8 bytes |
| **long long** | 更大的整數 | -2⁶³ ~ 2⁶³-1 | 8 bytes |
| **float** | **小數(單精度)** | 3.14 | 4 bytes |
| **double** | 小數(雙精度) | 3.1415926 | 8 bytes |
| **long double** | 超高精度小數 | 視編譯器(10~16 bytes) | 10~16 bytes |
| **bool**(C++) | 布林 | true/false | 1 byte(實際) |
| 型態 | 中文說明 | 範例 |
| ---------- | --------- | -------------------- |
| **char[]** | 字元陣列 → 字串 | `char s[] = "ABC";` |
|**int[]** |array|`int a[3] = {1,2};`|
| **char*** | 指向字串的指標 | `char *p = "Hello";` |
| **int*** | 指向整數的指標 | `int *p;` |
(C 沒有 STL)
| 型態 | 說明 | 範例 |
| -- | -- | -- |
| **vector** | 動態陣列 | `vector<int> v;` |
| **string** | 字串類別 | `string name = "Jack";` |
| **map<K, V>** | key-value 結構 | `map<string, int> mp;` |
| **set** | 自動排序,不重複 | `set<int> s;` |
## C C++差異
| 項目 | C | C++ |
| ----- | --------------------------------- | ----------------------------------------------- |
| 語言類型 | 結構化程式語言 | 支援物件導向(OOP) + 結構化程式 |
| 標準庫 | 標準 C 函式庫 `<stdio.h>` `<stdlib.h>` | 標準 C++ 庫 `<iostream>`, STL `<vector>` `<map>` 等 |
| 記憶體管理 | 手動(malloc/free) | 支援 new/delete,還可用智能指標(C++11) |
| 函式多載 | 不支援 | 支援函式多載(同名不同參數) |
| 運算子多載 | 不支援 | 支援(可以自訂 +, -, << 等) |
| 結構 | `struct` 只存資料 | `struct`/`class` 可存資料與函式(方法) |
| 引用 | 無 | 有引用 `int &a = b;` |
| 名稱空間 | 無 | `namespace`,避免命名衝突 |
輸入輸出
| C | C++ |
| -------------------------- | -------------------------------- |
| `printf("Hello %d\n", x);` | `cout << "Hello " << x << endl;` |
| `scanf("%d", &x);` | `cin >> x;` |
記憶體分配
| C | C++ |
| ------------------------------------- | ------------------- |
| `int *p = (int*)malloc(sizeof(int));` | `int* p = new int;` |
| `free(p);` | `delete p;` |
字串處理
| C | C++ |
| ------------------------------------------------- | ------------------------------- |
| char 陣列 + 函式:`char str[100]; strcpy(str, "abc");` | `string s = "abc"; s += "def";` |
| 不支援直接比較字串 | `==` 可直接比較 `string` |
容器
| C | C++ |
| ------- | ------------------------------------ |
| 陣列或自訂結構 | STL:`vector`, `list`, `map`, `set` 等 |
```cpp
vector<int> v;
v.push_back(10);
cout << v[0];
```
函式差異
* **C**:同名函式只能一個(無多載)
* **C++**:支援多載
```cpp
int add(int a, int b) { return a+b; }
double add(double a, double b) { return a+b; } // 多載
```
結構/類別差異
C:
```c
struct Node {
int data;
struct Node* next;
};
```
C++:
```cpp
struct Node {
int data;
Node* next;
Node(int val) : data(val), next(nullptr) {} // 建構子
};
```
* C++ 可以在 struct/class 裡放 **函式、建構子、解構子**
* C 只能存資料欄位
其他常用差異
| 功能 | C | C++ |
| ---- | ------------ | ------------------------------------- |
| bool | 無(用 int 0/1) | 有 `bool`,true/false |
| enum | 基本 enum | enum class(類型安全) |
| 預處理器 | `#define` 常用 | 可以用 `const`, `inline`, `constexpr` 替代 |
| 指標 | 完全依賴指標 | 可以用引用 & 智能指標,減少手動管理 |
**總結**
* **C**:簡單、結構化,手動管理記憶體,多用函式、指標
* **C++**:C 的超集,支援 OOP、STL(標準模板函式庫)、引用、函式多載、物件管理,更高階抽象
## pointer 指標
### 語法
| 符號 | 內容/房間 | 說明 |
| -- | ----- | --------------------- |
| x | 5 | x 的value是 5 |
| &x | A房間 | &口,口是變數,取其位址,用 A 房間表示 |
| *p | 5 |*口,口是地址,讀其地址的變數value|
| p | A房間 | p 是x 的位址,A 房間 |
| &p | B房間 | p真正的房間 |
```cpp
int a = 5;
// 此為簡寫
int *p = &a; // 宣告p變成指標變數,並指向a的地址
// 此為展開後
int *p; // 宣告p變成指標變數
p = &a; // 指向 a 的地址
```
```cpp
int x = 5;
int *p = &x;
*p = 10; // 指定這個位子的value是10
/*
x → 10
*p → 10
p → 還是 A 房間
*/
int y = 20;
p = &y; // 改變p這個變數指向的地址
/*
p → y 的房間
*p → 20
x → 5 不變
*/
```
|||
|- | - |
- 地址賦值給指標
```cpp
struct Node {
int data;
Node* next;
Node(int val) : data(val), next(nullptr) {}
};
Node* top;
Node* newNode = new Node(val);
newNode->next = top;
top = newNode; // 因為top跟newNode都是指標,直接改top指向的位子
```
### 變數、stack、heap
```c
int a = 10; // 建立在stack上
int *p = &a; // p 指向 stack 上的 a
*p = 20; // 修改 a 的值
int* p = (int*)malloc(sizeof(int)); // 建立在heap上
*p = 5; // 同p[0]是5 // 實際上並不知道heap上value是5的位子的identify是什麼
```
- int*p = malloc 在heap上建立任何東西 其實都看不到heap層的id
- 所以用指標會覺得不知道自己在指什麼,但實際上指到了
### 為什麼要用指標? call by address,func輸入指標(地址)改stack的value
- call by value,是讀`5`這個數字
- call by address,是讀`0x100`這個地址
- 想改main上stack的任何變數、指標,都要傳stack上的地址
- 指標 + malloc將記憶體分配到 heap 層:函式可以操作 heap 上的資料,任何函式外也可見
- Python 為何沒有指標?:因為 Python 使用 mutable(可變)型別,例如 `list`、`dict`、`set`,函式可以直接修改物件內容,而不需要指標概念。
```c
void func(int* c) {
*c = 10; // 指定這個位子的value是10
}
int main() {
int a = 5;
func(&a);
printf("%d", a); // 透過指標間接改值
}
```
|address|identifier|value|
|-|-|-|
|0x100|a|5|
|0x200|c|0x100|
|0x300|*c|10|
### 建立空指標
- 只要看到建立指標,就要知道在stack建立了一個變數
```c
int *a;
/*
stack
address | id | value |
0x1000 | a | ??? |
*/
a = (int*)malloc(sizeof(int));
// 在 heap 配置一塊可存 int 的空間,並把位址存進 a
// 同int*a = (int*)malloc(sizeof(int));
/*
stack
address | id | value |
0x1000 | a | 0x3000 |
heap
address | id | value |
0x3000 | ???| ??? |
*/
*a = 5; // 將 5 存進 heap 中 a 指向的位置
/*
stack
address | id | value |
0x1000 | a | 0x3000 |
heap
address | id | value |
0x3000 | ???| 5 |
*/
```
### func輸入指標的地址改stack的指標的value
- 要改stack上的變數或指標,func都要input其地址
- 看到func有輸入指標 `func(Node *head)` 此時`head`在func內就是地址,指向Node
- `func(Node **head)` 此時`*head`在func內就是地址,指向Node*
```c
int *b = NULL;
func(&b); // 寫法1 void,用func更改指標
b = func2(b); // 寫法2 int*,用func回傳地址賦值給指標
// 想要改 stack 上的指標(b 指向哪)
// 一定要傳入指標的位址 → int **
// call by address 也適用於更改指標的value(地址)
void func(int **c) {
*c = (int*)malloc(sizeof(int)); // 配置 heap,並改變外層指標 b (*c) 指向某個heap地址,該地址放int
**c = 10;
int *d; // d 是 func stack 內的指標
d = (int*)malloc(sizeof(int)); // heap 配置空間
// c 跟 d 會在 func 結束後消失
// 若不 return 或 free, d 指向的這塊 heap 會 memory leak
}
int* func2(int* e) {
int f = (e == NULL) ? 10 : *e;
int *g = malloc(sizeof(int));
*g = f + 10;
return g; // 回傳heap建立的地址
}
```
### python自帶指標功能
```python
# list範例
a = [1, 2, 3]
b = a # b 指向同一個 list
b[0] = 10
print(a) # [10, 2, 3]
# class範例
class Node:
def __init__(self, val):
self.val = val
a = Node(5)
b = a # b 指向同一個 Node 物件
b.val = 10
print(a.val) # 10
```
對應c++
```cpp
#include <iostream>
#include <vector>
using namespace std;
// 直接學python的寫法沒用
int main() {
vector<int> a = {1, 2, 3};
vector<int> b = a; // 拷貝 a 的內容
b[0] = 10;
for(int v : a) cout << v << " "; // 1 2 3
return 0;
}
int main() {
vector<int>* a = new vector<int>{1, 2, 3};
vector<int>* b = a; // b 指向同一個 vector
(*b)[0] = 10;
for(int v : *a) cout << v << " "; // 10 2 3
delete a;
return 0;
}
class Node {
public:
int val;
Node(int v) : val(v) {}
};
// 直接學python的寫法沒用
int main() {
Node a(5);
Node b = a; // 值拷貝
b.val = 10;
cout << a.val << endl; // 5
return 0;
}
int main() {
Node* a = new Node(5);
Node* b = a; // b 指向同一個 Node
b->val = 10; // 等效 (*b).val
cout << a->val << endl; // 10
delete a;
return 0;
}
```
### 台大上課補充
- `*(&x)`,指標提取`&x`這個地址,所以`*(&x) equal x`
- `int* p`跟`int *p`是一樣的
- 宣告用的`int* p`跟取值用的`*p`是完全不一樣的
- 取值的`&x`跟reference的`int &x = a;`是完全不一樣的
- 宣告空的指標要用`int* p = nullptr;`
- 掃描array時可以知道a[0]的指標跟當下a[i]的指標,那要如何知道當下的i是多少? `int* a = a[0];` `int* p = a[i]` `p-a equal i`
- call by value的時機:
- 不想改變main宣告的變數
- 函式只需要讀變數,不需要改它
- call by pointer的時機:
- 想改變main宣告的變數但函式沒return
- 使用指標的時機:
- Dynamic memory allocation
- Dynamic arrays
- Dynamic data structures
- C strings
### `++*p`, `*p++`, `*++p`的不同
- 討論兩件事情:
- 賦值
- 事後p的指標指向哪裡
- ++放後面,事後再+,++放前面,賦值前先處理
```c
#include <stdio.h>
int main() {
int arr1[] = {10, 20, 30};
int arr2[] = {10, 20, 30};
int arr3[] = {10, 20, 30};
int *p1 = arr1;
int *p2 = arr2;
int *p3 = arr3;
// ++*p // ++(*p)
int val1 = ++*p1; // arr1[0] 原本 10 → 11
printf("%d %d\n", val1, *p1); // 11 11 ← *p1 和 val1 都是 11
// *p++
int val2 = *p2++; // 先取 arr2[0] = 10,然後 p2 指向 arr2[1]
/*
後置遞增縮寫
int val2 = *p2; // 先取值
p2 = p2 + 1; // 再指標遞增
*/
printf("%d %d\n", val2, *p2); // 10 20 ← val2 = 10,*p2 = 20
// *++p // *(++p)
int val3 = *++p3; // p3 先移到 arr3[1],再取值 arr3[1] = 20
printf("%d %d\n", val3, *p3); // 20 20 ← val3 = 20,*p3 = 20
return 0;
}
```
### function pointer 函式指標
```c
int (*math)(int a, int b);
math = add;
```
```c
// (int*)(*f(int,int))(int)
int*(*f(int,int))(int){
// 讀取f回傳的函數指標
// 所以這邊要寫的是f的功能
// f回傳的函數input是int,output是int*
};
```
```c
typedef int* (*G_FUNC)(int);
G_FUNC f(int a, int b);
```
```c
#include <stdio.h>
int* g1(int x) {
int x = x + 100;
return &x;
}
int* g2(int x) {
int x = x * 10;
return &x;
}
int *(*f(int a, int b))(int) {
if (a + b > 10)
return g1; // 因為你前面定義了回傳int*,所以會回傳 g1 的函式指標
else
return g2; // 回傳 g2 的函式指標
}
int main() {
int *(*func_ptr)(int); // 第一次呼叫 f,給兩個 int
func_ptr = f(7, 5); // 7+5=12 > 10 → 會回傳 g1
int *p = func_ptr(123); // 第二次呼叫 func_ptr ,給一個 int
printf("Result = %d\n", *p); // g1(123) = 123 + 100 = 223
return 0;
}
```
### 群聯考古題1 What is the result?
```c
void change(int *a, int &b, int c) {
c = *a;
b = 3;
*a = 2;
return;
}
void main() {
int a = 1, b = 2, c = 3;
change(&a, b, c);
printf(“%d %d %d”, a, b, c);
return;
}
```
```
2 3 3
```
### 群聯考古題2 What is the result?
```c
int a[] = {1,2,3,4,5,6};
int *p = a;
*(p++) += 100; // postfix先做等號兩邊運算 再做括號內的運算 所以是p[0]+100 再位址+1到p[1]
*(++p) += 100; //prefix先做括號內的運算 再做等號兩邊運算 所以先位址+1到p[2]再取值+100
for(int i = 0; i<6 ; i++)
printf("%d ", a[i]);
//補充
int a[] = {1,2,3,4,5,6};
int *p2 = a;
int *p3 = a;
int e = (*p2)++; //e = *p = 1, 再e+1 但沒有e = e+1 所以++沒用
int f = ++*p3; //f = *p2 + 1 = 2
cout << "e =" << e << endl;
cout << "f =" << f << endl;
```
```
a = {101, 2, 103, 4, 5, 6}
e = 1
f = 3
```
## array and pointer 陣列與指標
### 陣列a退化成地址
- 非常反直覺,宣告好陣列a後,大部分a單獨出現時會退化成地址`0x1000`
- `siezof(a)`, `int *a`, 這種時候陣列a不會退化
### 1D array main建立好array -> func(int *a)
```c
// main
int arr[5] = {1, 2, 3, 4, 5}; // 存入stack
int *p = arr; // 合法 // 因為陣列退化成指標 a會變成存a的地址
int *p = &arr[0]; // 等價
int x = 10;
int *p = x; // 不合法
int *p = &x; // 正確
func(arr); // 傳入 stack 陣列
int *aaa = malloc(sizeof(int) * 5); // 存入heap
aaa[0] = 1;
func(aaa);
free(aaa);
// func
void func(int *a) {
a[0] = 99; // 改變 arr[0]
}
```
* main:定義 stack 陣列 `arr[5]`,內容初始化
* func input:`int* a`,退化成 pointer
* 操作結果:func 可以改內容,但不能 malloc 新記憶體給 arr
* 記憶體位置:全部在 stack
```c
int nums[3] = {1, 2, 3};
// 傳陣列,用索引操作
void sort(int arr[], int size) { // 同 int *arr
for (int i = 0; i < size; i++)
arr[i] += 10; // 編譯器幫你隱式轉成 *(arr + i)
}
// 傳指標,用指標運算
void sort(int *arr, int size) {
for (int i = 0; i < size; i++)
*(arr + i) += 10; // 同*arr++ += 10;
}
// 用指標 + 指標移動
void sort(int *arr, int size) {
int *ptr = arr;
/*
同
int *ptr;
int ptr = &arr[0];
用 ptr 不會修改原本的 arr
*/
for (int i = 0; i < size; i++)
*ptr++ += 10; // 先解引用,再讓指標移到下一個元素
}
```
三種寫法功能完全等價,只是表達方式不同
| 寫法 | 內部本質 | 好處 | 何時常用 |
| ------------ | --------------- | -------- | ----------------------------- |
| `arr[i]` | 隱式 `*(arr + i)` | 易懂、直觀 | 一般陣列操作 |
| `*(arr + i)` | 指標運算 | 明確顯示位址運算 | 教學、低階操作 |
| `*ptr++` | 指標迭代 | 適合走訪動態結構 | linked list、pointer traversal |
### 指標的指標 int**
```c
int* a; // a是指向int(整數)的指標
int** b;
// b是指標的指標 pointer to pointer
// b是指向int*的指標
// 指向指向整數的指標的指標
// b的value地址 →地址 →整數
```
|address|identifier|value|
|-|-|-|
|0x100|c|5|
|0x200|a|0x100|
|0x300|b|0x200|
```c
int* change(int* aaa, int* bbb) {
aaa[0] = 10; // 等價於 *(aaa + 0)
*bbb = 10;
return aaa;
}
int main() {
int aaa[5] = {0};
int bbb = 0;
int* res = change(aaa, &bbb);
printf("aaa[0] = %d\n", aaa[0]); // 10
printf("bbb = %d\n", bbb); // 10
return 0;
}
```
- `int* change(int* aaa, int* bbb)`兩個都是 `int*`
- aaa 指向陣列第一個元素
- bbb 指向單一整數
- 在 C 裡函式不能直接回傳陣列,只能回傳指標
- res是指向陣列第一個元素的指標
### 1D array main建立指標 -> func(int **a) malloc建立array在Heap 並回傳給 caller
```c
// main
int* p = NULL; // 只是定義指標,尚未分配記憶體
func(&p); // 傳入 p 的地址
// func
void func(int **a) {
*a = malloc(sizeof(int) * 5); // malloc 給 caller
(*a)[0] = 42;
}
```
* main:定義 pointer,未指向有效記憶體
* func input:`int** a` → 可以 malloc 並修改 main 的 pointer
* 你建立了一個指標,你想在函式內修改這個指標(建立malloc),就必須傳這個指標的地址,所以要傳入指標的指標進func
* 函式內才用`malloc`建立`array`
* 操作結果:main 的 `p` 會指向 heap 上的新 array
* 記憶體位置:array 在 heap,指標本身在 stack
### 2D array main建立好array在stack -> func(int a[][3])
```c
// main
int arr[2][3] = {{1,2,3},{4,5,6}}; // 在記憶體上是 1D 連續的,但在型態與語意上仍然是 2D array
func(arr); // 傳入 stack 二維陣列
// func
void func(int a[][3]) {
// func(int (*a)[3])
// 不能int **a,因為記憶體排序其實是1D
// 這裡的[3]是在說a每次移動3格
a[0][0] = 99; // 改變 arr[0][0]
}
```
* main:定義 stack 二維陣列 `arr[2][3]`
* func input:`int a[][3]` 或 `int (*a)[3]`
* 操作結果:func 可以改內容,但不能 malloc 新 array 給 arr
* 記憶體位置:全部在 stack
### 2D array main建立好array在heap -> func(int **a)
```c
// main
int **arr = malloc(sizeof(int*) * 2);
for(int i = 0; i < 2; i++) arr[i] = malloc(sizeof(int) * 3); // 已分配記憶體
func(arr); // 傳入已 malloc 的 array
// func
void func(int **a) {
a[0][0] = 99; // 可以直接改內容
}
```
* main:malloc 外層指標和每一層子陣列
* func input:`int** a`
* 操作結果:func 可以直接改內容
* 記憶體位置:array 在 heap,指標本身在 stack
Stack
| Address | Identify | Value |
| ------- | --------- | ---------------------- |
| 0x1000 | arr | 0x5000 (指向 heap 外層陣列) |
Heap
| Address | Identify | Value |
| ------- | --------- | ---------------------- |
| 0x5000 | arr[0] | 0x6000 (指向第一列 int[3]) |
| 0x5008 | arr[1] | 0x7000 (指向第二列 int[3]) |
| 0x6000 | arr[0][0] | ? |
| 0x6004 | arr[0][1] | ? |
| 0x6008 | arr[0][2] | ? |
| 0x7000 | arr[1][0] | ? |
| 0x7004 | arr[1][1] | ? |
| 0x7008 | arr[1][2] | ? |
### 指標陣列(陣列裡的元素是指標)
```c
int* a[3]; // 陣列裡的每個元素都是 int*
int** pa = a; // 指向陣列裡的元素 → int** // a 退化成 &a[0]
int*** c = &pa; // c 指向 pa
a[0] = malloc(sizeof(int) * 5); // 每個元素又指向一個 int 陣列
a[0][0] = 10; // 等價pa[0][0] = 10
c[0][0][0] // 最終等於 a[0][0] → 10
```
* `int* a[3]` → 陣列裡的每個元素都是 int*
* `int** pa` → 指向這個陣列裡的指標元素
* a指標陣列
* pa是pointer to pointer
* `pa[i][j] ≡ *(*(pa + i) + j)`
* 因為陣列a會退化成0x1000,a[0] → 0x2000(heap 開始地址)
* a[0][0] → *(a[0] + 0) → *(0x2000) → 值 = 10
* pa[0] → *(pa + 0) → *(0x1000)
* *(0x1000) → a[0] 的值 → 0x2000(heap 地址)
Stack
| Address | Identify | Value |
| ------- | -------- | ------------------ |
| 0x1000 | a | 0x2000 (a[0]) |
| 0x1008 | | 0x0000 (a[1] 尚未指向) |
| 0x1010 | | 0x0000 (a[2] 尚未指向) |
| 0x1018 | pa | 0x1000 (指向 a[0]) |
Heap
| Address | Identify | Value |
| ------- | -------- | ----- |
| 0x2000 | a[0][0] | 10 |
| 0x2004 | a[0][1] | ? |
| 0x2008 | a[0][2] | ? |
| 0x200C | a[0][3] | ? |
| 0x2010 | a[0][4] | ? |
* 用法對應 LeetCode 的 2D array:
```c
int** result = malloc(sizeof(int*) * capacity); // result[i] 是 int*(每個三元組)
result[i] = malloc(sizeof(int) * 3); // malloc 真正的三元組
result[i][0] = nums[i];
```
### 2D array main建立指標 -> func(int ***a) malloc建立array在Heap 並回傳給 caller
```c
// main
int **arr = NULL;
func(&arr); // 傳入 arr 的地址
// func
void func(int ***a) {
*a = malloc(sizeof(int*) * 2); // malloc 外層
for(int i=0;i<2;i++)
(*a)[i] = malloc(sizeof(int) * 3); // malloc 每一列
(*a)[0][0] = 42;
}
```
* main:只定義指標,未 malloc
* func input:`int*** a` → 可以 malloc 外層 pointer 給 caller
* 操作結果:main 的 `arr` 會指向 heap 上的 2D array
* 記憶體位置:外層和內層都在 heap
### 總整理
```c
int a; // 單一變數
int *p; // 指標
int a[5]; // array
int *a[5]; // array of pointer
int (*a)[5]; // pointer to array,input進func的形式
```
| Array 類型 | main 做什麼 | func input | pointer 層數 | 需 malloc 給 caller? |
| ------------------- | ----------------------- | ---------- | ---------- | ------------------ |
| Stack 1D | arr[5] 初始化 | int* a | 1 | ❌ |
| Heap 1D | int* p=NULL | int** a | 2 | ✅ |
| Stack 2D | arr[2][3] 初始化 | int a[][3] | 2D pointer | ❌ |
| Heap 2D | int** arr = malloc(...) | int** a | 2 | ❌(已 malloc) |
| Heap 2D + malloc 回傳 | int** arr=NULL | int*** a | 3 | ✅ |
## string and pointer 字串與指標
### char *str → func(char *s),指標(pointer to char)
```c
// main
char *str = "dog";
str[0] = 'D'; // 無法更改(UB)
// func
void func(char *s);
/*
Stack
str ──► 0x3000
RODATA
0x3000 d o g \0
*/
```
* `"dog"` 在 rodata(唯讀資料區)
* `str` 在 stack
* 本體是 指標
* 不可改內容(Undefined Behavior)
### char str[] → func(char *s),char array(陣列)
```c
// main
char str[] = "dog";
str[0] = 'D'; // 合法
func(str);
// func
void func(char *s) {
s[0] = 'D';
}
/*
Stack
str[0] = 'd'
str[1] = 'o'
str[2] = 'g'
str[3] = '\0'
*/
```
* 字串內容在 stack
* 本體是 陣列
* 傳入 func 時 退化成 char*
* 可以改內容
* 只要宣告時有 `[]`,本體一定是 array
### char *str = malloc(...) → func(char *s),指標(指向 heap 上的 char array)
```c
// main
char *str = malloc(4);
strcpy(str, "dog");
/*
Stack
str ──► 0x5000
Heap
0x5000 d o g \0
*/
```
* `str` 在 stack
* 字串內容在 heap
* 本體仍然是 指標
* 可以改內容
* 需要 `free(str)`
### static char str[] / global char str[],char array
```c
static char str[] = "dog";
// or
char str[] = "dog"; // global
/*
Data segment
d o g \0
*/
```
* 字串內容在 data segment
* 本體是 陣列
* 不在 stack、不在 heap
* 可以改內容
### char *str[] → func(char **s) 本體是:指標陣列(array of char*)
```c
// main
char *str[] = {"cat", "dog"};
func(str);
// func
void func(char **s);
/*
Stack
str[0] ──► 0x3000
str[1] ──► 0x3010
RODATA
0x3000 c a t \0
0x3010 d o g \0
*/
```
* `str` 本體在 stack
* 每個元素是 `char*`
* 指向的字串在 rodata
* 本體是 array
* 傳入 func 時退化成 `char** `
* 不可改字串值
### 總整理
| 宣告 | 本體 | 字串在哪 |
| -------------------- | ----- | ------ |
| `char *s = "dog"` | 指標 | rodata |
| `char s[] = "dog"` | array | stack |
| `char *s = malloc()` | 指標 | heap |
| `static char s[]` | array | data |
| `char *s[] = {...}` | 指標陣列 | rodata |
> 宣告看「[] 在哪」,使用看「退化後是什麼」
## reference 引用
```cpp
int b = 10;
int &ref = b; // 此時ref等同b這個變數 // 此寫法不是縮寫
```
- call by reference 是 call by pointer的捷徑
- call by call by pointer 是 call by reference的原理
- 因為call by pointer比較複雜,所以使用call by referece,以swap舉例
```cpp
// call by reference
int main()
{
int a = 10, b = 20;
cout << a << " " << b << n";
cout << &a << n";
swap(a, b);
cout << a << " " << b << n";
}
void swap(int & x, int &y)
{
cout << &x << n";
int temp = x;
x = y;
y = temp;
}
```
## c malloc & c++ new
```cpp
// malloc: C 語言函式,不會呼叫建構子
int* arr1 = (int*)malloc(5 * sizeof(int));
free(arr1);
// new: C++ 運算子,會呼叫建構子
int* arr2 = new int[5];
delete[] arr2;
// 以上宣告的是array [0, 0, 0, 0, 0]
```
- malloc
- 給左邊identifier的value一個地址
- 那個地址在Heap上取得一段指定大小的連續記憶體
- malloc 經常用來建立C陣列的array
- 用malloc建立陣列的優點是可以在程式跑的過程中決定大小
- 看不懂就列出`|address|indentifier|value|`
- Python:list 幾乎就是日常用的主要容器
- C:array 最常用,linked list 偶爾用
- C++:vector 幾乎取代了大部分需要 list 的情況,真正的 linked list (std::list) 很少用
### leetcode15 result 的用法
```c
int** result = (int**)malloc(sizeof(int*) * 5);
// result = [[], [], [], [], [],]
result[*returnSize] = (int*)malloc(sizeof(int) * 3);
// result = [[1, 2, 3], [1, 2, 3], [1, 2, 3], [1, 2, 3], [1, 2, 3],]
// result是5x3矩陣,5個row,3個column
result[*returnSize][0] = nums[i];
result[*returnSize][1] = nums[left];
result[*returnSize][2] = nums[right];
```
```c
int** result = (int**)malloc(sizeof(int*) * 5);
// 地址分配在result這個identifier的value
```
Stack(指標本身)
| address | identifier | value |
| ------- | ---------- | ----- |
| 0x080 | result | 0x100 |
Heap(5 個 int*)
| address | element | value |
| ------- | --------- | ----- |
| 0x100 | result[0] | ? |
| 0x108 | result[1] | ? |
| 0x110 | result[2] | ? |
| 0x118 | result[3] | ? |
| 0x120 | result[4] | ? |
```c
result[0] = (int*)malloc(sizeof(int) * 3);
// 地址分配在result[0]這個identifier的value
result[0][0] = 1;
result[0][1] = 2;
result[0][2] = 3;
```
| address | element | value |
| ------- | --------- | ----- |
| 0x080 | result | 0x100 |
| 0x100 | result[0] | 0x200 |
| 0x108 | result[1] | ? |
| 0x110 | result[2] | ? |
| 0x118 | result[3] | ? |
| 0x120 | result[4] | ? |
| 0x200 | result[0][0] | 1 |
| 0x204 | result[0][1] | 2 |
| 0x208 | result[0][2] | 3 |
- 最後因為是 int** threeSum,所以return result的value 0x100
## memory 記憶體
### 記憶體區域總覽
```
+-----------------------+
| Code Segment | <- 程式碼
+-----------------------+
| RO Data / Const | <- const 變數、字串
+-----------------------+
| Data Segment | <- 全域 / static 變數
+-----------------------+
| Heap | <- new / malloc 動態分配,額外增加的空間,只要是指標就能使用
+-----------------------+
| Stack | <- func內變數、參數
+-----------------------+
```
| 區域 | 存放對象 | 生命週期 | 常用語法 / 函式 |
| ----------------------- | ---------------- | ------------------------ | ---------------------------------- |
| **Stack(堆疊)** | 函式內的局部變數、參數、回傳地址 | 隨函式呼叫/結束自動分配與釋放 | `int x = 5;`、`char arr[10];` |
| **Heap(動態記憶體區)** | 動態分配物件 / 陣列 | 直到 `delete` / `free` 才釋放 | `new` / `delete`、`malloc` / `free` |
| **Data Segment (靜態區)** | 全域變數、static 變數 | 程式啟動到結束 | `int g = 10;`、`static int s = 5;` |
| **Code Segment (程式碼區)** | 程式本身的機器碼 | 程式啟動到結束 | 只讀,不可改寫程式碼 |
| **常數區 (RO Data)** | `const` 變數、字串常數,不可更改 | 程式啟動到結束 | `const int c = 5;`、`"Hello"` |
### Stack 詳細
* 優點:分配快速、自動管理
* 缺點:大小有限,過大可能造成 stack overflow
* 用法:
```cpp
void func() {
int x = 10; // 存在 stack
char buf[100]; // 存在 stack
}
```
### Heap 詳細
* 優點:大小彈性,可動態控制生命週期
* 缺點:分配慢,需要手動釋放,否則會記憶體洩漏
* 用法:
```cpp
// C++ 語法
int* p = new int(5); // 分配單一 int
int* arr = new int[10]; // 分配陣列
delete p; // 釋放單一 int
delete[] arr; // 釋放陣列
// C 語法
int* p2 = (int*) malloc(sizeof(int) * 10);
free(p2);
```
### Data Segment / 靜態區 / 全域 static 變數
```cpp
class Example {
public:
static int staticVar; // 宣告一個「靜態成員變數」
};
Example::staticVar = 300; // 可直接透過類別名稱存取
Example a, b;
a.staticVar = 400; // 也可透過物件名,但其實是同一個變數
cout << b.staticVar; // 會輸出 400
```
```cpp
int g = 100; // 全域變數 -> Data Segment
static int s = 50; // static 變數 -> Data Segment
void main() {
int x = 10; // 存在 stack
char buf[100]; // 存在 stack
}
```
* 全域變數、static 變數會在程式啟動時分配
* 程式結束自動釋放
### Code Segment(程式碼區)
```cpp
void func() {
// 函式指令儲存於 code segment
}
```
* 儲存程式機器碼
* 通常不可修改
### 常數區(只讀資料)
```cpp
const int c = 10; // 儲存在常數區
const char* str = "Hello"; // 字串常數 -> 常數區
```
* 儲存不可變的常數
* 防止程式修改
### 為什麼一定要用heap
```cpp
Stack:
enqueue() {
Node newNode --> 函式結束(只要return一次)就消失
}
Heap:
enqueue() {
Node* newNode = new Node --> 永遠存在
}
```
### 我如果都全域宣告,是否就能避免使用指標(heap)?
```c
int data = 10; // 全域變數
void func() {
data = 20; // 直接改!
}
// 這裡就不需要用:
void func(int *p) {
*p = 20;
}
```
在小型程式,很多人會為了簡單直接用全域變數取代指標傳遞
風險:
- 無法重複利用函式(失去模組化):
假設
```c
void sort() {
// sort global array
}
// 這只能排序你那個全域陣列,但如果你改寫成:
void sort(int *arr, int size) {
// sort 任何傳進來的陣列
}
```
- 可讀性與維護性差:如果程式有 10 個函式都在改 `global_data`,誰改過、何時改的,很難追蹤
- 執行緒安全(Thread Safety)
多執行緒同時操作全域變數會發生衝突(race condition),使用區域變數或用指標傳遞資料能避免互相干擾
## typedef vs. typedef stuck vs. stuck
- `typedef`定義宣告名稱
```c
typedef unsigned int u32;
u32 a = 100; // 等於 unsigned int a = 100;
```
```c
struct Point {
int x;
int y;
};
struct Point p1; // 必須加 struct
typedef struct Point {
int x;
int y;
} Point;
Point p2; // 不需要寫 struct
```
| 寫法 | 宣告變數方式 |
| -------------------------------------- | ---------------- |
| 只用 `struct node` | `struct node a;` |
| 加上 `typedef struct node { ... } Node;` | `Node a;` |
| 名稱 | 說明 |
| -------------------------- | -------------------- |
| `struct node` | 原始結構體型別名稱 |
| `typedef struct node Node` | 幫這個型別取個簡短名字 `Node` |
| `Node` | 就是 `struct node` 的別名 |
## 面試常見問題
### Swap Function 差別 (C 語言)
為什麼要呼叫指標swap才能work?
```cpp
#include <stdio.h>
// call by value -> 無法交換
void swap1(int a, int b) {
int temp = a;
a = b;
b = temp;
}
// call by pointer -> 可以交換
void swap2(int *a, int *b) {
int temp = *a;
*a = *b;
*b = temp;
}
int main() {
int x = 5, y = 10;
swap1(x, y);
printf("After swap1: x=%d y=%d\n", x, y); // 不會變
swap2(&x, &y);
printf("After swap2: x=%d y=%d\n", x, y); // x=10 y=5
}
```
A:參考c記憶體區域,呼叫值僅在swap函數內生效,僅在stack層生效,return後兩個數值又回歸原本的值,要呼叫指標才能更改heap層的數值
### C: `i++` `++i` 差異,python沒有
```c
// 後置遞增
int i = 0;
value = i++; // value = 0, i = 1
int i = 0;
value = ++i; // value = 1, i = 1
// 沒賦予變數沒差
i++;
++i;
```
### 實做 C str所有函式
```c
#include <stdio.h>
size_t my_strlen(const char *str) {
size_t len = 0;
while (str[len] != '\0') {
len++;
}
return len;
}
char* my_strcpy(char *dest, const char *src) {
char *p = dest;
while (*src != '\0') {
*p++ = *src++;
}
*p = '\0';
return dest;
}
int my_strcmp(const char *s1, const char *s2) {
while (*s1 && (*s1 == *s2)) {
s1++;
s2++;
}
return *(unsigned char*)s1 - *(unsigned char*)s2;
}
char* my_strcat(char *dest, const char *src) {
char *p = dest;
while (*p) p++; // 移到 dest 的結尾 '\0'
while (*src) *p++ = *src++;
*p = '\0';
return dest;
}
int main() {
char str1[50] = "Hello";
char str2[] = ", world!";
char str3[50];
printf("=== Testing my_strlen ===\n");
printf("Length of '%s' = %zu\n\n", str1, my_strlen(str1));
printf("=== Testing my_strcpy ===\n");
my_strcpy(str3, str1);
printf("Copied string: '%s'\n\n", str3);
printf("=== Testing my_strcmp ===\n");
char a[] = "abc";
char b[] = "abd";
printf("Compare '%s' and '%s' = %d\n", a, b, my_strcmp(a, b)); // -1
printf("Compare '%s' and '%s' = %d\n", a, a, my_strcmp(a, a)); // 0
printf("Compare '%s' and '%s' = %d\n\n", b, a, my_strcmp(b, a)); // 1
printf("=== Testing my_strcat ===\n");
my_strcat(str1, str2);
printf("Concatenated string: '%s'\n", str1);
return 0;
}
```
### C手刻vector
```c
#include <stdio.h>
#include <stdlib.h>
typedef struct {
int *data;
int size;
int capacity;
} Vector;
// 初始化
void init(Vector *v, int capacity) {
v->data = (int*)malloc(sizeof(int) * capacity);
v->size = 0;
v->capacity = capacity;
}
// 釋放記憶體
void free_vector(Vector *v) {
free(v->data);
v->size = 0;
v->capacity = 0;
}
// push_back
void push_back(Vector *v, int val) {
if (v->size == v->capacity) {
v->capacity *= 2;
v->data = (int*)realloc(v->data, sizeof(int) * v->capacity);
}
v->data[v->size++] = val;
}
// erase idx
void erase(Vector *v, int idx) {
if (idx < 0 || idx >= v->size) return;
for (int i = idx; i < v->size - 1; i++)
v->data[i] = v->data[i+1];
v->size--;
}
// resize
void resize(Vector *v, int new_size) {
if (new_size > v->capacity) {
v->capacity = new_size;
v->data = (int*)realloc(v->data, sizeof(int) * v->capacity);
}
v->size = new_size;
}
// 印出 vector
void print_vector(Vector *v) {
printf("[");
for (int i = 0; i < v->size; i++) {
printf("%d", v->data[i]);
if (i != v->size - 1) printf(", ");
}
printf("]\n");
}
int main() {
Vector v;
init(&v, 2);
push_back(&v, 10);
push_back(&v, 20);
push_back(&v, 30);
print_vector(&v); // [10, 20, 30]
erase(&v, 1);
print_vector(&v); // [10, 30]
resize(&v, 5);
print_vector(&v); // [10, 30, 0, 0, 0]
free_vector(&v);
return 0;
}
```
# 韌體0x10
## 預處理器 (Preprocessor):`#define`聲明
>用預處理指令 `#define` 聲明一個常數,用以表示一年中有多少秒 (忽略閏年問題)
```c
#define SECOND_PER_YEAR (365 * 24 * 60 * 60)UL
````
* `#define` 語法基本知識(不能以分號結束、括號的使用)
* 命名習慣:大寫 + 底線
* 預處理器會計算常數運算式
* 在 16-bit 系統上可能整數溢位,因此需要加 `L`
* 使用 `UL`(unsigned long)避免符號與大小問題
| 型態名稱 | 關鍵字 | 常見大小 (32/64-bit) | printf 格式化符號 |
| ------- | -------------- | ---------------- | ------------ |
| 字元 | char | 1 | %c |
| 無號字元 | unsigned char | 1 | %u |
| 短整數 | short | 2 | %d |
| 無號短整數 | unsigned short | 2 | %u |
| 整數 | int | 4 | %d |
| 無號整數 | unsigned int | 4 | %u |
| 長整數 | long | 4/8 | %ld |
| 無號長整數 | unsigned long | 4/8 | %lu |
| 單精度浮點數 | float | 4 | %f |
| 倍精度浮點數 | double | 8 | %f |
| 長倍精度浮點數 | long double | 12/16 | %Lf |
> 整數常數預設為 `int`,浮點數常數預設為 `double`。
> 若要避免溢位或明確指定型態,需加上後綴字:
| 後綴 | 意義 |
| ----- | --------- |
| U/u | unsigned |
| L/l | long |
| LL/ll | long long |
| F/f | float |
範例:
```c
unsigned int a = 10U;
long b = 10L;
float c = 3.14F;
long double d = 2.06L;
unsigned long e = 25UL;
```
## 預處理器 (Preprocessor):Macro 巨集 文字取代功能
>寫一個標準 `MIN` 巨集,輸入兩個參數並回傳較小的那個。
```c
#define MIN(X, Y) ((X) >= (Y) ? (X) : (Y))
```
* `#define` 用於巨集的基礎知識
* 三元運算子 `?:` 的使用
* 必須在巨集參數外加括號以防副作用
副作用範例:
```c
least = MIN(*p++, b);
// 會展開為:
// *p 可能被遞增一次或兩次
least = ((*p++) >= (b) ? (*p++) : (b));
// 解法:
int val = *p++;
least = MIN(val, b);
```
## 預處理器 (Preprocessor):`#error`的作用
```c
#error message
```
在編譯時輸出錯誤訊息並中斷編譯
範例:
```c
#include <stdio.h>
//#define NUM 3
int main(void) {
#ifndef NUM // if not defined
#error NOT define NUM
#endif
printf("NUM = %d", NUM);
return 0;
}
```
## 無窮迴圈 (Infinite loops)
嵌入式系統常見的無窮迴圈寫法:
```c
while (1) {
/* statement */
}
for (;;) {
/* statement */
}
```
## 資料宣告 (Data declaration)
| 類型說明(中文) | 類型說明(英文) | 宣告語法 |
| ----------------------------------------- | ----------------------------------------------------------------------------------------- | -------------------- |
| 一個整數 | An integer | `int a;` |
| 一個指向整數的指標 | A pointer to an integer | `int *a;` |
| 一個指向指標的指標,它指向的指標是指向一個整數 | A pointer to a pointer to an integer | `int **a;` |
| 一個有 10 個整數的陣列 | An array of 10 integers | `int a[10];` |
| 一個有 10 個指標的陣列,該指標是指向一個整數 | An array of 10 pointers to integers | `int *a[10];` |
| 一個指向有 10 個整數陣列的指標 | A pointer to an array of 10 integers | `int (*a)[10];` |
| 一個指向函式的指標,該函式有一個整數參數且回傳一個整數 | A pointer to a function that takes an integer as an argument and returns an integer | `int (*a)(int);` |
| 一個有 10 個指標的陣列,該指標指向一個函式,該函式有一個整數參數並回傳一個整數 | An array of ten pointers to functions that take an integer argument and return an integer | `int (*a[10])(int);` |
## static
`static` 有三種用途:
* 函式內的變數:函式結束後仍保留數值
* 模組內的全域變數:僅該檔案可存取
* 函式:僅限該檔案可呼叫
### `static` vs. `extern`
這是 **變數與函式作用域 (scope)** 的重要概念。
| 關鍵字 | 用途 | 範圍 / 壽命 | 使用場合 |
| ---------- | ---- | ---------------------- | ------------- |
| **static** | 靜態 | 只在定義的檔案或函式內可見(不能被外部使用) | 限制作用範圍、防止外部干擾 |
| **extern** | 外部宣告 | 告訴編譯器這個變數或函式在別的檔案定義 | 多檔案共用全域變數或函式 |
```c
// file1.c
static int count = 0; // 只在 file1.c 有效
void add() {
count++;
}
// file2.c
extern void add(); // OK,宣告函式
// printf("%d", count); // 無法使用 static 變數
```
```c
// file1.c
int value = 10; // 定義
// file2.c
extern int value; // 宣告
printf("%d", value); // 可讀取
```
| 關鍵字 | 儲存位置 | 作用範圍 | 可否被外部使用 |
| ------ | ----- | ---- | ------- |
| static | 全域變數區 | 檔案內 | ❌ |
| extern | 全域變數區 | 跨檔案 | ✅ |
## const
| 宣告| 意義 |舉例 |
| --- | --- | --- |
| `const int a;` | 整數常數 |
| `int const a;` | 整數常數 |
| `const int *a = &x;` | 指向整數常數的指標 (整數值不可改),綁住`*`a對指標來說是value|```*a = 15; 不能改變 a 所指的內容,a = &y; 可以改變 a 指向哪裡``` |
| `int *const a;` =`int *a`+`const` | 常數指標 (地址不可改) |
| `const int *const a;` | 皆不可改 |
> 小訣竅:從右往左解釋
## volatile
`volatile` 告訴編譯器:
>「這個變數可能在程式看不見的地方被改變,不要優化讀寫。」
如果沒有 `volatile`,編譯器可能會把變數讀一次放在暫存器,後續運算都用暫存器的值,可能錯過外部變化。
常見用途:
1. 週邊裝置暫存器
2. ISR 可存取的變數
3. 多執行緒共享變數
進階問題:
| 問題 | 答案 |
| ---------------------------- | --------------- |
| 可同時為 `const` 和 `volatile` 嗎? | 可以。例如唯讀暫存器 |
| 指標可為 `volatile` 嗎? | 可以,例如 ISR 修改指標 |
| 下列函式有何錯誤? | 語義錯誤,應先存區域變數再運算 |
```c
int square(volatile int *ptr) {
int temp = *ptr;
return temp * temp;
}
```
```c
#include <stdio.h>
#include <stdbool.h>
bool button_pressed = false;
volatile bool v_button_pressed = false; // 用 volatile
int main() {
// 模擬硬體中斷改變按鈕狀態
v_button_pressed = true; // 假設中斷改變這個值
button_pressed = true; // 假設中斷改變這個值
// 沒用 volatile 的情況
while (!button_pressed) { // 編譯器可能認為 button_pressed 永遠是 false,優化成無窮迴圈
// do nothing
}
// 用 volatile 的情況
while (!v_button_pressed) { // 每次都重新讀取,能正確跳出迴圈
// do nothing
}
printf("Button pressed!\n");
return 0;
}
```
* `button_pressed` 可能被編譯器優化掉,讀一次就不再讀。
* `v_button_pressed` 因為 `volatile`,每次迴圈都會重新讀取記憶體。
## Interrupt Service Routine ISR
```c
__interrupt double compute_area(double radius)
{
double area = PI * radius * radius;
printf("\nArea=%f", area);
return area;
}
```
錯誤原因:
- ISR 是硬體觸發的函式,像是按鈕按下、計時器溢位等事件
- ISR 不能有參數與回傳值
- 不建議使用浮點運算
- 不建議在 ISR 中使用 `printf`(可重入性問題)
正確 ISR 範例(C 語法簡化)
```c
#include <stdio.h>
#include <stdint.h>
volatile uint16_t timer_flag = 0;
void __interrupt timer_ISR(void) {
timer_flag = 1; // ISR 簡單操作,設定 flag
}
int main() {
// 模擬主程式
while (1) {
if (timer_flag) {
printf("Timer triggered!\n");
timer_flag = 0;
}
}
return 0;
}
```
* ISR 只做「簡單、快速」的操作,例如設定 flag
* 主程式讀 flag 去做複雜運算或輸出
### 中斷 Interrupt
中斷(Interrupt)= 一個「打斷CPU」的事件
- 鍵盤按下一個鍵 → 產生中斷 → CPU跳去執行「鍵盤處理程式」
- 定時器時間到 → 產生中斷 → CPU執行「定時器中斷處理程式」
8051 的中斷機制讓系統可以即時處理特定事件,而不必「一直輪詢 (polling)」去檢查事件是否發生
| 中斷名稱 | 含義 | 觸發方式 | 向量位址 |
| --------- | ---------- | --------------- | ------- |
| **IE0** | 外部中斷 0 | 可設成電平觸發或邊緣觸發 | `0003H` |
| **TF0** | 定時器 0 溢出中斷 | Timer0 overflow | `000BH` |
| **IE1** | 外部中斷 1 | 可設成電平觸發或邊緣觸發 | `0013H` |
| **TF1** | 定時器 1 溢出中斷 | Timer1 overflow | `001BH` |
| **RI/TI** | 串列通訊中斷 | 接收或傳送完成 | `0023H` |
## 位元操作 (Bit Operation)
- bitwise manipulation
- 往右邊8bit = 除以2的8次方
- 往左邊8bit = 乘以2的8次方
- 因為位移比乘法快很多,可以切換到bitwise的操作
- 把bit設為0,跟0做AND
- 把bit設為1,跟1做OR
- 微分做xor,有變化是1、沒變化是0
設定與清除 bit 3 (bit3 = 0000 1000 = 0x08):
```c
a = a | 0x08; // 設定a的bit3 = 1
a = a & (~0x08); // 清除a的bit3 = 0
```
更好的可移植寫法:
```c
#define BIT3 (0x1 << 3)
void set_bit3(void) { a |= BIT3; }
void clear_bit3(void) { a &= ~BIT3; }
```
> 需用指標轉型成 `int *` 才能存取指定位址
| 運算子 | 名稱 | 說明 | 範例 |
| - | - | - | - |
| `&` | AND(且)| 兩邊都為 1 才是 1 | `1100 & 1010 = 1000` |
| `\|` | OR(或)| 只要有一邊是 1 即為 1| `1100 \| 1010 = 1110` |
| `^` | XOR(互斥或) | 不同為 1,相同為 0 | `1100 ^ 1010 = 0110` |
| `~` | NOT(反相)| 0→1、1→0 | `~0101 = 1010` |
| `<<` | 左移 | 乘以 2ⁿ | `0001 << 2 = 0100` |
| `>>` | 右移 | 除以 2ⁿ | `1000 >> 3 = 0001` |
## `unsigned`,求result
```c
void foo(void)
{
unsigned int a = 6;
int b = -20;
(a + b > 6) ? puts(">6") : puts("<=6");
}
```
輸出:
```
>6
```
原因:`signed` + `unsigned` → 轉為 `unsigned`,導致 `b` 被轉為非常大的正數。
## `unsigned`
以下程式何者有錯?
```c
unsigned int zero = 0;
unsigned int compzero = 0xFFFF;
```
不同平台整數長度不同,在 32-bit 系統上此值為 `0x0000FFFF`
正確寫法:
```c
unsigned int compzero = ~0;
```
## 動態記憶體分配 (Dynamic Memory Allocation)
嵌入式系統中使用動態配置的問題:
* Heap 空間有限
* 碎片化 (fragmentation)
* 無法保證即時性
* 無法輕易釋放導致記憶體洩漏
## 存取固定記憶體位址
```c
int main() {
int *ptr = (int *)0x67a9;
*ptr = 0xaa55;
return 0;
}
```
## 遞增運算子 Increment Operator
```c
int a = 5, b = 7, c;
c = a+++b;
// 等價於:
c = (a++) + b;
```
結果:
```
a = 6;
b = 7;
c = 12;
```
* "maximum munch" 原則:盡可能解析最長合法 token
## typedef
```c
#define dPS struct s *
typedef struct s * tPS;
```
**建議使用 typedef**
**原因:**
```c
dPS p1, p2; // 展開為 struct s *p1, p2; (p2 不是指標)
tPS p3, p4; // 皆為 struct s * 類型
```
`typedef` 更安全且語義清楚
## 8051
### 8051 是什麼?
8051是 Intel 在1980年推出的一款8位元微控制器(Microcontroller, MCU)。
它是一顆「小型電腦晶片」,內部整合了 CPU、記憶體、I/O 埠、計時器與中斷控制器等模組。
> 8051 = 一顆可以執行程式、控制外部設備的「迷你電腦」
### 8051 的主要組成架構
| 模組| 功能說明 |
| - | - |
| CPU(Central Processing Unit) | 控制與運算核心|
| ROM(程式記憶體)| 儲存使用者程式(通常 4KB) |
| RAM(資料記憶體) | 儲存變數(128 Bytes) |
| I/O Ports | 4 組(P0~P3),可連接 LED、按鍵、感測器等 |
| Timer/Counter | 2 組計時器(Timer0、Timer1) |
| Serial Port | 串列通訊埠,可做 UART 通訊 |
| Interrupt System| 支援 5 個中斷源(外部、Timer、Serial) |
| Oscillator & Clock | 提供時脈訊號(通常是 12MHz)|
### 8051 與一般電腦的差別
| 項目 | 微控制器(8051) | 一般電腦(PC) |
| ---- | --------------------- | ------------------- |
| 構成 | 單晶片整合 CPU、記憶體、I/O | CPU、RAM、硬碟分開 |
| 目的 | 控制特定裝置(嵌入式系統) | 通用計算 |
| 執行環境 | 實時、無作業系統 | 有 OS(Windows/Linux) |
| 儲存容量 | 幾 KB ~ 幾十 KB | 幾十 GB 起跳 |
| 範例 | 洗衣機控制板、微波爐、電風扇、車用 MCU | 筆電、伺服器 |
### 8051 的記憶體架構
8051 採用「**哈佛架構(Harvard Architecture)**」:
程式記憶體(ROM)與資料記憶體(RAM)是**分開**的。
```
┌───────────────┐
│ ROM (程式區) │ ← 儲存程式碼
├───────────────┤
│ RAM (資料區) │ ← 儲存變數、堆疊
└───────────────┘
```
### 常見功能介紹(重點複習)
| 功能 | 簡介 | 範例 |
| -------------------------- | --------------- | ------------ |
| **I/O 控制** | 可控制 LED、按鍵輸入 | `P1 = 0xFF;` |
| **Timer/Counter** | 定時或計數外部事件 | 計算時間、週期性中斷 |
| **Interrupt(中斷)** | 即時處理事件 | 按鍵觸發 ISR |
| **UART 串列通訊** | 與 PC 或其他 MCU 溝通 | 傳送資料 |
| **位元操作 (Bit-addressable)** | 直接操作單一位元 | `SETB P1.0` |
### 常見面試 / 口試題目整理
| 類型 | 常見問題 | 你可以怎麼回答 |
| --------- | ---------------------- | ------------------------------------------------- |
| **基本概念** | 什麼是 8051? | 一種 8-bit 微控制器,內含 CPU、RAM、ROM、I/O、Timer、UART、中斷系統。 |
| **架構理解** | 8051 有幾個 I/O port? | 4 個:P0、P1、P2、P3。 |
| **記憶體** | RAM 與 ROM 的大小? | 128 Bytes RAM、4KB ROM(標準版本)。 |
| **中斷** | 8051 有幾個中斷來源? | 5 個中斷源(IE0、TF0、IE1、TF1、Serial)。 |
| **Timer** | Timer0 和 Timer1 有什麼作用? | 可作為計時器或事件計數器。 |
| **應用** | 給一個 8051 實際應用例子? | 電子時鐘、LED 閃爍、溫度感測控制器。 |
| **組語題** | MOV A, #55H 代表什麼? | 將立即數 55H 傳入累加器 A。 |
| **中斷題** | ISR 是什麼? | Interrupt Service Routine,中斷服務程式。 |
### 8051範例程式
```c
#include <reg51.h>
// 基礎延遲
void delay() {
unsigned int i, j;
for(i = 0; i < 1000; i++)
for(j = 0; j < 120; j++);
}
// UART:傳送單一字元
void uart_send(char c) {
SBUF = c;
while(TI == 0);
TI = 0;
}
// UART:傳送字串
void uart_send_string(char *s) {
while(*s) {
uart_send(*s);
s++;
}
}
// Timer0 中斷:LED 翻轉
void Timer0_ISR(void) interrupt 1 {
P1_0 = ~P1_0;
}
// 外部中斷 INT0:亮 LED
void External_INT0(void) interrupt 0 {
P1_0 = 0;
}
// LED 閃爍
void demo_led_blink() {
while(1) {
P1 = 0x00;
delay();
P1 = 0xFF;
delay();
}
}
// 點亮單一 LED(P1.0)
void demo_led_single() {
P1 = 0xFF;
P1_0 = 0;
while(1);
}
// 按鍵控制 LED(P3.2)
void demo_button_led() {
while(1) {
if(P3_2 == 0)
P1_0 = 0;
else
P1_0 = 1;
}
}
// Timer0 週期中斷
void demo_timer0_interrupt() {
TMOD = 0x01;
TH0 = 0xFC;
TL0 = 0x66;
ET0 = 1;
EA = 1;
TR0 = 1;
while(1);
}
// UART 傳送字串
void demo_uart() {
SCON = 0x50;
TMOD = 0x20;
TH1 = 0xFD;
TR1 = 1;
while(1) {
uart_send_string("Hello\n");
delay();
}
}
// 外部中斷 INT0
void demo_ext_int0() {
EA = 1;
EX0 = 1;
IT0 = 1;
while(1);
}
// 軟體 PWM(簡易)
void demo_pwm() {
while(1) {
P1_0 = 1;
_nop_(); _nop_();
P1_0 = 0;
_nop_(); _nop_();
}
}
void main() {
// 根據需要啟用其中一個功能:
// demo_led_blink();
// demo_led_single();
// demo_button_led();
// demo_timer0_interrupt();
// demo_uart();
// demo_ext_int0();
// demo_pwm();
// 預設跑 LED 閃爍
demo_led_blink();
}
```
# Python
## Python 常用的內建函式語法:
### `print`
```python
i, j, k = 3, 4, 5
print("i") #輸出i
print(i) #輸出3
# print 一次印多個數值
print(f"i={i}, j={j}, k={k}") #輸出i=3, j=4, k=5
print("i=", i, "j=", j, "k=", k)
print("i=" + str(i) + ", j=" + str(j) + ", k=" + str(k))
```
### `def` (要加冒號),傳值呼叫
( )內為函式內的變數,python雖然沒有括號,在函式內空格還是要對整齊,最後return的值,是函式最後的輸出
```python
def main(a, b):
#程式碼
return 0 #return為這個函式的輸出
```
Python定義任何函式都用def,C++定義函式要依據回傳值決定

leetcode 1.twoSum
```python
class Solution(object):
def twoSum(self, nums, target):
for i in range(len(nums)):
for j in range(i+1, len(nums)):
if nums[i] + nums[j] == target:
return i, j
nums = [2, 7, 11, 15]
target = 9
print(Solution.twoSum(1, nums, target))
```
### `return` vs `print`
retrun是func輸出的值,要用print印出來
### Python 讀取 Module,讀取同個資料夾內檔案名叫 `tool.py` 程式的四種方法
```python
import tool #第一種呼叫module法
print(tool.sum_mul(1, 2, 3, 4)) #要加.
import tool as t #第二種呼叫module法,改掉呼叫module的名稱
print(t.sum_mul(1, 2, 3, 4)) #要+.
from tool import sum_mul #第三種呼叫module法,指定呼叫某個def
print(sum_mul(1, 2, 3, 4))
from tool import * #第四種呼叫module法,全部呼叫,若主程式內有與module相同名稱的程式會有bug
print(sum_mul(1, 2, 3, 4))
```
### `if __name__ == "__main__":`,防止被引用時誤跑程式
```python
def sum_mul(a, b, c, d):
return (a+b+c)*d
if __name__ == "__main__":
print("直接執行時才會列印,可以防止被引用時執行")
if __name__ == "tool":
print("被引用時才會列印")
# __name__是一個python內部的變數,當程式直接執行時會變__main__,被引入時會變「檔案名稱」
# 使用if __name__ == "__main__":是為了防止被引入時執行內部的程式
```
#### 範例
```python
def main():
print("hello")
if __name__ == "__main__": #防止被引用時直接執行
main()
```
### `map` 大量宣告 `int`
```python
# map(函式, 可迭代物件) 是一種簡潔的「批次運算」方式
map(int, …)
map(float, …)
num1 = "123"
num2 = "456"
result = num1 + num2
print(result) # 輸出 "123456",而不是整數相加的結果
num1 = int(num1) # 將字符轉換為整數
num2 = int(num2) # 將字符轉換為整數
result = num1 + num2
print(result) # 輸出整數相加的結果,即 579
```
#### 用map一次對一個變數要用list外包
```python
num1 = "123"
num2 = "456"
num1 = list(map(int, [num1])) #因為用list,所以是列表,值存在num1[0]
num2 = list(map(int, [num2])) # 將字符轉換為整數並放入列表
result = num1[0] + num2[0] # 從列表中提取整數值並相加
print(result) # 輸出整數相加的結果,即 579
num1, num1 = map(int, [num1, num1]) # 用map的多重賦值特性,達成一次對一個變數不用list
result = num1 + num2
print(result) # 輸出整數相加的結果,即 579
```
### `stored`
```python
sorted(iterable(遞迴物), key=函數) # 有點像.sort
print(sorted([5, 3, 9, 1])) # [1, 3, 5, 9]
```
### `lambda` 匿名函式
- `lambda func`匿名函式:一行內建的小函式,可以在 `sorted()`、`map()`、`filter()` 等地方使用,不用額外`def`
- `lambda` 參數1, 參數2, ... : 表達式、運算結果

```python
add = lambda x, y: x + y
print(add(3, 5)) # 8
data = [('apple', 5), ('banana', 2), ('cherry', 8)]
sorted_data = sorted(data, key=lambda x: x[1]) # 按數字大小排序
print(sorted_data)
nums = [1, 2, 3, 4]
squared = list(map(lambda x: x ** 2, nums))
print(squared) # [1, 4, 9, 16]
nums = [1, 2, 3, 4, 5, 6]
even_nums = list(filter(lambda x: x % 2 == 0, nums))
print(even_nums) # [2, 4, 6]
from functools import reduce
nums = [1, 2, 3, 4]
product = reduce(lambda x, y: x * y, nums)
print(product) # 24 (1*2*3*4)
data = ['apple', 'banana', 'cherry']
sorted_data = sorted(data, key=lambda x: x[1])
# 用每個字串的 第 2 個字元 來排序
# ['banana', 'cherry', 'apple']
```
### 輸入以空格相間的列表
```python
user_input = input("請輸入一些文字: ") #等待用戶輸入一行以空格分隔的數字或單詞
words = user_input.split() #.split是將字串轉成list,"hello world" →["hello", "world"]
print(words)
for word in words:
print(word) # 打印分割後的單詞列表
data = [int(x) if x.isdigit() else x for x in words]
print(data)
```
### 輸入多個資料 `input().split()` + `map(int, …)` + `list`
```python
x, y = map(int, input("輸入兩個整數(以空格分隔): ").split())
print(f"{x},{y}")
numbers = list(map(int, input("輸入多個整數(以空格分隔): ").split())) #比上面多一個list
print(numbers)
floats = list(map(float, input("輸入多個浮點數(以空格分隔): ").split()))
print(floats)
words = input("輸入多個字串(以空格分隔): ").split()
print(words)
```
### enumerate
使迴圈變比較好寫,第一個i表示編號,第二個j表示值
```python
data = ['a', 'b', 'c', 'd', 'e']
for index, obj in enumerate(data):
print(f"Index: {index}, Value: {obj}")
strs = ["flower","flow","flight"]
for i, char in enumerate(strs[0]): # 以第一個flower為基準
for word in strs: # 依序比對所有字元的i=1、i=2、i=3等
# 超出字串範圍,或字串不同,直接結束返回結果
if i >= len(word) or word[i] != char:
return ans
```
### List Comprehension(列表生成式)
```python
squares = [x**2 for x in range(10)]
```
>用一行簡潔的語法產生新 list,常取代 for 迴圈。
輸入 (Input): `range(10)`
輸出 (Output): `[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]`
可加條件判斷,例如 `[x for x in range(10) if x % 2 == 0]`
效率高、語法簡潔
* Generator / yield(生成器)
```python
def gen():
for i in range(3):
yield i
for x in gen():
print(x)
```
>建立可逐步產生值的迭代器,節省記憶體
輸入 (Input):** 無
輸出 (Output):** 依序輸出 `0, 1, 2`
`yield` 暫停函式狀態,下次繼續執行,有點像建立好一個list
適合大資料流,例如逐行讀檔
### Decorator(裝飾器)
```python
def timer(func):
def wrapper():
print("Start")
func()
print("End")
return wrapper
@timer
def hello():
print("Hello World")
hello()
```
>在不修改原函式的情況下,額外包裝功能
可以用在記錄執行時間、登入檢查、快取控制
輸入 (Input): 呼叫 `hello()`
輸出 (Output):
```
Start
Hello World
End
```
### Context Manager(上下文管理器)
```python
with open('file.txt', 'r') as f:
data = f.read()
```
>自動處理資源開啟與釋放(例如檔案)。
輸入 (Input): `'file.txt'`
輸出 (Output): 讀取到的檔案內容字串
不需手動 `close()`
即使出錯也會自動釋放資源
## 數學有關的語法:
### 整數的大於、等於、小於
- 整數中 `i+1 >9` 同 `i>=9`
- 整數中 `10>9` 同 `10-1>=9`
### 除法取整數 `//`,除法取餘數 `%`
- 除法取整數 `t = 10//3`
- 除法取餘數 `t = 10%3`
### 奇偶判斷,`x % 2 == 0`(例:x 10783)
```python
if x % 2 == 1: # 奇數
if x % 2 == 0: # 偶數
```
### 找數列中最大值 `max(list)`
```python
num = [(1,2.5),(1.5,3.2),(1.3,4.0),(2.2,1.8)]
y, z = max(num, key=lambda x:x[0])
print(y,z)
a, b = max(num, key=lambda x:x[1])
print(a, b)
```
### 累加縮寫 `a += b`
`a += b` 同 `a = a + b`
### 印小數點(`format`、`round`、`f"{:.2f}"`)
印到小數點第三位`print(f"{age:.3f}%")`
### 輸入變數後指定變數
```python
t = int(input("請輸入一個數字: "))
name = input("請輸入你的名字: ")
print("你好," + name)
```
常用在輸入指定變數後決定迴圈數量
```python
t = int(input()) # int表示把值變成可計算
for case in range(1, t + 1):
```
### 兩數值交換
```python
c = a # 口 ← A
a = b # A ← B
b = c # B ← 口
# 簡寫為
a, b = b, a
```
```python
a, b = b, a % b
```

### 歐幾里得找最大公因數(`math.gcd`)
- 歐幾里德演算法參考https://hackmd.io/@donglinwu/HyOMwBhfY
- 每次都會有三個數字,a, b, a%b,選其中最小的兩個繼續做,新a,新b,新a%新b,直到某a%某b = 0 為止
- 如 a=120, b=1512
- round1, a = 120, b = 1512,a%b = 120,此時120與1512的最大公因數跟1512與120的最大公因數相同
- round2, a = 1512,b = 120 ,a%b = 72,此時120與1512的最大公因數跟120與72的最大公因數相同
- round3, a = 120, b = 72, a%b = 48,此時120與1512的最大公因數跟72與48的最大公因數相同
- round4, a = 72, b = 48, a%b = 24,此時120與1512的最大公因數跟48與24的最大公因數相同
- round5, a = 48, b = 24, a%b = 0,此時120與1512的最大公因數同(48 ,24),最大公因數是24
```python
a = 120
b = 1512
def find_gcd(a, b):
while b:
a, b = b, a % b
print(f"a = {a},b = {b}")
print(a)
return a
find_gcd(a, b)
```
## 匯入外部函式:
### `matplotlib.pyplot`
```python
import matplotlib.pyplot as plt
plt.plot(x, y) # 繪製折線圖(連續的線條)
plt.stem(x, y) # 繪製桿狀圖(垂直線條,底部連接 x 軸,上方有圓點)
plt.bar(range(0, 256), y) # 繪製條形圖(每個區間以矩形表示)
b.imshow(image, cmap='gray') # 加入圖片image,gray是顯示灰階圖,沒這行不會有圖片
plt.figure(figsize=(width, height)) # 設置 Matplotlib 圖形的尺寸大小
plt.xlim([0, 12]) # 設一個大概的x範圍
plt.ylim([-60, 140])
plt.title(label) # 設置 Matplotlib 圖形的標題
plt.legend(labels) # 添加 Matplotlib 圖例,用於標識不同的數據序列或圖形
plt.xlabel("年資") # 設定x軸名稱
plt.ylabel("月薪(千)")
plt.savefig('照片名稱') # 放在show前面就會儲存照片
plt.show() # 顯示 Matplotlib 圖形,將之前設置的圖形顯示出來
plt.figure() # 畫一張大圖放好幾個子圖
plt.suptitle(標題)
# 加入第一個子圖
plt.subplot(1, 2, 1) # 這個子圖要放在哪裡,plt.subplot(總共的row, 總共的column, 位子)
plt.axis('off') # xy軸不要出現
plt.imshow(A, cmap="gray") # 加入圖片A,gray是顯示灰階圖,沒這行不會有圖片
# 加入第二個子圖
plt.subplot(1, 2, 2)
plt.axis('off')
plt.imshow(B, cmap="gray")
plt.savefig(f'HW4_{filename1} and {filename2}')
plt.show()
'''
plt.subplot(2, 2, 1) # 左上角的子圖
plt.subplot(2, 2, 2) # 右上角的子圖
plt.subplot(2, 2, 3) # 左下角的子圖
plt.subplot(2, 2, 4) # 右下角的子圖
'''
# 使用 fig.add_subplot() 函數來添加子圖
for i in range(4):
fig = plt.figure(figsize=(12, 8))
for j in range(7):
output_image = together[i][j]
if j == 0:
fig.add_subplot(n, m, 2)
plt.imshow(output_image, cmap='gray')
plt.title("{}\nSNR = {}".format(together_title[i], SNR(img, output_image)))
else:
fig.add_subplot(n, m, j + 3)
plt.imshow(output_image, cmap='gray')
plt.title("{} SNR = {:.6f}".format(title[j - 1], SNR(img, output_image)))
plt.xticks([])
plt.yticks([])
plt.savefig(os.path.join(output_directory, f'HW8_image_{together_title[i]}.jpg'))
plt.show()
```
---
### `numpy`
```python
import numpy as np
# 陣列(array):
# 矩陣的標準用法
A = np.mat([
[1, 1, 1, 1],
[2, 2, 2, 2],
[3, 3, 3, 3]
])
# 用陣列也可以代表矩陣 # 上面是3x2矩陣,下面是2x3矩陣
aaa1 = np.array([np.array([-0.707, 0.707]),
np.array([0.8,0.6]),
np.array([ 0.,-1.])])
aaa2 = np.array([[-0.707, 0.8, 0],
[0.707, 0.6, -1]])
a_array = np.array() # 函式創建數組,例如 arr = np.array([1, 2, 3, 4, 5])
a_array = np.zeros_like(A) # 函式創建與A同大小,元素全為零的矩陣
a_array = np.zeros() # 函式創建元素全為零的數組,例如 zeros_arr = np.zeros((3, 3)),np.zeros((列, 行))
a_array = np.ones() # 函式創建元素全為一的數組,例如 ones_arr = np.ones((2, 4))
a = np.round(a_array, decimals=6) # 取小數點到第六位,數字會變比較漂亮
# 兩種矩陣乘法
c = a @ b
c = np.dot(a, b)
A = np.array([[-0.707, 0.8, 0], [0.707, 0.6, -1]] # row在台灣是列,在大陸、日本是行
a_array = np.split(A, A.shape[1], axis=1) # np.split(切割A, 切割多少份, 由哪裡切),
# A.shape[1]是行數 = 3,A.shape[0]是列數 = 2,A.shape = (2, 3)
# axis = 0、1、2對應x、y、z,在此僅能用axis = 1由行切割
b_array = np.concatenate(a_array, axis=1) # 把分開的矩陣合在一起
max_idx = np.argmax(np.abs(w)) # np.abs(w),找w的最大值,argmax找w的最大值在矩陣中的位子
Anew[:, max_idx] = b_array[max_idx][:, 0] # 參考7.5 #[:, 0]是[列,行],冒號是選擇全部,所以[:, 0]是指定第0行
# 這邊b_array是由concatenate來的,由好幾列(口,口)疊在一起,只能用[max_idx][:, 0]的方式去找,
# 其中[max_idx]是取列,測其他數字都沒用
Anew[:, max_idx] = 某數列,某數列加入到Anew的行裡面
a_inv = np.linalg.pinv(a) # 取a的反矩陣
# 數組屬性:
array.shape # 獲取數組的形狀,例如 (3, 3)
array.dtype # 獲取數組的數據類型,例如 int64
array.T # array的T轉換
# 數組索引和切片:
arr[index] # 獲取數組中指定索引位置的元素,例如 arr[0] 獲取第一個元素。
arr[start:end] # 進行切片操作,獲取指定範圍的元素,例如 arr[1:4] 獲取第二個到第四個元素。
# 數學函式:
np.sum() # 計算數組中元素的和,例如 total = np.sum(arr)。
np.mean() # 計算數組中元素的平均值,例如 avg = np.mean(arr)。
y = np.exp(x) # 計算e^x次方,例如x = np.arange(-5, 5, 0.1),y = np.exp(x)
y = np.log(x) # 計算log(x),x = np.arange(1, 10, 0.1),y = np.log(x)
y = np.sin(x) # 計算sin(x),x = np.arange(0, 2 * np.pi, 0.1),y = np.sin(x)
np.fft.fft(x) # 計算fourier transform,time = np.array([0, 1, 2, 1, 0]),frequency = np.fft.fft(time)
np.fft.ifft(x) # 逆fourier transform,frequency = np.array([0, 1, 2, 1, 0]),time = np.fft.ifft(frequency)
# 矩陣運算:
np.dot() # 執行矩陣乘法,例如 result = np.dot(matrix1, matrix2)
np.transpose() # 獲取矩陣的轉置,例如 transposed_matrix = np.transpose(matrix)
```
---
### `OpenCV`
#### 112-1傅楸善CV HW只有hw3.5.8.有用到plt.savefig
```python
import cv2 as cv
img = cv.imread('lena.bmp') # 讀取跟.py檔同個資料夾名稱為lena.bmp的照片,並傳送到img這個位子
img = cv.resize(img, (1000, 600)) # 改變相片長與寬,比例可能會跑掉
cv.imwrite(image_eq) # 存照片到跟.py檔同個資料夾
cv.imshow('photo', img) # 輸出照片叫photo,從img內輸出;按enter可以關掉,plt.show不行
cv.imwrite('HW3_part(b)_divided_by_3.jpg', image2_new)
cv.waitKey(0) # 鍵盤訊號或等待框內時間後關閉,0表示無限時間,1000表示1秒,plt.show不用刻意+這個
```
---
### `OS`
```python
import os
output_directory = 'results'
os.makedirs(directory, exist_ok=True) # 等效以下
if not os.path.exists(output_directory):
os.makedirs(output_directory)
cv.imwrite(os.path.join(output_directory, 'equalized_image.jpg'), image_eq) # 一定要+ cv.waitKey(0)一張一張存
plt.savefig(os.path.join(output_directory, f'HW8_{together_title[i]}.jpg'))
plt.savefig(os.path.join(output_directory, 'HW7_image.jpg'))
# 等同以下
cv.imwrite('results/dif_gaussian.jpg', dif_gaussian) # 沒result資料夾,會自動建立,可以一次存很多張照片
plt.savefig('results/HW7_image.jpg') # 如果沒result資料夾,程式會卡住
```
## 資料處理相關的外部函式:
### 資料處理的流程
```python
input_file_name = 'P0001'
input_dir = f'./data/{input_file_name}'
output_dir = './ data_output'
output_csv_file = f'./{output_dir}/output.csv'
variable1 = 1
variable2 = 2
variable3 = 3
def function1():
def function2():
```
以下是我在撰寫 Python 時的習慣與原則:
* 盡可能不使用 `def` 函式定義
* 不使用 `def process():`
* 不使用 `if __name__ == "__main__"`
* 把input/output路徑變數放在程式最上方
* Jupyter Notebook (`.ipynb`) 很方便,但我仍偏好標準的 `.py`
### 讀取資料夾內所有檔案
處理每張圖片
```python
input_folder = f"datasets/test" # 原始圖片路徑
for file_name in photo_files:
file_path = os.path.join(input_folder, file_name)
"""
檔案處理,如predict、解壓縮等,input是file_path
"""
```
處理所有檔案
```python
directory = "."
directory = os.path.abspath(directory) # 確保路徑為絕對路徑
os.makedirs(directory, exist_ok=True) # 確定有此資料夾
for file_name in os.listdir(directory): # 遍歷目錄下所有檔案
file_path = os.path.join(directory, file_name)
"""
檔案處理,如predict、解壓縮等,input是file_path
"""
```
### `pandas`(讀取 `csv`)
```python
### 智慧醫療程式設計 HW5-1的輸出圖片
import pandas as pd
data = pd.read_csv('./100.csv') #讀跟.py檔案同個資料夾內的excel
print(data.columns, data.shape) #data.columns是列的數量,data.shape是行的數量 #確認有讀到資料
import matplotlib.pyplot as plt
def ecg_peak(data):
plt.figure(figsize=(13, 5)) #設定圖形大小
plt.plot(data) #繪製折線圖
plt.show() #顯示圖形
ecg_peak(data["'MLII'"][:1000])
ecg_peak(data["'MLII'"][11000:12000])
ecg_peak(data["'MLII'"][51000:55000])
### 讀excel跟網路上的csv
import pandas as pd
data = pd.read_csv("https://raw.githubusercontent.com/GrandmaCan/ML/main/Resgression/Salary_Data.csv")
### 410Lab讀mimi3的csv
df = pd.read_csv("./60_people.csv")
row = 1
gender = df.loc[row - 1, 'gender'] # 讀取第0 row的gender這個項目,第一行項目名稱不算row
age = df.loc[row - 1, 'age']
death = df.loc[row - 1, 'death']
loadPath = df.loc[row - 1, 'loadPath']
### 112-2數位訊號處理導論
import pandas as pd
data = pd.read_csv('stock_price_globe.csv')
data.head()
# 選擇最後 5 個日期的資料
last_5_data = data.loc[11390:11395] # loc就是row,把第二列當0,實際範圍[0:14651]
# 計算對數收益率
adj_close = last_5_data['Adj Close']
adj_close_shifted = adj_close.shift(1) # 取得上一期的 adj_close
# 計算對數收益率
log_returns_Q1 = np.log(adj_close / adj_close_shifted)
#Q1 顯示最後 5 個日期的對數收益率
print("\nLog Returns of Last 5 Dates in Taiwan:")
print(log_returns_Q1[1:].values) # 不印出索引為 11390 的結果
```
### `json`
### `openpyxl`
## 範例程式:
### CPE 程式範本
```python
# 輸入用到的變數
t = int(input()) #跑幾個迴圈 # 輸入一個整數
for case in range(1,t+1): #跑1到t個迴圈
data = list(map(int, input().split())) # 輸入一個字串 # 1 2 3 4 5 # 讀取多個數字並轉換為整數列表
string = input() # 讀取單行字串
x, y = map(int, input().split()) # 輸入兩個整數以空格隔開 # 5 10
a, b, c = map(int, input().split()) # 輸入三個整數以空格隔開
int n; // 讀取輸入的行數
cin >> n; // 對應 t = int(input())
string s;
getline(cin, s); // 清除緩衝區的 '\n',避免影響後續輸入 // 對應string = input()
# 列表lis # list可以指定位子,如s[1][2]
s = [] # 初始化空列表list來存放輸入的每一行
while True:
try:
s.append(input()) # 逐行加入到s
except:
break
# 字串
number = ' '
number += '1'
number += '0'
# 數列,1D_array,可以指定位子,如number[2]
number = [0]*3
for i in range(len(number)):
number[i] = i
# 2D 陣列 # 可以指定位子,如arr_2d[1][2],python中list更靈活更常用
arr_2d = np.array([[1, 2, 3], [4, 5, 6]])
arr_2d[0][0] = 10
# 常用的非輸入變數
count = 0 # 要計算某次數,放在迴圈外面
for i in range( ):
count += 1
max_cols = max(max_cols, len(s[i])) # 一直更新最大值
# 迴圈範例
for case2 in range(a): # for迴圈
b+=1
while a>b: # while條件內迴圈
c+=1
while True: # while設定條件結束迴圈
a+=1
if 5>=a>0: # if用法
print("so small")
elif a==6:
print("mid")
else:
print("big")
if a==10: # 條件必須用==
print(data[1])
print(f"hello,world{a},I like {b}")
break
```
### `numpy` 實例
```python
### 程式1
import numpy as np
b_list = np.array([np.array([[-0.707],[ 0.707]]),
np.array([[0.8],[0.6]]),
np.array([[ 0.],[-1.]])])
Anew = np.array([[0., 0., 0.],
[0., 0., 0.]])
Anew[:, 1] = b_list[1][:, 0]
print(f"{b_list}\n{b_list[0][:, 0]}\n{Anew}")
### 程式2
import numpy as np
x = np.array([[1,2,3],
[4,5,6]])# array盡量不和matrix混用,
#row列操作
a = x[0] #取第一列 [[1 2 3]] row在台灣是列,在大陸日本是行
print(f'x[0] is {a}\n')
b = x[1] #取第二列 [[4 5 6]]
print(f'x[1] is {b}\n')
c = x[1,0:2] # 索引取值,取第二列,1元素 [[4 5]]
print(f'x[1,0:2] is {c}\n')
#column行操作
d = x[ :,0] #取第一行
print(f'x[ :,0] is\n {d}\n')
'''
[[1]
[4]]
'''
e = x[:,:2] #取前两行
print(f'x[ :,:2] is\n {e}\n')
'''
[[1 2]
[4 5]]
'''
f = x[:,-1]#取最后一行
print(f'x[ :,-1] is\n {f}\n')
'''
[[3]
[6]]
'''
g = x[:,1:3]#取第2-3行
print(f'x[ :,1:3] is\n {g}\n')
'''
[[2, 3],
[5, 6]]
'''
h = x[0:2,:]#取1-2列
print(f'x[0:2,:] is\n {h}\n')
'''
[[1, 2, 3],
[4, 5, 6]]
'''
```
## 程式環境:
### IDE、compiler、interpreter
#### IDE:集成開發環境(Integrated Development Environment)
* 功能強大的記事本,撰寫、排版、除錯與執行程式的完整工具
* 常見 IDE:
* PyCharm
* VSCode
* Visual Studio
#### Compiler:編譯器
* 將原始碼編譯成可執行檔(例如 `.exe`)
* 適用語言:C、C++、C#、Java(編譯成 JVM bytecode)
* 常見 C/C++ 編譯器:
* GCC
* Clang
* 安裝方式:可透過MinGW或CodeBlocks取得
#### Interpreter:解譯器
* 將程式逐行解釋執行
* 適用語言:Python、JavaScript、R 等。
* 常見解譯器:
* Python 解譯器
* Node.js(JavaScript 執行環境)
#### 系統環境變數 PATH 設定
* 不論是編譯器或解譯器,都必須將其路徑加入系統環境變數的PATH
* 目的:讓 IDE 或 Terminal 能在任何資料夾直接找到對應的執行檔(如 `gcc`, `python`)。
#### 常見路徑範例:
* C/C++ 編譯器路徑:
```txt
C:\mingw64\bin
C:\Program Files\CodeBlocks\MinGW\bin
```
* Python 解譯器路徑:
```txt
C:\Users\User\AppData\Local\Programs\Python\Python311\
```
### Python interpreter 設定
#### pycharm創立新環境
##### 選項 1:New environment(建立新環境)
* 在專案資料夾內會自動創建一個 `venv` 子資料夾。
* 該資料夾即為虛擬環境,專案所需套件將安裝在此,不會影響其他專案。
##### 選項 2:Previously configured interpreter(使用現有的 Interpreter)
* 路徑範例:
```
C:\Users\User\AppData\Local\Programs\Python\Python311
```
* 好處:
* 之後新建的專案可以共用相同的 Interpreter。
* 統一管理套件與環境設定,避免重複安裝。
* 換電腦時,只需將該 Interpreter 資料夾整個複製到新電腦上即可繼續使用。

#### 已經建立好project想改環境
* 使用 pip 安裝的套件,會依據所選 Interpreter 對應安裝到指定路徑。
* 專案建立後,也可以進入 `Settings > Project: 專案名稱 > Python Interpreter` 修改 Interpreter 設定。

### C++ vscode、vs2019 設定
#### Visual Studio 2019(VS2019)
* VS2019 在建立專案時,會在資料夾中自動建立 `.vs` 資料夾
* `.vs` 裡面包含設定檔(如 `.json`)來儲存 project 設定
* **可以透過 GUI 設定 include path 和 linker 選項**,不需手動編輯 `.json`
* 這些設定完成後,系統會自動產生對應的 `.json` 設定檔
* 已於2024/02/29在臺大完成設定測試
#### Visual Studio Code(VSCode)工作區概念
* VSCode使用工作區(Workspace)管理專案設定
* 建立工作區會產生 `.vscode/` 資料夾,內含下列重要檔案:
| 檔案名稱 | 說明 |
| --------------- | --------------------------- |
| `settings.json` | VSCode 的基本環境設定(如字體大小、換行寬度等) |
| `launch.json` | 設定除錯器的執行參數與目標檔案 |
| `tasks.json` | 設定自動化任務,例如編譯程式、執行指令腳本 |
* 這些 `.json` 檔可透過 VSCode 介面設定,也可手動編輯,方便搬移設定。
#### 是否需要使用工作區?
* 如果你主要在 Terminal 執行程式,就不太需要設定 `.vscode` 裡的 `.json`
* 使用 Terminal 執行程式是最直接、簡單的方式
* 你也可以安裝套件(如Extension Pack、Code Runner)來執行程式:
* 但這些套件執行時無法正確處理 `input()` 輸入類型的程式

#### 使用 Terminal 指令執行 C / C++ 程式
> 執行 C 程式
```bash
gcc name.c -o name; ./name.exe
```
> 執行 C++ 程式
```bash
g++ -o test test.cpp; ./test
```
### Verilog 環境
#### 方法一:使用 Intel Quartus Prime(含波形顯示)
詳細使用步驟與圖示請參考另一份 Word 文件。
#### 方法二:使用 VSCode 搭配 Icarus Verilog + GTKWave
* 使用VSCode作為文字編輯介面。
* 下載並安裝 [Icarus Verilog](http://bleyer.org/icarus/)(內含 `iverilog`、`vvp`、`GTKWave`)。
#### 專案檔案結構
將以下兩個檔案放在同一資料夾:
* `test.v`:主設計檔
* `test_tb.v`:測試檔(testbench)
> test\_tb.v 需額外加入以下程式碼以產生波形檔:
```verilog
initial begin
$dumpfile("test_tb.vcd"); // 產生 vcd 波形檔
$dumpvars(0, counter_tb); // 設定 testbench 模組名稱
end
```
> 執行步驟(在 Terminal 中操作)
```bash
iverilog -o test.bar test.v test_tb.v # 編譯兩個檔案成一個可執行檔
vvp test.bar # 執行模擬,產出 test_tb.vcd
gtkwave test_tb.vcd # 使用 GTKWave 開啟波形
```
# Reference
- [Hello Algo](https://www.hello-algo.com/zh-hant/)
- [GeeksforGeeks](https://www.geeksforgeeks.org/)
- [Leetcode](https://leetcode.com/)
- [Collegiate Programming Examination](https://cpe.mcu.edu.tw/)
- [HackerRank](https://www.hackerrank.com/)
- [韌體0x10](https://hackmd.io/@ChenZE/ByOoO0LEee)