# Interview Ques. for C
> 紀錄面試過程中被問過的 C 語言相關題目,部分題目可能有錯誤(出公司就忘了),請斟酌參考
## 完整程式測驗
### 1. 金字塔
<!-- 新漢筆試 -->
> Q: 請使用任一種程式語言,印出以下結果
```
*
***
*****
*******
*********
```
**Solution:**
```c=
void main() {
int i, j, k;
for (i = 0; i < 5; i++) {
for (j = 4 - i; j > 0; j--) {
printf(" ");
}
for (k = 0; k < 2 * i + 1; k++) {
printf("*");
}
printf("\n");
}
}
```
### 2. 波浪舞
<!-- 新漢筆試 -->
> Q: 請使用任一種程式語言實作以下功能 : 將一個輸入字串轉成文字波浪舞
```java
Input: "hellp"
Output: ["Hello","hEllo", "heLlo", "helLo", "hellO"]
```
**Solution:**
```c=
```
### 3. 函式呼叫型式
<!-- 云辰電子 筆試 -->
> Q: 請寫出 call by value/call by address/call by reference 的範例
**Solution:**
```c
void call_by_value(int a) {
a = 100;
}
void call_by_address(int *a) {
*a = 100;
}
void call_by_reference(int &a) {
a = 100;
}
```
### 4. 回呼函式(call back function)
<!-- 云辰電子 筆試 -->
> Q: 請寫出一個 call back function 範例。
**Solution:**
```c
// prototype
void func(int, void (*)(int));
void call_back_func(int);
// main
void main(void) {
func(10, call_back_func);
}
// definition
void func(int y, void (*call_back)(int)) {
printf("(func) y = %d\n", y);
call_back(100);
}
void call_back_func(int x) {
printf("(Call back function) x = %d\n", x);
}
```
## 輸出結果
### 1. 一般輸出(1)
<!-- 神準科技 筆試 -->
> Q: 以下程式輸出結果為何?
```c=
int a = 0;
while (a <= 20) {
if (a == 5) {
a += 3;
continue;
}
if (a % 3) {
printf("%d,", a);
}
if (a > 8) {
break;
}
a++;
}
printf("%d\n", a);
```
**Output:**
```
1, 2, 4, 8, 9
```
### 2. 一般輸出(2)
<!-- 神準科技 筆試 -->
> Q: 以下程式輸出為何?
```c=
int g = 0;
int foo(int a) {
static int b = 0;
g++; a++; b++;
return b;
}
void main() {
int a = 0, b;
g = foo(a) + 1;
b = foo(a);
printf("%d, %d, %d\n", g, a, b);
}
```
**Output:**
```
3, 0, 2
```
### 3. 一般輸出(3)
<!-- 神準科技 筆試 -->
> Q: 以下程式輸出結果為何?
```c=
#define MAX(x, y) ((x) > (y) ? (x) : (y))
void main() {
int i = 10, j = 10, k;
k = MAX(i++, j++);
printf("%d, %d, %d\n", i, j, k);
}
```
**Output:**
```
11, 12, 11
```
### 4. 一般輸出(4)
<!-- 新漢筆試 -->
> Q: 以下程式產生結果為何?
```c=
int func(int i) {
if ((i == 1) || (i == 2)) {
return 1;
}
return func(i-1) + func(i-2);
}
void main() {
printf("%d\t", func(10));
}
```
**Output:**
```
55
```
## 字元/字串
### 1. 字串輸出(1)
<!-- 神準科技 筆試 -->
> Q: 以下程式輸出結果為何?
```c=
char str[16] = "hello!world", *c;
for (c = str; *c != '\0'; c++) {
if (*c == 'l')
*c = 'p';
if (*c == '!')
*c = '\0';
if (*c == 'r')
break;
}
printf("%s%s\n", str, c);
```
**Output:**
```
hepporld
```
### 2. 字元輸出(1)
<!-- 新漢筆試 -->
> Q: 以下程式輸出結果為何?
```c=
void main() {
char c;
for (c = 'a'; c < 'g'; c++) {
switch (c) {
case 'a': c += 2;
case 'b': c += 1;
case 'g': ++c;
printf("%c\n", c--);
default: ++c;
}
printf("***%c\n", c);
}
}
```
**Output:**
```
e
***e
***g
```
> [!Caution]
> 在 C 的 `switch` 結構中,只會去匹配第一個 case 條件,若用來比對的 case 沒有 `break` 敘述,則會繼續執行後面的條件,直到遇見 `break` 或 `return`
## Pointer
### 1. 指標與陣列(1)
<!-- 其陽科技 筆試 -->
> Q: 以下程式中,假設 `&a = 0x1000;`,則輸出分別為何?
```c=
#include <stdio.h>
int main(void) {
int a[2][2] = {{2, 1}, {3, 4}};
int *ptr = a;
printf("ptr = %#X", ptr);
printf("++ptr = %#X", ++ptr);
printf("*ptr = %#X", *ptr);
printf("*(ptr++) = %#X", *(ptr++));
printf("++*ptr = %#X", ++*ptr);
printf("++(*ptr) = %#X", ++(*ptr));
printf("++*(ptr) = %#X", ++*(ptr));
printf("*(--ptr) = %#X\n", *(--ptr));
}
```
**Output:**
```
ptr = 0x1000
++ptr = 0x1004
*ptr = 0x1
*(ptr++) = 0x1
++*ptr = 0x4
++(*ptr) = 0x5
++*(ptr) = 0x6
*(--ptr) = 0x1
```
### 2. 指標與陣列(2)
<!-- 神準科技 筆試 -->
> Q: 以下程式輸出結果為何?
```c=
void main() {
int a = 1, b[2] = {2, 3}, *p = &a;
b[*p]++;
(*p)--;
p = b;
p += a;
printf("%d, %d, %d\n", a, b[1], *p);
```
**Output:**
```
0, 4, 2
```
### 3. 指標輸出(1)
<!-- 神準科技 筆試 -->
> Q: 以下程式輸出結果為何?
```c=
void foo1(char* str) {
strcpy(str, "4567");
}
void foo2(char* str) {
str = (char *) malloc(5);
strcpy(str, "4567");
}
void main() {
char str1[10] = "1234";
char *str2 = "1234";
foo1(&str1[3]);
foo2(str2);
printf("%s, %s\n", str1, str2);
}
```
**Output:**
```
1234567, 1234
```
### 4. 指標與陣列(3)
<!-- 新漢筆試 -->
> Q: 以下程式碼產生結果為何 ?
```c=
void main() {
int a[] = {3, 8, 9, 2, 1};
int *b = a, *c;
int v1, v2, v3, v4;
c = b + 2;
v1 = *c;
v2 = *c++;
v3 = *c + 5;
v4 = *(c + 1);
printf("%d%d%d%d\n", v1, v2, v3, v4);
}
```
**Output:**
```
9971
```
> [!Caution]
> `*c++` 等價於 `*(c++);`
### 5. 指標與陣列(4)
<!-- 云辰電子 筆試 -->
> Q: 試問以下程式中 `a[]` 之結果為何?
```c=
int a[] = {6, 7, 8, 9, 10};
int *p = a;
*(p++) += 123;
*(++p) += 123;
```
**Output:**
```
a[5] = {129 7 131 9 10 }
```
:::danger
`*(p++) += 123;` 不等價於 `*(p++) = *(p++) + 123;`。等號左邊與右邊用的是同一個 `*(p++)`
:::
### 6. 指標與陣列(5)
<!-- 云辰電子 筆試 -->
> Q: 以下程式輸出為何?
```c=
int a[] = {0, 1, 3, 4, 5, 6, 7};
int *p = &(a+1)[3];
printf("%d", *p);
```
**Output:**
```
5
```
`a` 原本指向第一個元素 `a[0]`,加 1 後指向 `a[1]`。接著 `[3]` 表示以 `a[1]` 開始的第三個元素,因此會指向 `a[5]`。這題要考的是指標的索引表示法。
### 7. 指標與陣列 (6)
<!-- 亞信電子 筆試 -->
> Q: 以下程式中,陣列 `a` 的各項元素值為何?
```c=
int a[] = {5, 6, 7, 8, 9};
int *p = a;
*(p++) += 10;
*(++p) += 10;
```
**Output:**
```
15, 6, 17, 8, 9
```
### 8. 常數指標(1)
<!-- 亞信電子 筆試 -->
> Q: 以下程式片段是否有錯誤?
```c=
int d = 1;
int e = 2;
int f = 3;
int* const a = &d;
void func(void) {
a = &e;
*a = f;
}
```
**Output:**
```c
int d = 1;
int e = 2;
int f = 3;
int* const a = &d; // a: 指向整數的「常數指標」
void func(void) {
a = &e; // Error, 不能指向其他位址
*a = f;
}
```
## Bitwise manipulation
### 1. Big/little endian
<!-- 迅杰科技 筆試 -->
位元組順序(endianness)有分為 big endian 與 little endian,指的是資料在記憶體中存放的型式與位元順序。
* Big endian: 高位元放在較低的記憶體位址
* Little endian: 低位元放在較高的記憶體位址

