# 緯穎科技 - 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 ### 你的優點/缺點 ### 求學過程中,最有印象的一門課 ### 面對衝突,你會怎麼解決? ### 最有成就感的事? ### 在團體中是甚麼角色? ### 在團隊中有無遇到衝突?遇到衝突如何解決? ### 在團隊中討厭哪種人?為甚麼? ### 遇到很多件棘手的任務要如何面對? ### 上一次面對失敗是甚麼時候?你從中學習到甚麼?