# C/C++ & Debugging & Profiling & Compiler ## Runtime library and process overview - runtime library會包含 "Entry Point" function,負責process的初始化和結束 - Process的運行步驟大概如下 1. OS create process 後,把控制權交給 process 的入口,入口就是 runtime library 的Entry Point function (so called startup function) 2. 入口 function 對 runtime library、process 進行初始化,包含 heap、I/O、thread、global variable 等等 3. 初始化後,執行 main function 4. main 執行完後,返回入口 function 進行清理工作,包含 global variable、heap、I/O 等等,然後進行系統調用結束 process ### glibc startup - 有分靜態、動態 glibc;可執行檔、shared object。程式碼複雜... - `_start` ![](https://i.imgur.com/rfwoaN2.jpg) ![](https://i.imgur.com/gARwxj1.jpg) - `__libc_start_main` - parameter - ubp_av: 包含argv和環境變量 - init: main調用前的初始化 - fini: main結束後的收尾 - rtld_fini: 和dynamic loading相關的收尾,rtld 是 runtime loader - stack_end: stack base address - 取出argv、env - 初始化,並設定結束後的收尾callback function - call main function > bounded pointer: 含有三個pointer的pointer type,但2003年被廢棄了 ![](https://i.imgur.com/CDXCCSG.jpg) - `void exit(int status)` - 執行收尾callback function list - 正常程式的必經之路 - `_exit` - platform dependent assembly function - 正常情況下會在`int $0x80`,呼叫exit system call中止程式,而不會執行hlt,`__libc_start_main`也不會return,所以`_start`的hlt也不會執行 - 如果無法正常中止,hlt就會強制把程式停下 ![](https://i.imgur.com/UGIM4qa.jpg) ### MSVC CRT startup - 版本是Visual Studio 2003 32bits,預設的startup function是 `int mainCRTStartup(void)` - 不同的版本會call不同的startup,包括 main/wmain/WinMain/wWinMain 等 - 執行步驟 - 初始化和OS版本有關的global variables - 因為還沒初始化heap,要用_alloca()動態分配在stack的memory,function return時釋放 - 初始化heap - 使用`HeapCreate()` - 初始化 I/O - 獲取command line參數、環境變量 - 初始化C library的一些數據 - call main function並取得返回值 - 檢查錯誤並return main的返回值 ### Runtime library and I/O - 定義 I/O - 對於computer,I/O是外部裝置 - 對於process,I/O包括file、pipe、socket、signal等。更廣義的來看,I/O可視為OS抽象化為"FILE"的東西。 - 在OS層面,文件有類似C語言裡的FILE的概念,在Linux裡,稱為File Descriptor,在Windows裡,稱為Handle - 在Linux中,process在Kernel Space會有個打開文件列表(`p`是base address),每個元素會個別指向實際打開文件的kernel object。所以process要使用文件時,只能透過system call。 - I/O初始化,就是要在User Space建立`stdin`、`stdout`、`stderr`,與其對應的`FILE` object,使得可以使用`printf`、`scanf`等等function ![](https://i.imgur.com/FFThX1P.jpg) ### MSVC CRT startup I/O initialization - MSVC的I/O初始化function `_ioinit()` ,主要進行幾個工作,完成後所有I/O function就能使用 - 建立User Space的打開文件表 - 如果能繼承自父行程,繼承自父行程的Handle - 初始化標準輸入輸出 - `FILE` 與User Space打開文件表 `__pioinfo` 之間的映射關係與流程如下 - MSVC定義的 `FILE` 中的 `_file` 就是User Space的Handle ![](https://i.imgur.com/Czr1HTM.jpg) - `ioinfo` 是已經開啟文件的資料, `osfhnd` 就是Handle ![](https://i.imgur.com/RRw34RC.jpg) - `__pioinfo` 是User Space的打開文件表, `_nhandle` 是實際打開文件的數量 ![](https://i.imgur.com/luAWOZl.jpg) - 使用文件時,要通過 `_osfhnd(i)` 的macro從 `FILE` 的 `_file` 變數,得到 `__pioinfo` 相對應的 `ioinfo` ,也就是相對應的User Space已開啟文件資料 ![](https://i.imgur.com/r1uiLBI.jpg) ## C Runtime library (CRT) - CRT大致包含以下功能 - 啟動與退出: Startup function和相關的function - 標準函數: C語言standard library - I/O: I/O功能的封裝和實現 - Heap: Heap功能的封裝和實現 - 語言實現: C標準以外的功能 - Debug: 實現Debug功能 ### ANSI C standard library - 特別介紹兩個library,變長參數(stdarg.h)、非局部跳轉(setjmp.h) - 變長參數的function,例如`int printf(const char* format, ...);` - 根據cdecl calling convention,caller由右至左push function arguments進stack - callee先從format取得參數的數量和type,再到stack取得參數 - 要特別處理參數可能格式和type不符 - 根據cdecl,caller清除stack - 非局部跳轉,jump可指定跳躍program counter ### glibc - 在Linux上,glibc也被稱作libc6 - glibc的發行板主要分成兩部分,header和binary - gcc可以使用`-nostartfile`和`-nostdlib`來取消預設的startup和CRT - 在Ubuntu中,`/usr/lib/x86_64-linux-gnu`裡的`crt1.o`、`crti.o`、`crtn.o`,分別是startup function,支持調用`_init()`、`_finit()`;`_init()`的開始;`_finit()`的結束 - 為了保證初始化正確,連結時的順序一般是 `ld crt1.o crti.o [user_objects] [system_libraries] crtn.o` ![](https://i.imgur.com/36hbD79.jpg) - C++的global object構造和解析不在上述三個.o中,而在`/usr/lib/gcc/x86_64-linux-gnu/`的`crtbeginT.o`和`crtend.o` - 連結順序: `ld crti.o crtbeginT.o [user_objects] [system_libraries] crtend.o crtn.o` ### MSVC CRT - 指定使用動態連結程式庫 (DLL) 時,預設連結器就會包含 Visual C++ 執行階段程式庫 (VCRuntime) - VCRuntime 包含初始化及終止 C/C++ 可執行檔所需的程式碼 - VCRuntime 程式碼會提供內部 DLL 進入點函式呼叫 - `_DllMainCRTStartup` 函式會執行基本工作,例如堆疊緩衝區安全性設定,C 執行階段程式庫 (CRT) 初始化及終止,而且會呼叫建構函式和解構函式 - `_DllMainCRTStartup` 也呼叫攔截函式的其他程式庫,例如 WinRT、 MFC 和 ATL 來執行他們自己的初始化及終止。而不需要這項初始化、 CRT 和其他程式庫,以及靜態變數,就會處於未初始化的狀態。 - 無論 DLL 使用靜態連結的 CRT 或動態連結的 CRT DLL,都會呼叫相同的 VCRuntime 內部初始化和終止常式 #### C++ 全域變數的建構和解構 - `mainCRTStartup`() 中呼叫 `_initerm( __xc_a, __xc_z );`: 此函示的目的是,遍歷呼叫所有建構子的 function pointer ![](https://i.imgur.com/bpnWazs.jpg) - 其中 `__xc_a` 和 `__xc_z` 是 global value 分別指向位於 `.rdata` section 的 `.CRT$XCA` section 和 `.CRT$XCZ` section,該段 section 的屬性是 read only。 - 在編譯時期,每個 object file 都會有 `.CRT$XCU` 的 sectoin,會加入自身的全域初始化函數。連結時,linker 會將所有相同屬性的 section 合併,且會照字母大小排序,因此 `.CRT$XC*` 這些 section 會儲存所有的全域初始化函數,下面有示意圖 ![](https://imgur.com/FbHOSoh.jpg) - 解構流程 - 呼叫 ctor 後,會調用 `atexit()` 註冊相對應的 dtor,讓函數退出時呼叫 ### CRT 混用問題 ![](https://i.imgur.com/x4Dlilc.jpg) - 如果一個 process 裡面不同的 executable 使用了不同的 CRT 會有甚麼問題? - 防止連結到多個版本的 CRT,與 main object 連結時可以指定或禁止某 CRT 版本 - 靜態的 library 不會包含 CRT - 若都是動態 library 且使用同一個 CRT (Module 2、3),需要注意以下事情。若同時使用不同 CRT (Module 1、2),還是**可能會有奇怪的 bug 且很難發現**,即使沒有違反以下規則 - 不能在A申請記憶體,B釋放,因為兩者屬於不同的CRT,有不同的heap - 在A打開的文件不能在B使用,因為依賴於不同的CRT文件操作 - 不能共享locale等等 - You can avoid many of these issues (Allocated memory, CRT resources, or classes passed across a DLL boundary can cause problems in memory management, internal static usage, or layout interpretation) by using Application Binary Interface (ABI) technologies instead, as they are designed to be stable and versionable. ==為甚麼ABI可以避免一些問題? 難道可以在calling convention中設計動態記憶體的檢查或管理嗎?== - Design your DLL export interfaces to pass information by value, or to work on memory that is passed in by the caller rather than allocated locally and returned to the caller. #### 參考資料 - [What problems exist if an application uses more than one CRT version?](https://docs.microsoft.com/en-us/cpp/c-runtime-library/crt-library-features?view=msvc-160#what-problems-exist-if-an-application-uses-more-than-one-crt-version) ### CRT 與 Multithread - thread 私有空間 - local variable - function parameter - Thread Local Strorage (TLS) - thread 共享空間 - global variable - heap data - static variable - program code - file I/O (同一個CRT,只有一個打開文件列表) - 最初C/C++ runtime library設計時沒有考慮multithread,加入multithread會有一些挑戰如下 - errno: 在C standard library,大多數的error code在function return前,會存在errno的global variable - strtok()等函數: 會使用static variable - malloc、free、new、delete等: heap分配、釋放必須是加鎖,才會是thread safe - excaption handling: 早期C++,不同thread的throw會互相衝突 - I/O: 不同thread之間,共享I/O,所以必須加鎖 - 其他thread unsafe function: signal等等 - CRT 改進 - 使用TLS: 將一些原本的global variable放進TLS變成私有,例如`errno` - 加鎖: 原本thread unsafe會在內部自動加鎖 - 改進function API: 例如在Glibc thread safe 版本的 `strtok_r()` ### Thread Local Strorage (TLS) - 使用情境,希望使用thread獨有的global variable - 隱式的宣告TLS變量,前加上關鍵字 - MSVC是 `__declspec( thread )` ,例如: `__declspec( thread ) int tls_i = 1;`,參考[MSDN文件](https://docs.microsoft.com/en-us/cpp/parallel/thread-local-storage-tls?view=msvc-160) - gcc是 `__thread` ,例如: `__thread int i;` ,參考[gcc文件](https://gcc.gnu.org/onlinedocs/gcc/Thread-Local.html) - 顯式申請TLS變量,訪問變量時要調用相對應的函數來得到地址,訪問完後要釋放該變量。較麻煩,使用不當可能有bug,較推薦使用隱式 - Windows系統提供,`TlsAlloc()`、`TlsGetValue()`、`TlsSetValue()`、`TlsFree()` - Linux的pthread提供,`pthread_key_create()`、`pthread_getspecific()`、`pthread_setspecific()`、`pthread_key_delete()` ### 使用 Windows API 與 MSVC CRT 使用 thread 的差別? - `CreateThread()` 、 `ExitThread()` 是 Windows API - `_beginthread()` 、 `_endthread()` 是 MSVC CRT - 兩者混用可能造成memory leak - `_beginthread()` 是 `CreateThread()` 的包裝,在調用 `CreateThread()` 前,會建立一個 `_tiddata` 的結構,裡面裝的是: thread ID、thread Handle、erron、excption handle等與CRT有關的thread私有資料。 `_tiddata` 會由 `_endthread()` 來釋放。 - 如果使用 `CreateThread()` ,當呼叫到 CRT 相關函數(例如 `strtok()` ),會發現該thread沒有 `_tiddata` ,而自動建立一個並初始化 - 動態連結版CRT的startup function是 `DllMain` ,一個thread開始或退出時,每個DLL的 `DllMain` 都會被調用一次,於是就可以在 `DllMain` 釋放 `_tiddata` 。 - 靜態連結CRT沒有 `DllMain` ,使用 `CreateThread()` 也沒辦法使用 `_endthread()` 來釋放 `_tiddata` ,會導致memory leak - 結論是,會使用CRT的process要建立thread時(基本上都會用到CRT),盡量使用 `_beginthread()` > 總體思路總結,預設要使用上層API的thread library,因為可能會有一些變量是由該層來維護 ## CRT 與 Kernel API ### MSVC CRT 與 Windows API 之間的關聯? ![](https://i.imgur.com/AaFNa1L.jpg) - 對於User Space的程式來說,Windows API是可以用到的計算機的最低一層。 處於Windows API之上的則是CRT,它對作業系統進行了封裝,並隱藏了不同的作業系統之間的差異。 標準 C++ 庫則提供了更多的功能,並且把CRT作為它的一部分。 通過標準的函式以及相關類可以寫出跨平臺的程式,這個程式只需在新的平臺上重新編譯一次釋出出來,程式碼不需要改動。 根據應用程式的客觀需要,它可以靜態或動態的連結到C執行時程式庫和標準 C++ 程式庫。 每種方法都有它自己的優缺點。 - UCRT: Visual studio 2015 以後,CRT被重構成UCRT,包含C99標準,且各版本都支援multithread - vcruntime library: **Visual C++ CRT implementation-specific code**, such as exception handling and debugging support, runtime checks and type information, implementation details and certain extended library functions. The vcruntime library version needs to match the version of the compiler you're using. - CRT initialization and termination library: The code that initializes the CRT is in one of several libraries, based on whether the CRT library is statically or dynamically linked, or native, managed, or mixed code. This code **handles CRT startup, internal per-thread data initialization, and termination**. It's specific to the version of the compiler used. **This library is always statically linked**, even when using a dynamically linked UCRT. #### 參考資料 - [C runtime (CRT) and C++ Standard Library (STL) .lib files](https://docs.microsoft.com/en-us/cpp/c-runtime-library/crt-library-features?view=msvc-160) - [關於Windows API、CRT和STL二三事](https://www.itread01.com/p/382221.html) ### Linux API 與 crt1.o 、 glibc 之間的關係?==待完成== ## 靜態與動態 連結、library、CRT ### Windows 的靜態 連結、library、CRT - executable module: 包含動態函式庫 (dynamic-link library) 和 application,以下以 executable 簡稱。兩者不同之處在於 DLL 無法單獨執行,以系統的角度來看,主要有兩個不同點: - An application can have multiple instances of itself running in the system simultaneously. A DLL can have only one instance. - 延伸思考: DLL載入時,DLL的 static storage duration 和external linkage 的 object,在各 application 會獨有一份 .txt section 程式碼區段應該會共享,所以也會用到 virtual memory,mapping 到各 application - 如果共享 static storage 可以用來當作 shared memory - applications 可以載入成 process,**載入後擁有(own) stack、threads of execution、global memory、file handles、 message queue** - 靜態連結 (static linking): executable 在連結時期,從靜態函式庫 (static library),複製所需 library code 到自己的 code 中 - static library (.lib .a): Linux 和 Winodws 差不多: 一群 object file 的集合 - 編譯時期: 一群 object file 的集合,每個 object file 只有單純編譯 - 連結時期: - 連結 CRT 或其他 library: **static library 本身不會連結 CRT 或其他外部 library**,使用 static library 時只能透過靜態連結 - 被 executable 靜態連結: 使用到該 static library 的 executable 會複製所需的 library code 到自己的 code 中 - 載入時期: 因為所有需要的 static library 都已經在 application 中了,只要把 application 載入就好 - 執行時期: 因為所有需要的 static library 都已經載入了,不需要額外載入 - static CRT: 顧名思義,CRT 的靜態函式庫版本,executable 只能透過靜態連結來使用,使得該 executable 會有自己的 CRT ### Windows 的動態 連結、library、CRT - 動態連結 (dynamic linking): executable 在連結時期,只會從動態函式庫 (dynamic-link library) 取得該庫的位置和提供的 data、function 等資訊,不會複製 library code。 - executable 有兩種 linking DLL 的方式,使得 load DLL 的時間點會不同,分別是 **implicit linking** 和 **explicit linking**,前者會在 **load time** 載入,後者會在 **run time**。**以下整理兩者在編譯到執行期間相同的概念和過程**,兩者不同之處和概念細節獨立出來,整理在後方 - 優點: DLL 可以讓 library 更容易的在 executable 之間分享;多個 application 可以同時使用在 memory 的 DLL;**更容易的支持跨程式語言的 application,只要有共同遵守的 calling convention、linking convention,或是相同的 ABI** - ABI: 規定編譯出來的目標文件格式 (例如 Linux 上的 ELF 格式)、符號修飾標準 (name mangling)、memory 的安排方式、calling convention 等為了 Binary 等級兼容性的介面定義稱為 ABI。有 ABI 定義,OS 會照著 ABI 去執行程式,才可能連結和使用不同編譯器編譯出來的函示庫,或是使用其他程式語言編譯出來的函示庫 ![](https://upload.wikimedia.org/wikipedia/commons/thumb/b/bb/Linux_API_and_Linux_ABI.svg/1920px-Linux_API_and_Linux_ABI.svg.png) - Windows dynamic-link library (.dll): dynamic-link library (DLL)是一種 **executable file**,包含 shared library 的 functions 和 resources。Dynamic linking 是 OS 提供的一種技術,支援 executable 使用儲存在外部檔案的 functions 或 resources。OS 會 load DLL 到 **application 的 memory space** - 編譯時期: DLL 可以獨立於 executable,被個別編譯和部屬,**建立 DLL 時也會建立 import library (.lib) 來記錄 DLL 的位置和 DLL 提供的 data、function** - 連結時期: - 連結 CRT: **不管 DLL 使用靜態或動態的 CRT,VCRuntime (linker 預設包含 VCRuntime) 會在 DLL 中加入 entry-point function `_DllMainCRTStartup`**,此函數會負責接收 OS 的訊息來 attach to or detach from process or thread;處理必要的初始化和結束工作,像是 stack buffer security set up、**CRT**;建構和解構 static、global objects;呼叫外部 library (WinRT, MFC, ATL) 的 hook function,進行初始化和結束工作;VCRuntime 本身的初始化和結束工作;**還有該函示庫自己定義的 `DllMain`** - 連結其他 library: ==若連結到其他動態庫,行為應該也一樣?== - 被 executable 動態連結: executable 有兩種 linking DLL 的方式,使得 load DLL 的時間點會不同,分別是 **implicit linking** 和 **explicit linking**,前者會在 **load time** 載入,後者會在 **run time** - 載入時期: attach、dettach DLL 到 process 或 thread會呼叫 `_DllMainCRTStartup` - 執行時期: DLLs 不是一個stand-alone executable,需要被 applications 呼叫才能執行。==補充 app 執行時的行為== - dynamic CRT: 連結時預設應該是這個CRT,缺點是要找版本要相符 #### implicit linking - implicit linking (so called static load or load-time dynamic linking): DLL 會在 application load 的階段一起被 load,跟 static linking 很像,但不會把 code 放入 application - 編輯程式: 標頭檔 (.h) 有該函示庫所提供的 exported data、functions、C++ classes,並且標註 `__declspec(dllimport)` - 連結時期: 在 application 編譯後,使用到外部 DLL 的部分,該 DLL 會將相對應的 external function reference 放入 application。連結階段,會與 DLL 對應的 import library (.lib) 進行連結,所以會將所需要的 import library 內容匯入 application - 載入時期: OS 找到 DLL 並載入 - 優點: 相對 explicit linking 容易使用 #### explicit linking - explicit linking (so called dynamic load or run-time dynamic linking): DLL 會在 application runtime 的階段,需要使用時被 load,並透過 function pointer 來呼叫,使用完要被 unload。 - 編輯程式: 使用 `LoadLibraryEx`、`GetProcAddress`、`FreeLibrary` 等 API 來控制 DLL 的使用 - 執行時期: 在執行到相關 API 時控制 DLL - 優點: - 當所需的 DLL 直到 runtime 才能被準備好,DLL 可能要根據 runtime 時一些設定來產生,這時就能使用 explicit linking - 可以在 runtime 提醒使用者重新給位址尋找 DLL - 在連結時期不需要連結 import library,執行前更改 DLL 不需要重新連結 - 缺點: - 如果函式庫的設計依賴 `DllMain` 來做 thread 和 process 的初始化,當 process 已經載入 DLL 且初始化,若有 thread 也要載入該 DLL 時,會造成該 thread 無法透過 `DllMain` 來初始化。因為載入 DLL 時 OS 會呼叫 `LoadLibrary`,只有在對應的 `FreeLibrary` 被呼叫後,`LoadLibrary` 才能再次被呼叫 - 如果 DLL 裡宣告了 static-extent data (global、local static、TLS items),可能會出現問題。因為當載入 DLL 時會初始化這些 static-extent data,而原來執行的 code 可能有 reference 到這些 data #### 相關資料 - [Walkthrough: Create and use your own Dynamic Link Library (C++)](https://docs.microsoft.com/en-us/cpp/build/walkthrough-creating-and-using-a-dynamic-link-library-cpp?view=msvc-160) - implicit linking with DLL 範例 - [Create C/C++ DLLs in Visual Studio](https://docs.microsoft.com/en-us/cpp/build/dlls-in-visual-cpp?view=msvc-160) - [DLLs and Visual C++ run-time library behavior](https://docs.microsoft.com/en-us/cpp/build/run-time-library-behavior?view=msvc-160) - [Link an executable to a DLL](https://docs.microsoft.com/en-us/cpp/build/linking-an-executable-to-a-dll?view=msvc-160) - [MSVC 與 CRT 之間的恩怨情仇](https://blog.xuite.net/kamory0931/fightdreamer/243316361-MSVC+%E8%88%87+CRT+%E4%B9%8B%E9%96%93%E7%9A%84%E6%81%A9%E6%80%A8%E6%83%85%E4%BB%87) - [What is an application binary interface (ABI)?]( - 使用者的電腦,必須先安裝「Visual C++ 可轉發套件」(MSVC 2008 或 MSVC 2005 )。將所需的 **DLL** 檔案,例如 MSVCR90D.dll 與 MSVCP90D.dll,直接附在程式的下載包當中。以**靜態連結**方式建置程式執行檔。 ==DLL可以靜態連結?應該指的是implicit linking== ### 為甚麼每個DLL的 DllMain 都會被調用一次? 每個DLL會有各一個CRT沒錯,但是在其中一個使用MSVC CRT的function (例如 `_beginthread()`),為甚麼其他的CRT也會被通知? ==待完成== - Q: 這裡的靜態連結CRT指的是甚麼? library靜態連結CRT,還是main object靜態連結CRT? 還是兩者皆是? - 應該指的是"main object靜態連結CRT" - 範例中的靜態連接CRT (`/MT`, `/MTd`),是使用Multithread、static version CRT "LIBCMT.lib",compiler會將"LIBCMT.lib"放入 .obj file,linker會使用"LIBCMT.lib"來解析external symbols - Q: .obj file 是甚麼? ## 不同 thread library 之間的關係 ==待完成== ### thread 在 CRT 與 Kernel thread 或 第三方thread(Boost) 之間的關係? - [Toward Concurrency](https://hackmd.io/@sysprog/Skh_AaVix?type=view#Functional-Programming) - [From Source to Binary: How A Compiler Works: GNU Toolchain](https://hackmd.io/@sysprog/c-prog/%2Fs%2FHy72937Me#From-Source-to-Binary-How-A-Compiler-Works-GNU-Toolchain) - [為什麼 thread (執行緒、線程)越少越好?](https://cntofu.com/book/46/other/wei_shi_me_thread_zhi_xing_xu_3001_xian_7a0b29_yue.md) ## Compiler Optimization ### LLVM IR Optimizer - [Understanding Compiler Optimization - Chandler Carruth - Opening Keynote Meeting C++ 2015](https://www.youtube.com/watch?v=FnGCDLhaxKU) - [Single-Static Assignment Form and PHI](https://mapping-high-level-constructs-to-llvm-ir.readthedocs.io/en/latest/control-structures/ssa-phi.html) ## 其他問題 - forward declaration (例如放在 header file) 改變,是否會需要重新編譯引入的 source file (該 source file 沒有改變) - enum class 不用 - C++ contructor不能用遞迴? ```C++ /** * // This is the interface that allows for creating nested lists. * // You should not implement it, or speculate about its implementation * class NestedInteger { * public: * // Return true if this NestedInteger holds a single integer, rather than a nested list. * bool isInteger() const; * * // Return the single integer that this NestedInteger holds, if it holds a single integer * // The result is undefined if this NestedInteger holds a nested list * int getInteger() const; * * // Return the nested list that this NestedInteger holds, if it holds a nested list * // The result is undefined if this NestedInteger holds a single integer * const vector<NestedInteger> &getList() const; * }; */ class NestedIterator { vector<int> flat; int cur = 0; public: void flatten(vector<NestedInteger> &nestedList) { for(auto& nl : nestedList) { if(nl.isInteger()) { flat.push_back(nl.getInteger()); } else { flatten(nl.getList()); } } } NestedIterator(vector<NestedInteger> &nestedList) { //flatten(nestedList); // correct version // Error version /************************************/ for(auto& nl : nestedList) { if(nl.isInteger()) { flat.push_back(nl.getInteger()); } else { flatten(nl.getList()); } } /***********************************/ } int next() { return flat[cur++]; } bool hasNext() { return cur < flat.size(); } }; /** * Your NestedIterator object will be instantiated and called as such: * NestedIterator i(nestedList); * while (i.hasNext()) cout << i.next(); */ ``` - pointer vs. array in C - `sizeof(ptr)` 是該指標 object 的記憶體大小,在 32 bits 系統下為 4 (byte) - `sizeof(arr)` 是該陣列 object 的記憶體大小,若宣告 `char arr[8];`,則為 8 (byte) - 陣列操作,`arr[1]` 等於 `*(arr + 1)` ```c #include <stdio.h> int main() { int arr[3][2][2] = { {{0, 1}, {2, 3}}, {{4, 5}, {6, 7}}, {{8, 9}, {10, 11}}}; printf("arr + 1 = %p\n", arr + 1); printf("&arr[1] = %p\n", &arr[1]); printf("&arr[1][0][0] = %p\n", &arr[1][0][0]); printf("*arr + 1 = %p\n", *arr + 1); printf("&arr[0][1] = %p\n", &arr[0][1]); printf("&arr[0][1][0] = %p\n", &arr[0][1][0]); printf("**(arr[1] + 1) = %d\n", **(arr[1] + 1)); printf("**(*(arr + 1) + 1) = %d\n", **(*(arr + 1) + 1)); printf("arr[1][1][0] = %d\n", arr[1][1][0]); printf("**((arr + 1)[1]) = %d\n", **((arr + 1)[1])); printf("**(arr[2]) = %d\n", **(arr[2])); printf("arr[2][0][0] = %d\n", arr[2][0][0]); /* **(arr[1] + 1) = 6 **(*(arr + 1) + 1) = 6 arr[1][1][0] = 6 **((arr + 1)[1]) = 8 **(arr[2]) = 8 arr[2][0][0] = 8 */ } ``` - volatile - 保證每次從該 object 的記憶體存取,而不做最佳化,例如從 register 取值 - 通常用於 memory io、interrupt,ansync 且 non-concurrent 的情況 - 並沒有 atomic 性質,一樣會造成 race condition - C++物件導向三大特性(封裝、繼承、多型) - 封裝: 只提供物件的操作介面,而隱藏和保護物件的細節和資料 - 繼承: 提高程式碼的再利用效率,可以繼承物件的interface or implement - 普通繼承: 直接複製父類別,無法使用多型介面來呼叫 - virtual class: 繼承interface和implement,可覆蓋implement - pure virtual class: 只繼承interface,強制自行定義implement - 多型: 提供不同型態物件相同的介面給caller,實際執行的物件在run-time決定(run-time binding) - Overriding: 繼承相同參數的介面,覆蓋並重新定義implement - Overloading: 不同參數的介面,定義各自的implement - union - Union裡的變數共用記憶體空間,因此不能同時使用Union裡的多個變數,一次只能存取一個 - Union的記憶體大小為Union裡最大變數的記憶體大小 - reference & pointer - reference must be initialized but pointer don't - reference can't refer to other but pointer can - class life time - Const member function: 不能修改member data or this pointer (T is a class type) - void T::fun() const; - By default, 在member function裡this的type是 T *const. In const member function, this的type是 const T *const - Construct - 初始化的順序是class member declare的順序,所以初始化時要按照declare的順序才不會混亂 - Destruct - base classes ordinarily should define virtual destructor ## 面試考題整理 - [C面試考題](https://hackmd.io/@a110605/By6DscbVM?type=view) - [發哥(聯發科)上機考題目整理](https://hackmd.io/tpspI4IYSROcE9ATbpH78g?view) - [GeekforGeek](https://www.geeksforgeeks.org/quiz-corner-gq/) - [C Operator Precedence - cppreference](https://en.cppreference.com/w/c/language/operator_precedence) - [C++ Operator Precedence](https://en.cppreference.com/w/cpp/language/operator_precedence) ## 主要參考資料 - 程式設計師的自我修養-連結、載入、程式庫 - MSVC 官網 - [你所不知道的 C 語言: 執行階段程式庫 (CRT)](https://hackmd.io/@sysprog/c-runtime?type=view)