可透過以下方式作判斷
```c=
#include <stdio.h>
typedef union {
unsigned int n;
unsigned char ch[4];
} endianTest;
int main() {
endianTest et;
et.n = 0x23F7;
printf("Endianness: ");
if (et.ch[0] == 0xF7 && et.ch[1] == 0x23) {
printf("little endian\n");
}
else if (et.ch[0] == 0x23 && et.ch[1] == 0xF7) {
printf("big endian\n");
}
printf("Memory map: \n");
for (int i = 0; i < 2; i++) {
printf("%p: %#x\n", &et.ch[i], et.ch[i]);
}
return 0;
}
```
**Output:**
```
Endianness: little endian
Memory map:
0x7ffc107837c8: 0xf7
0x7ffc107837c9: 0x23
```
### 2. 位元運算(1)
<!-- 神準科技 筆試 -->
> Q: 以下程式結果輸出為何?
```c=
unsigned int v = 0xF0F0F0F0, i;
for (i = 0; i <= 20; i++) {
if (v & (1 << i))
v &= ~(1 << i);
else
v |= 1 << i;
}
printf("%X\n", v);
```
**Output:**
```
F0EF0F0F
```
### 3. 位元運算(2)
<!-- 新漢筆試 -->
> Q: 以下程式碼產生結果為何?
```c=
void main() {
unsigned int a = 60;
unsigned int b = 13;
int c = 0;
printf("%d\t", a & b);
printf("%d\t", a | b);
printf("%d\t", a ^ b);
printf("%d\t", ~a);
printf("%d\t", a << 2);
printf("%d\t", a >> 2);
}
```
**Output:**
```
12 61 49 -61 240 15
```
> [!Caution]
> 以 `%d` 呈現無號數,會使用 2 補數的方式顯示。
### 4. 位元運算(3)
<!-- 亞信電子 筆試 -->
> Q: 以下程式輸出為何?
```c=
#define MASK 0x03
int i = 31;
if (i & MASK == 0x03) {
i = 100;
}
else {
i = 200;
}
printf("%d", i);
```
**Output:**
```
100
```
## 巨集
### 1. 巨集陷阱(1)
<!-- 亞信電子 筆試 -->
> Q: 以下程式執行後 `j`的數值為何?
```c=
#define INC(x) x*=2; x+=0x10
int i, j;
for (i=0, j=1; i<3; i++)
INC(j);
```
**Output:**
```
24
```
##### 說明:
巨集會展開成兩行敘述:
```c
for (i=0, j=1; i<3; i++)
j *= 2;
j += 0x10;
```
因為沒有大括號的存在,所以事實上只有 `j *= 2` 屬於 `for` 迴圈的結構,等價於以下片段:
```c
for (i=0, j=1; i<3; i++) {}
j *= 2;
}
j += 0x10;
```
因此迴圈結束後 j = 8,最後加上 `0x10` 得 24。
## 其他
### 1. 關鍵字作用(1)
<!-- 云辰電子 筆試 -->
> Q: 請問 `volatile` 應用時機?
關鍵字 `volatile` 用來提醒編譯器該變數可能會被改變,不要對該變數做最佳化,每次都要去記憶體讀取最新的值。
### 2. 絕對位址
<!-- 云辰電子 筆試 -->
> Q: 設定一個位置 `0x2000` 的整數變數,其值為 `0xBBCC`
```c=
int *a = (int *) 0x2000;
*a = 0xBBCC;
```
### 3. Memory map
<!-- 云辰電子 筆試 -->
> Q: 何為 stack 與 heap?區域陣列的長度最大與何者有關?
**Output:**
* Stack: 呼叫函式時用來暫時儲存資料(Ex. 變數/參數)的記憶體區域。
* Heap: 程式執行期間動態分配的記憶體區域。
* 區域陣列的最大長度會受 stack 大小限制,超過會發生 overflow
* 要更大陣列需要使用動態分配的記憶體空間
### 4. 關鍵字作用(4)
<!-- 云辰電子 筆試 -->
> Q: `extern const volatile unsigned int clock` 中,`const volatile` 的應用時機為何?
**Output:**
`const volatile` 表示該變數會被非預期修改,需要每次從記憶體讀取它的值,且該變數在程式內是唯讀的,不能在程式中改變。(但可能被其他週邊硬體改變)
> [!Tip]
> 會被外部硬體改變的唯讀變數就可以用 `const volatile` 宣告。