# [面試] C語言面試題目 ###### tags: `面試` `C` [toc] ## TBD - OS - 分析 kernel Oops - https://www.youtube.com/watch?v=ORLKg22XkBs&list=PLj6E8qlqmkFvCxHVYYVYtnPuQb3NeOlUb&index=2 - 寫程式速度 - https://systemd-book.junmajinlong.com/systemd_bootup.html ## 參考網頁 1. https://hackmd.io/@Rance/Byhz8-eJE 2. [軟體/韌體工程師《面試重點與觀念複習》](https://eeepage.info/interview-c/): 客家沒買 3. https://www.raind.blog/c&cpp-zh/c&cpp_interviewexam.html 4. https://hackmd.io/@SupremeEJ/B1FO1YJCU 5. https://hackmd.io/@a110605/By6DscbVM?type=view 6. https://hackmd.io/@Rance/SkSJL_5gX 7. https://hackmd.io/@npFjxYHBSRKgY2jI7nG_ag/Ske9ixP7O 8. https://hackmd.io/@6zSPCmL1Szq0bq_rOqcO6w/HkkhJ-ah-?type=view 9. https://hdlbits.01xz.net/wiki/Main_Page - 類似Verilog版的leetcode 10. https://hackmd.io/E9N9mkhcQgyun08X2U-fyA#1214 - 晚點看 - ### 工程師應知道的0x10個問題 - 來源: - [原文](https://rmbconsulting.us/publications/a-c-test-the-0x10-best-questions-for-would-be-embedded-programmers/) - [中文](https://medium.com/@racktar7743/工程師應知道的0x10個問題-11-中斷-7c59ac7a10c4) #### Preprocessor 1. 用`#define`來宣告一個會回傳「一年的秒數」的常數,不考慮閏年 - ANS: `#define SECONDS_PER_YEAR (60UL * 60UL * 24UL * 365UL)` 1. 有關`#define`的基礎用法: 不用分號";", 需要括弧 2. 命名: 大小寫, 下劃線 3. 因為preprocessor會幫你算完數字相乘,並不會浪費實際執行的時間,所以不需要自己手動算完數字相乘的部分 4. 了解使用int來計算會在16位元的系統上產生overflow的問題,所以要在數字後標示"L"表達用long來計算 5. BONUS: 使用"UL"來標示表示你有注意到signed與unsigned的差別 2. 撰寫一個MIN marco,有兩個傳入值並回傳較小的部分 - ANS: `#define MIN(A,B) ((A) <= (B) ? (A) : (B))` 1. 基礎理解`#define`在定義MARCO的使用 - 因為在inline operator成為C的標準之前,macros是唯一產生inline code的方法 - 在embedded systems中,常常利用marco來去達成更好的效能 2. 了解ternary conditional operator (?:). - 比起使用if-then-else sequence,它會讓C compiler更好的去優化編譯這個需要條件判斷的部分。畢竟在embedded system,效能是很重要的 3. 了解在marco中小心使用括弧 - 因為#define是直接把東西放在對應位置,沒有好好地括起來容易會有優先權錯的問題 4. the side effects of macros - what happens when you write code such as :`least = MIN(*p++, b);` - ans: [link](https://stackoverflow.com/questions/7299286/whats-the-side-effect-of-the-following-macro-in-c-embedded-c), [link:better](https://gcc.gnu.org/onlinedocs/cpp/Duplication-of-Side-Effects.html) - 他的展開會變成: `least = ((*p++) <= (b) ? (*p++) : (b))` - `*p++`會變成執行2次,就不是原先寫法預期的一次 3. 使用`#error <ERROR MSG>`的目的是甚麼? - 程式宅才會比較知道這個 - [ref](https://rmbconsulting.us/publications/in-praise-of-the-c-preprocessors-error-directive/), [ref](https://rmbconsulting.us/Publications/ErrorDirective.pdf) - 用途: 讓編譯器跳出STDERR - 使用目的: 1. Incomplete code 2. Compiler-dependent code 3. Conditionally-compiled code - 2 3通常搭配`#if #endif`來使用 #### Infinite Loops 4. embedded systems經常使用無線迴圈。怎麼在C中產生無限迴圈? There are several solutions to this question. My preferred solution is: ```clike= // Sol1: Prefer while(1){} // Sol2: for(;;){} // Sol3 Loop: … goto Loop; ``` - 2不好在於這種寫法沒有清楚的表達這要做甚麼 - 除非說這是K&R preferred method and the only way to get an infinite loop passed Lint - 3可能以前是寫組合語言、FORTRAN或是BASIC,除此之外都不太好。 #### Data declarations 5. 用變數a,寫出以下定義: 1. An integer - `int a;` 2. A pointer to an integer - `int *a;` 3. A pointer to a pointer to an integer - `int **a`; 4. An array of ten integers - `int a[10];` 5. An array of ten pointers to integers - `int *a[10];` 6. A pointer to an array of ten integers - `int (*a)[10];` 7. A pointer to a function that takes an integer as an argument and returns an integer - `int (*a)(int);` 8. An array of ten pointers to functions that take an integer argument and return an integer. - `int (*a[10])(int);` > People often claim that a couple of these are the sorts of thing that one looks up in textbooks – and I agree. While writing this article, I consulted textbooks to ensure the syntax was correct. However, I expect to be asked this question (or something close to it) when in an interview situation. Consequently, I make sure I know the answers – at least for the few hours of the interview. Candidates that don’t know the answers (or at least most of them) are simply unprepared for the interview. If they can’t be prepared for the interview, what will they be prepared for? > 雖然這問題比較像是查書就可以知道的,但是起碼,為了這次面試準備一下吧 #### Static 6. keyword static的用途是甚麼? - 看修飾在哪邊,對誰修飾 1. 在function中,對variable修飾: 該variable在不同的function call時,維持是對同一個值操作,而不是每次都是產生新的。 2. 在module中(func.外),對variable修飾: 該variable可以被所有在這個module中的function存取 - localized global. 3. 在module中,對Function修飾: 這個function只能被在這個module之中的function所呼叫. - localizing the scope of both data and code - 第3個很常被忽略 ```clike= // 1. void foo(){ static int c = 0; cout << c++ << endl; } void main(){ foo(); foo(); } // STD: // 0 // 1 ``` #### Const 7. const的意思是甚麼 - const means "read-only”. - > As soon as the interviewee says ‘const means constant’, I know I’m dealing with an amateur. - [Dan Saks](https://www.dansaks.com/articles/1999-02%20const%20T%20vs%20T%20const.pdf) - What do the following incomplete [2] declarations mean? ```clike= // const (read-only) integer const int a; int const a; // a pointer to a const integer // the integer isn’t modifiable, but the pointer is const int *a; int const *a; // a const pointer to an integer // the integer pointed to by a is modifiable, but the pointer is not int * const a; // a const pointer to a const integer // neither the integer pointed to by a, nor the pointer itself may be modified int const * a const; ``` - usage: - 提供有用的資訊給其他code協作者 - 讓編譯器可以給出更好的優化 - 因為會被編譯器所保護,無意間用到就會跳錯誤了,所以比較不容易產生不明顯的BUG #### Volatile 8. What does the keyword volatile mean? Give three different examples of its use. - 主要是因為被修飾的變數容易被無預警的改變,所以要確保在執行計算時,得到的計算結果是那一刻你想要的 - 讓你的code不會被編譯器預測優化 - Examples of volatile variables are: - Hardware registers in peripherals (e.g., status registers) - Non-stack variables referenced within an interrupt service routine. - Variables shared by multiple tasks in a multi-threaded application. - > If a candidate does not know the answer to this question, they aren’t hired. I consider this the most fundamental question that distinguishes between a ‘C programmer’ and an ‘embedded systems programmer’. Embedded folks deal with hardware, interrupts, RTOSes, and the like. All of these require volatile variables. Failure to understand the concept of volatile will lead to disaster. - parameter可以同時是const與volatile? - 可以 - Example: read only status register - volatile: 可能隨時改變 - const: 程式不該去修改read-only的參數 - 指標可以是volatile? - 可以,但不常見 - Example: an interrupt service routine modifies a pointer to a buffer. - What is wrong with the following function?: ```clike= int square(volatile int *ptr){ return *ptr * *ptr; } ``` 編譯器會因為傳入參數是volatile所以優化成以下 ```clike= int square(volatile int *ptr){ int a,b; a = *ptr; b = *ptr; return a * b; } ``` 但是在執行的過程中,因為volatile變數可能會隨時改變,讓a, b可能拿到的是不同變數,這樣的結果就不是預期的平方數,所以有手動寫成只取值一次,之後拿取出來的值平方的寫法 ```clike= long square(volatile int *ptr){ int a; a = *ptr; return a * a; } ``` #### Bit Manipulation 9. 嵌入式系統需要使用者去操作register或variable中的bit。給定int a,寫出兩段code,第一段要讓a的bit3變成1,第二段要讓a的bit3變成0,其他部分都需要維持不變 - Wrong solution: bit fields在不同的編譯器執行的結果不相同(non-portable),這樣會在執行上產生問題 - Use #defines and bit masks. - key point: manifest constants, together with the `|= &= ~` constructs. ```clike= #define BIT3 (0x1 << 3) static int a; void set_bit3(void) { a |= BIT3; } void clear_bit3(void) { a &= ~BIT3; } ``` -------------------------------------------- ## 重點整理 ### 指標陣列與陣列指標 - 理解法: - 由外而內的拆,可能有 - 解讀優先度: Array subscripting ([])是大於Indirection(*)的 - [link](https://hackgrass.blogspot.com/2018/03/c-pointerint-foo-int-bar.html) - `int *a[10];` - 指標的陣列 - 一個陣列,內有10個int指標 - a先解讀[]所以是個陣列,裡面的型別是`int*` - `int (*a)[10];` - 陣列的指標 - 一個指標,指向大小為10的int陣列 - 依照(),a先解讀`*`所以是個指標,指向的是`int[10]` ### Function pointer 1. Function Pointer void (*fptr)(type_a, type_b) = &func; 2. Function Pointer Type typedef void(F1)(void); ```clike= #include <stdio.h> typedef int(*OP)(int,int); int add(int a, int b) { return a+b; } int mult(int a, int b) { return a*b; } int main() { int (*op)(int a, int b); op = add; printf("op(3,5)=%d\n", op(3,5)); op = mult; printf("op(3,5)=%d\n", op(3,5)); OP op2 = add; printf("op(3,5)=%d\n", op2(3,5)); op2 = mult; printf("op(3,5)=%d\n", op2(3,5)); } ``` - `void(*(*papf)[3])(char*);` rewrite 成 `typedef ______________; pf(*papf)[3];` - ans1: https://www.ptt.cc/bbs/C_and_CPP/M.1400666198.A.3FC.html - ans2: https://www.zhihu.com/question/39951798 - 跟\*p[10], (\*p)[10]一起想 ### const ```clike= const int *p1; // pointer可改,指向內容不能改 int const *p2; // pointer可改,指向內容不能改 int * const p3; // pointer不可改,指向內容可改 const int * const p4; // 都不能改 int const * const p5; // 都不能改 ``` ### \#ifndef \#endif https://medium.com/@nickhuang_1199/ifndef-define-endif-%E7%9A%84%E7%94%A8%E6%B3%95-c7d33d6b1d47 ### internal/external linkage https://stackoverflow.com/questions/1358400/what-is-external-linkage-and-internal-linkage ### inline https://dotblogs.com.tw/v6610688/2013/11/27/introduction_inline_function ### Other ![](https://hackmd.io/_uploads/Byn8UDqqh.png) ------------------------------- ## 群聯 ### 05/12 1. 實作一個4捨5入的函式 2. static 3. const 4. call by value、call by address(探討與差距,然後C沒有call by ref.) - https://www.wongwonggoods.com/all-posts/cplusplus/cpp-concept/cpp-value-address-pointer-reference/ - call by value: - 傳數值進去 - call by address: - 傳指標(內涵address)進去 - call by reference: - c++獨有 - 在宣告func. para.時加`&`即可 - 其他都跟call by value一樣,不用像call by address一樣特地取address出來再傳進去 - 會去「檢查這個記憶體位置是否合法」 ```c= void callByVal(int a){} callByVal(a) void callByAddr(int* a){} callByAddr(&a) void callByRef(int& a){} callByRef(a) ``` ### 05/16 1. 顛倒字串 第一個跟最後一個換,以此類推,跑道中間停 2. violate的應用場景 [link](https://newscienceview.blogspot.com/2013/09/c-volatile.html) Volatile是一個變數聲明限定詞。它告訴編譯器,它所修飾的變數的值可能會在任何時刻被意外的更新 類似因為變數常常被更改,所以告訴編譯器不要優化這部分 常用於multi-thread或是interrupt 3. c與c++ static的差別: - REF: [link1](https://medium.com/@alan81920/c-c-%E4%B8%AD%E7%9A%84-static-extern-%E7%9A%84%E8%AE%8A%E6%95%B8-9b42d000688f) - c: - 定義區域性靜態變數: 被init後就直到程式結束才會被free - 限定存取區域: - 只能在同個程式碼區塊中存取 - 反過來說,可以避免放在func外的global var在不同的檔案之間互相引用時的名稱衝突 - c++: - C的上述功能 - 定義類的成員變數和函數,即靜態成員和靜態成員函數。 - REF2: [link2](https://shengyu7697.github.io/cpp-static/) - C/C++ 中 static 放在區域變數之前 - 被init後就直到程式結束才會被free - C/C++ 中 static 放在全域變數之前 - 在 c/cpp 檔裡該變數無法被其他 c/cpp 檔用 extern 來使用。 - C/C++ 中 static 放在函式之前 - 該func.只能在這支原始檔裡使用,不想給別人用extern呼叫 - C++ 中 static 放在 class 的 member variable 之前 - 所以class instance 共用同一個variable - C++ 中 static 放在 class 的 member function 之前 - 該func.可以直接乎要使用,不需要instance來使用 4. linked list的push與middle 小孩子都會寫XD 5. 請評論一下以下程式碼: ```clike= __interrupt double compute_area(double radius) { double area = PI * radius * radius; printf("\nArea = %f", area); return area; } ``` - ANS: - [Link1](https://blog.xuite.net/tzeng015/twblog/113273155) - [Link2](https://medium.com/@racktar7743/工程師應知道的0x10個問題-11-中斷-7c59ac7a10c4) - interrupt 沒有return值 - interrupt不能傳入參數 - 不能用浮點數, printf 6. 比較下面兩者的差別 - extend: 第三行是合理的嗎? ```clike= const int* a; int* const b; const int* const c; ``` ans: [link](http://c.biancheng.net/view/2041.html) const會讓值保持持定不能改 ```clike= const int *p1; // pointer可改,指向內容不能改 int const *p2; // pointer可改,指向內容不能改 int * const p3; // pointer不可改,指向內容可改 const int * const p4; // 都不能改 int const * const p5; // 都不能改 ``` 修飾在*號右邊代表說你是對本體修飾,所以是真正的內容(記憶體位置)修飾,所以是指標不能改 7. 1 ```clike= struct a{ union b{ u32 c; struct { u8 d:4; u4 e:2; u4 f:1; u4 g:1; u16 h; u32 i; } } } c = 0x30; d, e, f, g, h, i = ? ``` [link: little endian and bit field](https://stackoverflow.com/questions/6043483/why-bit-endianness-is-an-issue-in-bitfields) - [link: little/big endian](https://hackmd.io/@AlienHackMd/SkLp6R34U#Union) - origin: 0x12345678 - little endian: 78563412 - big endian: 12345678 - : 記憶體左低右高 ans: d0, e3, f0 ... 0 https://hackmd.io/@sysprog/c-bitfield ---- ## Canonical ### System architecture interview - 列出PCI, usb, cpu, storage - lspci, lsusb, lscpu, lsblk - chmod - 755: 擁有者|group|其他人 - rws: s是甚麼 - **linux開機流程** - initramfs - 怎麼看boot time - systemd-analyze - 怎麼detect USB insert - udev - RAM usage - top, free - daemon - 控制: systemd - 看log: journalctl - elf file, section, symbol - https://blog.csdn.net/helowken2/article/details/113782851 - built-in module, loadable module - [difference](https://stackoverflow.com/questions/22929065/difference-between-linux-loadable-and-built-in-modules) - 怎麼輸入參數 - loadable: 啟動時輸入 - 怎麼的知loadable有甚麼參數 - modinfo - 裝置插進來怎麼取得資料 - modalias - IPC: inter process communication - signal, pipe, socket - https://tldp.org/LDP/tlk/ipc/ipc.html - 用指令關wifi藍芽 - rfkill - OS怎麼跟device driver溝通: ACPI - https://blog.csdn.net/weixin_45279063/article/details/115867110 - heap sort 時空間複雜度 - https://www.javatpoint.com/heap-sort - time: O(nlogn) - space: O(1) - git merge 與 rebase差別 ### Linux Kernel/OS Interview 因為應徵職位沒吃kernel這麼重,主要是確認kernel問題怎麼確認問題與debug - 確認以下BUG LOG,可以看出甚麼資訊 - 主要: module, process, 時間, function - 問題中的00000008怎麼看 ``` BUG: unable to handle kernel NULL pointer dereference at virtual address 00000008 printing eip: c022a7b5 *pde = 00000000 Oops: 0000 [#1] SMP Modules linked in: thinkpad_acpi ppdev speedstep_lib cpufreq_conservative cpufreq_userspace cpufreq_ondemand cpufreq_stats cpufreq_powersave freq_table video bay dock ac sbs button container battery lp irtty_sir sir_dev pcmcia parport_pc parport snd_cs46xx gameport snd_ac97_codec ac97_bus snd_pcm_oss snd_mixer_oss nsc_ircc snd_pcm snd_seq_dummy irda crc_ccitt snd_seq_oss psmouse i2c_piix4 snd_seq_midi snd_rawmidi snd_seq_midi_event serio_raw pcspkr snd_seq i2c_core snd_timer snd_seq_device snd soundcore snd_page_alloc shpchp pci_hotplug intel_agp yenta_socket rsrc_nonstatic pcmcia_core agpgart evdev ext3 jbd mbcache sg sr_mod cdrom sd_mod uhci_hcd usbcore ata_piix ata_generic libata scsi_mod e100 mii thermal processor fan fuse apparmor commoncap CPU: 0 EIP: 0060:[<c022a7b5>] Not tainted VLI EFLAGS: 00010202 (2.6.22-12-generic #1) EIP is at acpi_ns_internalize_name+0xd/0x83 eax: 00000008 ebx: 00000000 ecx: 00000000 edx: c7879e54 esi: d0b980c0 edi: c7879e54 ebp: c7879e70 esp: c7879de8 ds: 007b es: 007b fs: 00d8 gs: 0033 ss: 0068 Process modprobe (pid: 4467, ti=c7878000 task=ce5c94c0 task.ti=c7878000) Stack: 00000000 00000000 d0b97e60 00008080 c01c4390 d0b97e60 00000000 00000000 d0b980c0 00000000 c7879e70 c022a85c d0b97e60 c795d030 c7c604e0 c01c44ef 00000004 d0b97e60 c7acea18 c01c3884 00008080 00000004 00000004 00000080 Call Trace: [<c01c4390>] __sysfs_new_dirent+0x20/0x50 [<c022a85c>] acpi_ns_get_node+0x31/0x93 [<c01c44ef>] sysfs_make_dirent+0x2f/0x50 [<c01c3884>] sysfs_add_file+0x74/0x90 [<d0b910b7>] drv_acpi_handle_init+0x37/0x90 [thinkpad_acpi] [<c0231aef>] acpi_ut_release_mutex+0x5b/0x63 [<c0233ac0>] acpi_method_notify_enable+0x15/0x34 [<d0b5ba32>] cmos_init+0x52/0x70 [thinkpad_acpi] [<d0b5c21f>] thinkpad_acpi_module_init+0x27f/0x69a [thinkpad_acpi] [<c014a811>] sys_init_module+0x151/0x1a00 [<c01fb8cf>] prio_tree_insert+0x1f/0x250 [<c01041d2>] sysenter_past_esp+0x6b/0xa9 ======================= Code: c7 44 24 14 01 00 00 00 8b 54 24 14 8d 04 96 e9 f2 fe ff ff 83 c4 18 89 d0 5b 5e 5f 5d c3 55 57 89 d7 56 53 83 ec 1c 85 c0 74 67 <80> 38 00 74 62 85 d2 74 5e 89 04 24 89 e0 e8 b5 fb ff ff 8b 4c EIP: [<c022a7b5>] acpi_ns_internalize_name+0xd/0x83 SS:ESP 0068:c7879de8 ``` - 假如客戶開啟wifi之後(不用連上網路),整個系統就會產生卡頓,該如何遠端確認他的情形 - 如何debug聲音driver相關問題 ### Software development interview - Reverse linked list - Full debug program: ```clike= #include <iostream> struct ListNode { int val; struct ListNode *next; }; // ListNode* (ListNode *head) { // }; ListNode* newRoot; ListNode* reverse(ListNode* root, ListNode* cur){ ListNode* pre; if(cur->next == NULL) { newRoot = cur; std::cout << cur->val << ", "; return cur; } else{ pre = reverse(root, cur->next); pre->next = cur; pre = cur; std::cout << pre->val << ", "; return pre; } } int main(){ ListNode* root = (ListNode*)malloc(sizeof(ListNode)); root->val = -1; ListNode* cur = root; std::cout << cur->val << ", "; for(int i = 0; i < 10; ++i){ cur->next = (ListNode*)malloc(sizeof(ListNode)); cur = cur->next; cur->val = i; std::cout << cur->val << ", "; } std::cout << "\n"; cur = reverse(root, root); std::cout << "\n"; return 0; } ``` - Please write a script to rename the files under home directory aaa -> aaa.123 bbb -> bbb.123 xxx -> xxx.123 ```bash= #!/bin/bash for file in * do mv $file $file.123 done ``` - How to switch Runlevel 3 ("multi-user.target") to Runlevel 5 ("graphic.target")? ```bash sudo systemctl set-default runlevel3.target ``` - Query display status through dbus session? (mutter) ```bash! gdbus call --session --dest org.gnome.Mutter.DisplayConfig --object-path /org/gnome/Mutter/DisplayConfig --method org.gnome.Mutter.DisplayConfig.GetCurrentState ```