# 緯穎科技 - BMC 韌體工程師
## 考題
### 1. 位元操作
給一個整數變數 a,寫出兩段程式碼。第一個要設定 a 的 bit 4,第二個要清除 a 的 bit 4。在以上兩個操作中,要保持其它位不變。
```c
#define SET_BIT(a, n) ((a) |= (1 << (n)))
#define CLEAR_BIT(a, n) ((a) &= ~(1 << (n)))
```
:::info
來源:[韌體工程師的0x10個問題](https://hackmd.io/@Chienyu/S1loEqCuo)
:::
### 2. 中斷(interrupt)
典型的,這個新的key word是__interrupt。以下的程式碼使用__interrupt來定義一個中斷service routine。評論這個程式碼:
```c
__interrupt double compute_area(double radius)
{
double area = PI*radius*radius;
printf("\nArea=%f", area);
return area;
}
```
這個函數有太多錯誤了,問題如下:
- ISR 不能返回一個值
- ISR 不能傳遞參數
- 在許多編譯器/處理器中,浮點數操作是不可重入的(re-entrant)。有些處理器/編譯器需要讓多餘的暫存器入棧(PUSH入堆疊),有些處理器/編譯器就是不允許在 ISR 中做浮點運算。此外,ISR應該是短而有效率的,在 ISR 中做浮點運算是不明智的。
- 與第三點類似,printf 通常會有可重入和效能的問題。
:::info
來源:[韌體工程師的0x10個問題](https://hackmd.io/@Chienyu/S1loEqCuo)
:::
### 3. 0xFFFF 和 ~0 的差異
評論以下的程式碼片段?
```c
unsigned int zero = 0;
unsigned int compzero = 0xFFFF;
/* 1's complement of zero */
```
:::spoiler 對於一個 int 型**不是 16 位元**的機器來說,`0xFFFF` 將會導致錯誤。
`unsigned int compzero = 0xFFFF;`
- 是一個 16 位元的十六進位數值,對應的二進位表示是 `1111 1111 1111 1111`。
- 在 unsigned int(通常是 32 位元)中,這個值會被擴展為 `0x0000FFFF`(即 `0000 0000 0000 0000 1111 1111 1111 1111`)。
`unsigned int compzero = ~0;`
- `0` 的二進位表示是 `0000 0000 0000 0000 0000 0000 0000 0000`(假設 `unsigned int` 為 32 位元)。
- `~0` 代表對 0 取反,變成 `1111 1111 1111 1111 1111 1111 1111 1111`,即 `0xFFFFFFFF`。
:::
正確的程式碼如下:
```c
unsigned int compzero = ~0;
```
這個問題真的可以知道應試者是否了解字組長度在電腦的重要性。在我的經驗中,好的嵌入式程式設計師會嚴謹的知道硬體的細節與它的限制,然後電腦程式設計師傾向忽視硬體並把它視為一個必要的煩擾。
:::spoiler [相關題目](https://hackmd.io/fZ7elUOWQ8SbaEJW5b09nw)
**What Is The Output Of this program?**
```c
#include <stdio.h>
int main()
{
unsigned int a = 0xffff;
unsigned int k = ~a;
printf("%d %d\n", a, k);
return 0;
}
```
**Answer**
Output: 65535 -65536
**Explaination:**
`a` 為 `0x0000ffff`,因此 `k` 為 `0xffff0000`。而 `%d` 代表要印出 `signed int`。`%u` 才是印出 `unsigned int`。
:::
:::info
來源:[韌體工程師的0x10個問題](https://hackmd.io/@Chienyu/S1loEqCuo)
:::
### 4. 解釋 “struct” 和 “union”
| 特性 | struct(結構體) | union(聯合體) |
| -------- | -------- | -------- |
| 記憶體分配 | 每個成員都有自己的儲存空間,總大小為所有成員大小的總和(加上對齊填充)| 所有成員共用相同的記憶體空間,大小等於最大成員的大小 |
| 成員存取 | 可以同時存取多個成員 | 只能存取一個成員,寫入一個成員會覆蓋其他成員 |
| 用途 | 用來組織不同類型的相關資料 | 用來節省記憶體,適合不同情況下存不同的值|
struct 在 C 上是一種資料型態裡面可以包含多種資料型態,並且會按照程式中定義時擺放的順序在記憶體中排序,按照宣告的順序擺放。編譯器會自動為 struct 的成員分配空間。
在 C++ 上 struct 可以包含方法,和 class 幾乎相同,只是繼承與存取權限預設皆為 public (相反地 class 權限預設為 private)。另外,class 比 struct 更常用於**泛型**程式設計。例如:
```cpp
template <typename T>
class MyClass {
T value;
public:
MyClass(T v) : value(v) {}
T getValue() { return value; }
};
int main() {
MyClass<int> obj(10);
printf("%d\n", obj.getValue()); // 10
return 0;
}
```
:::spoiler 什麼是**泛型**程式設計?
泛型程式設計(Generic Programming) 是一種程式設計範式,它允許你編寫可以適用於不同資料型別的程式碼,而不需要針對每種型別重複撰寫相同的程式碼。
在 C++ 中,泛型程式設計最常透過 `Template` 來實作。
假設你要寫一個函式來交換兩個變數的值:
```cpp
void swap_int(int &a, int &b) {
int temp = a;
a = b;
b = temp;
}
```
這函式只能交換 `int` 型別,如果要交換 `double` 或 `char`,就必須額外寫:
```cpp
void swap_double(double &a, double &b) { ... }
void swap_char(char &a, char &b) { ... }
```
這樣的程式碼會變得重複且難以維護。
泛型程式設計 讓我們可以寫一個通用的函式,適用於所有型別:
```cpp
template <typename T>
void swap_generic(T &a, T &b) {
T temp = a;
a = b;
b = temp;
}
```
這樣我們就可以用同一個 `swap_generic `函式交換不同型別:
```cpp
int x = 10, y = 20;
double a = 1.1, b = 2.2;
swap_generic(x, y);
swap_generic(a, b);
```
:::
union 是一種特殊的資料型態,所有內容都從記憶體開頭開始存取的資料型態,並且他的大小由內部最大的那個資料型態來定義。會用同一個存儲空間,只能存儲最後一個成員訊息。只給其中一個成員給值而使用其他值就會壞掉。
**範例**
```c
#include <stdio.h>
#include <string.h>
struct MyStruct {
int a;
float b;
char c[10];
};
union MyUnion {
int a;
float b;
char c[10];
};
int main() {
struct MyStruct s = {1, 2.5, "Hello"};
union MyUnion u;
// 設定 union 的不同成員
u.a = 1;
printf("Union a: %d\n", u.a);
u.b = 2.5; // 這會覆蓋 `a`
printf("Union b: %f\n", u.b);
printf("Union a (被覆蓋後): %d\n", u.a); // 這時候 `a` 的值已經被破壞
strcpy(u.c, "Hello"); // 這會覆蓋 `b`
printf("Union c: %s\n", u.c);
printf("Union b (被覆蓋後): %f\n", u.b); // `b` 的值不再正確
return 0;
}
```
**執行結果**
可以看到 union 的成員共用記憶體,因此寫入 b 之後,a 的值就變了。
```c
Union a: 1
Union b: 2.500000
Union a (被覆蓋後): 1082130432
Union c: Hello
Union b (被覆蓋後): 1261528175.000000
```
:::spoiler 補充:enum
C 語言提供自定型態為 enum,是一組由識別字所代表的整數常數(不可變更其值)。除非特別指定,不然都是由 0 開始,接下來遞增 1。例如:
```c
enum week{Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday};
```
:::
:::info
來源:[C 語言考古題](https://hackmd.io/aoE7Y_wjS1ynSad6TUMj8w)
:::
### 5. Reverse Linked List
給你一個指向一組 Linked List 的指標叫做 head,請你將這組Linked List 進行反轉,並且回傳反轉後的 Linked List。
```c
class Solution {
public:
struct ListNode* reverseList(ListNode* head) {
struct ListNode* newhead = NULL;
struct ListNode* curr = head;
while (curr != NULL){
struct ListNode* next = curr->next;
curr->next = newhead;
newhead = curr;
curr = next;
}
return newhead;
}
};
```
空間複雜度為 `O(1)`,時間複雜度為 `O(n)`。
:::info
來源:[2023 年「資訊科技產業專案設計」作業 2](https://hackmd.io/@sysprog/Sk6SC-7Va)
:::
### 6. call by value / call by pointer / call by reference
- call by value 傳值
- call by pointer 傳址
- call by reference 傳參考
- *: pointer, dereference(解指標)
- &: address-of
- **: pointer to pointer(指標的指標)
#### call by value
caller 和 callee 各自有自己的記憶體。
這種參數傳遞方式是最簡單的方式,就是把每個參數都複製一份到函式裡運算。
```c
int add(int x, int y) {
return x + y;
}
```
這種方式在某些情形變得不適用,例如要**修改傳遞進來的參數**,或者不想複製一份參數浪費效能。這時就會採用傳址 call by pointer 或傳參考 call by reference。
#### call by pointer
又稱 Call by address,但為了跟後面的方式作區分,用 Call by pointer 這名子比較明確,這邊以最簡單的swap作為範例。
```c
void swap(int *x, int *y) {
int tmp = *x;
*x = *y;
*y = tmp;
}
int a = 3;
int b = 5;
swap(&a, &b);
```
#### call by reference
caller 和 callee使用相同的記憶體,C++ 才有。
好處在於之後在函式裡的變數寫法如同一般的變數寫法。
```c
void swap(int &x, int &y) {
int tmp = x;
x = y;
y = tmp;
}
int a = 3;
int b = 5;
swap(a, b);
```
:::info
來源:[call by value傳值, call by pointer傳址, call by reference傳參考 的差別](https://shengyu7697.github.io/cpp-call-by-value-pointer-reference/)
:::
### 7. Implement a Queue and Stack by linked-list
沒看清楚題目要問甚麼?但可能是要考這個
#### 用 linked-list 實作 queue
```c
struct linked_list {
struct linked_list *next;
uint8_t data;
};
struct Queue {
struct linked_list *head, *tail;
void (*qpush)(uint8_t);
void (*qpop)();
};
struct Queue queue = NULL;
void qpush(uint8_t val)
{
struct linked_list *tmp = malloc(sizeof(struct linked_list));
tmp->next = NULL;
tmp->val = val;
if (queue->head == NULL)
queue->head = tmp;
else
queue->tail->next = tmp;
queue->tail = tmp;
}
void qpop()
{
struct ListNode *tmp = malloc(sizeof(struct ListNode));
queue->tail->next = NULL;
queue->head = queue->head->next;
if (queue->head == NULL)
queue->tail = NULL;
free(tmp);
tmp = NULL;
}
void init_queue(struct Queue **q)
{
*q = (struct Queue *)malloc(sizeof(struct Queue));
if (*q == NULL) return;
(*q)->head = NULL;
(*q)->tail = NULL;
(*q)->qpush = qpush;
(*q)->qpop = qpop;
}
int main(void)
{
init_queue(&queue);
list.push(1);
list.push(2);
list.push(3);
list.pop();
list.push(4);
list.pop();
}
```
#### 用 linked-list 實作 stack
```c
struct linked_list {
struct linked_list *next;
uint8_t data;
};
struct Stack {
struct linked_list *top;
void (*spush)(uint8_t);
void (*spop)();
};
struct Stack stack = NULL;
void spush(uint8_t val)
{
struct linked_list *tmp = malloc(sizeof(struct linked_list));
tmp->next = NULL;
tmp->val = val;
if (stack->head == NULL)
stack->head = tmp;
else
stack->top->next = tmp;
queue->tail = tmp;
}
void spop()
{
struct ListNode *tmp = malloc(sizeof(struct ListNode));
queue->tail->next = NULL;
queue->head = queue->head->next;
if (queue->head == NULL)
queue->tail = NULL;
free(tmp);
tmp = NULL;
}
void init_stack(struct Stack **s)
{
*q = (struct Queue *)malloc(sizeof(struct Queue));
if (*q == NULL) return;
(*q)->head = NULL;
(*q)->tail = NULL;
(*q)->qpush = qpush;
(*q)->qpop = qpop;
}
int main(void)
{
init_stack(&stack);
list.push(1);
list.push(2);
list.push(3);
list.pop();
list.push(4);
list.pop();
}
```
:::info
來源:[一些我夢到的常見面試問題](https://hackmd.io/@DirtyDIrty/ry2v2i2Ui)
:::
### 8. 介紹 Mutex
他是用來解決同步問題的一種 Data Type,會有兩個 Atomic operation,分別是 Acquire() 及 Release(),裡面的實作方法是利用一個共享的 boolean 變數控制。
:::info
來源:[面試準備](https://hackmd.io/@bMgcVyp5TwiCYEatcz1j-Q/rkOqpwfNo)
:::
### 9. 介紹 Memory Leak
記憶體流失的意思。主要是記憶體管理失當,像是 C/C++ 如果使用`malloc()` 取的記憶體空間,就必須在不須使用後利用 `free()` 適當,如果沒有釋放,則會產生 memory leakage。
:::info
來源:[面試準備](https://hackmd.io/@bMgcVyp5TwiCYEatcz1j-Q/rkOqpwfNo)
:::
## Behavior Questions
### 你的優點/缺點
### 求學過程中,最有印象的一門課
### 面對衝突,你會怎麼解決?
### 最有成就感的事?
### 在團體中是甚麼角色?
### 在團隊中有無遇到衝突?遇到衝突如何解決?
### 在團隊中討厭哪種人?為甚麼?
### 遇到很多件棘手的任務要如何面對?
### 上一次面對失敗是甚麼時候?你從中學習到甚麼?