--- title: C語言 description: --- # C語言 ###### tags: `Interview` ## pointer ```c= int a; // a integer int *a; // a pointer to integer int **a; // a pointer to pointer to a integer int a[10]; // an array of 10 integer int *a[10]; // an array of 10 pointer to integer int (*a)[10]; // a pointer to an array of 10 integer int (*a)(int); // a pointer to function(int) returning int int (*a[10])(int); // an array of pointer to function(int) returning int ``` ## function pointer ```c= int (*f()) int *f() ``` * declare f as function *returning* **pointer to int** ```c= int (*f)() ``` * declare f as **pointer to function** *returning* int * 俗稱 : function pointer ```c= int (*f())[]; ``` * declare f as function *returning* **pointer to array of int** ```c= int (*f())()[] ``` * declare f as function returning **pointer to function** *returning* array of int ```c= int (*f[])() ``` * declare f as array of **pointer to function** returning int * 俗稱 : function pointer array ### function ptr 小技巧 ```c= 可將這個改成下面那樣 // use function pointer to declare int operation(int a, int b, int (*operation)(int, int)) { return operation(a, b); } 這樣子比較容易閱讀 typedef int (*calculation)(int, int); // 之後宣告都可以用 calculation 就好 // use calculation to declare int operation(int a, int b, calculation calc) { return calc(a, b); } ``` ## const * [難懂的const關鍵字,const v.s. 指標](https://ithelp.ithome.com.tw/articles/10230783) * const 通常表示只可讀取不可寫入的變數,常用來宣告常數。使用const有以下好處: * 提升程式碼可讀性 * 使編譯器保護那些不希望被改變的參數 * 給優化器一些附加的資訊 * 編譯器處理方式 : define 在預處理階段展開;const 在編譯階段使用。 * 類型和安全檢查 : const 會在編譯階段會執行類型檢查,define 則不會。 * 存儲方式 : define 直接展開不會分配記憶體,const 則會在記憶體中分配。 ```c= char *const p2 = &c; // (const pointer to char) 限制不可以 改變地址本身 const char *p1 = &c; // (pointer to const char) 限制不可以透過地址去 改變變數的內容 ``` ## static * `local`變數 : local 變數僅活在該函式內,存放位置在 stack 或 heap 記憶體中。 * `static`變數 : static變數生命周期(life time)跟程式一樣長,而範圍(scope)則維持不變,即在宣告的函式之外仍無法存取static變數。 * `global`變數 : 所有區段皆可使用此變數。 * http://ashinzzz.blogspot.com/2013/12/extern-static.html * https://www.google.com/search?q=extern+static+function&oq=extern+static&aqs=chrome.1.69i57j0l5j69i60l2.4323j0j7&sourceid=chrome&ie=UTF-8 ## volatile * [volatile的正確用法](https://www.ptt.cc/bbs/C_and_CPP/M.1410942542.A.B3E.html) * 由於嵌入式系統常處理 I/O、中斷、即時操作系統 (RTOS) 相關的問題,因此在嵌入式系統開發中 volatile 尤為重要。**被 volatile 修飾的變數代表它可能會被不預期的更新**,因此告知編譯器**不對它涉及的地方做最佳化**,並在每次操作它的時候都讀取該變數**實體位址**上最新的值,而不是讀取**暫存器**的值。 ## inline vs macro > macro 跟 inline 用 空間換取時間 > function 用時間換取空間 * inline 可以將修飾的函式設為行內函式,即像巨集 (macro) 一樣將該函式展開編譯,用來加速執行速度。 * inline 函數的參數類型被檢查,並進行必要的型態轉換。 ```c= #define B(N) (N+N+N) int GetValue=7; inline int ADD(int n){ return n+n+n; } int main() { int result1,result2; result1 = B(GetValue); result2 = ADD(GetValue); printf("B=%d\n",result1); printf("C=%d\n",result2); return 0; } result1 會呼叫GetValue三次 來做N+N+N result2 則是呼叫一次GetValue,然後再把三個加起來 這是兩個編譯時候的差異 然後inline還有一個問題就是,編譯器會自動判斷是否用inline展開函式合乎效益 例如若函式太長 然後在程式呼叫點中展開效果不那麼好 編譯器會忽略inline來做程式的編譯 ``` ## extern * 可以聲明變數會在其它的位置被定義,這個位置可能是在同一份文件之中,或是在其它文件之中 * 看到「extern」,至少會讓「人」知道這個變數已經在別處定義了。 * 不能在使用extern時同時指定其值,必須先聲明extern找到變數,再重新指定其值 ## [#error](https://www.itread01.com/content/1541838451.html) * ```#error``` 預處理指令的作用是,編譯程式時,只要遇到 ```#error``` 就會生成一個編譯錯誤提示訊息,並停止編譯。其語法格式為: ```c= // glibc中常見 #if !defined(__cplusplus) #error C++ compiler required. #endif ``` >在 C 語言中,沒有內建的 try ... catch ... 控制結構,使用一般的控制結構來處理錯誤,類似第二種方式。但 C 語言本身沒有錯誤物件 (error object) 的概念,通常是藉由回傳常數來代表錯誤的狀態。 ## [union vs structure](https://www.youtube.com/watch?v=KL65a0TyeYo) * 在存儲多個成員信息時,編譯器會自動給struct第個成員分配存儲空間,struct 可以存儲多個成員信息,而Union每個成員會用同一個存儲空間,只能存儲最後一個成員的信息。 * union 乍看和 structure 有點像,但union內的屬性共用同一塊記憶體,故同一時間內僅能用union內其中一種屬性。union主要用來表示同概念但不同資料類型的實體。 * struct是不能用==跟!=來判斷是否相等的,因為struct的成員變數在記憶體空間裡面有可能是不連續的,但可以用=,若要比較兩個structure是否相等,要比較其中的每個element(element的比較可以用\== ) ## typedef * typedef保留字可以為資料型態建立別名,例如 ```typedef unsigned uint;```,之後uint就會被當作unsigned來看待 ## lvalue vs rvalue * 左值(lvalue):左值是保存在單一運算式之外的物件。您可以將左值當做具有名稱的物件。所有變數都是左值,包括不可修改的 (const) 變數。 * ++x * 右值(rvalue):右值是暫存值,不會在使用它的運算式之外保存。 * x++ * 不會特別用一個變數去保存他的 ## fopen vs open * **fopen**:緩衝IO * 緩衝IO,會先讀一塊到memory中,才不會每次要讀多少就讀一次,這樣會syscall好幾次。 * **open**:非緩衝IO * 非緩衝IO,每次要讀多少,就要叫一次syscall。 ## sizeof implementation ```c= #define my_sizeof(x) ((char *)(&x+1) - (char *)(&x)) ``` ## attribute ```c= __attribute((packed))__ // 如果**struct**加上**__attribute__((packed))**編譯器就不會做優化把大小用成2的倍數,會按照struct裡面原本的大小。 __attribute((aligned(16)))__ // 分配的空間會按照16bytes對齊 ``` ## # and ## Operators * #會把後面接的字串,在pre-processor階段時加上""。 ```c= #include <stdio.h> #define mkstr(s) #s int main(void) { printf(mkstr(geeksforgeeks)); return 0; } output : geeksforgeeks ``` * ##則是把##前後的字串相接 ```c= #include <stdio.h> #define concat(a, b) a##b int main(void) { int xy = 30; printf("%d", concat(x, y)); return 0; } output : 30 ```