# 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: 低位元放在較高的記憶體位址 ![image](https://hackmd.io/_uploads/HJiW-zGHxe.png) 可透過以下方式作判斷 ```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` 宣告。