開篇廢話:html
本文意在回顧 C 語言中的關鍵字,整理文件發現當時作的這些筆記仍是蠻用心的,有臨摹node
前輩的足跡也有本身的理解和體會。時至今日2018已經跨過一半,對不起過去半年,今天面試
拿這篇關鍵字開篇,開啓本身的程序猿心路,主要記錄一下本身遇到的問題和學習的經歷,編程
方便本身。若是能對別也有用那就更開心了,因爲本身還很菜,理解和體會都頗有限,如json
果你打開發現了錯誤還請不吝賜教,隨便評論,不要客氣,我都會很感激的。首篇廢話就數組
這麼多吧,我可能廢話比較多,哈哈,批評我吧~多線程
C 關鍵字函數
/** * 到目前C語言中有32+5+7=44個關鍵字,具體以下: * * ->C89關鍵字 * * char short int unsigned * long float double struct * union void enum signed * const volatile typedef auto * register static extern break * case continue default do * else for goto if * return switch while sizeof * * ->C99新增關鍵字 * * _Bool _Complex[複雜的] _Imaginary[虛構的] inline restrict[限定/約束] * * * ->C11新增關鍵字 * * _Alignas Alignof _Atomic _Generic * _Noreturn _Static_assert _Thread_local * **/
C89 32個關鍵字解釋
oop
/* 1) char 解釋: 聲明變量的時候用,char 佔1個字節8bit,多數系統上是有符號的(arm 上無符號) 範圍 [-128, 127] 在工程項目中開發推薦用 int8_t -> singned char uint8_t -> unsigned char */ char c = 'p'; /* 2) short 解釋: 聲明變量的時候用,short佔2個字節,爲無符號的,默認自帶signed 範圍[-2^15, 2^15-1]2^15 = 32800 推薦使用 int16_t or uint16_t 類型 */ short port = 8080; /* 3) int 解釋: 聲明變量的時候用,int聲明的變量佔4個字節,有符號,範圍[-2^31,2^31-1]2^31 推薦使用:int32_t or uint32_t 類型開發,方便移植 */ int i = 0; /* 4) unsigned 解釋: 變量類型修飾符,被修飾的變量就是無符號的,範圍>=0,unsigned 只能修飾整型的變量 固然你用這個修飾變量的時候,再使用++和--運算的時候必定要當心。 */ unsigned int i = 0; //正確 unsigned short s = 0; //正確 unsigned float f = 0.11f; //錯誤 /* 5) long 解釋: 聲明變量的時候使用,長整型x86上4個字節,x64上8個字節,必定不比int字節數少 c99以後出現long long 類型爲8個字節 */ long l = 4; long long ll = 8; /* 6) float 解釋: 聲明變量的時候用,4個字節,精度是6-7位, 詳細精度能夠看:https://blog.csdn.net/dxy612/article/details/5518477 */ float f = -0.12f; /* 7) double 解釋: 聲明變量的時候用,8個字節,精度在15-16位左右,有的時候壓縮內存用float代替 */ double d = 2e13; /* 8) struct 解釋: 定義結構體,這個關鍵字用法很廣,是大頭。C的重要的思路就是面向過程編程, 撐起面向過程的大頭就是結構體。 */ // 普通結構體 struct node { int id; struct node *next; }; struct node n = {1,NULL}; // 匿名結構體 struct { int id; char* name; } per = {2,"Tom"}; /* 9) union 解釋: 定義共用體,用法很花哨,常在特殊庫函數封裝中用到,技巧很強 */ // 普通定義 union type { char c; int i; float f; }; union type t = {.f = 3.33f}; // 匿名定義 union {...} t = {...}; //類型匿名定義 struct cjson { struct cjson *next; //採用鏈表結構處理,放棄二叉樹結構,優化內存 unsigned char type; // 數據類型和方式定義,一個美好的願望 char *key; // json內容那塊的key名稱 union { char *vs; // type == _CJSON_STRING, 是一個字符串 double vd; // type == _CJSON_NUMBER, 是一個num值((int)c->vd)轉成int或bool }; }; /* 什麼是大小端? Endian表示數據在存儲器中的存放順序, 大端(Big Endian): 是指數據的高字節保存在內存的低地址中,而數據的低字節保存在內存的 高地址中,這樣的存儲模式有點兒相似把數據當作字符串順序處理:地址由小向大增長, 兒數據從高位往低位。 小端(Little Endian): 是指數據的高字節保存在內存的高地址中,而數據的低字節保存在 內存的低地址中,這種存儲模式將地址的高低和數據位權有效的結合起來,高地址部分權值 高,低地址部分權值低,和咱們的邏輯方法一致。 這兩種模式,泥瓦匠記憶宮殿:「小端低低」。這樣就知道小端的模式,反之大端的模式。 https://www.cnblogs.com/Alandre/p/4878841.html */ // 在來一種union用法,判斷大小端,筆試題中長見 inline bool sh_isbig(void) { static union { unsigned short s; unsigned char c; }U = {1}; // 1 -> 0x0001; return U.c == 0; } /* 10) void 解釋: 這個關鍵字用法不少,也是用在函數聲明中,或函數參數 */ // 函數聲明 extern void foo(); // 函數參數約束 extern void foo(void); //函數參數爲void表示函數是無參數的,不然是任意的 // 萬能類型定義,指針隨便轉 void* vp = NULL; /* 11) enum 解釋: 枚舉類型,C中枚舉類型很簡陋,至關於一種變相的INT宏常量,估計也許是INT宏 常量和枚舉並存的緣由 問題1 有些面試題中會問你enum 和#define 的區別: 1. #define 宏常量是在預編譯階段進行簡單的替換;enum常量則是在編譯的時候肯定其值 2. 通常在調試器裏,能夠調試枚舉常量,但不是不能調試宏常量。 3. 枚舉能夠一次定義大量相關的常量,而#define 宏一次只能定義一個。 問題2 1. 枚舉能作的事,#define 宏能不能作到?若是能,那爲何還須要枚舉? 答:能,枚舉能夠自增1,這樣不用每個值都定義,而宏必須每一個值都定義, 而枚舉是一個集合,表明一類值,像代碼中的顏色歸爲一類方便使用,而#define不能造成集合 2. sizeof(ColorVal)的值是多少?爲何? enum Color{ GREEN = 1, RED, BLUE, GREEN_RED = 10, GREEN_BLUE }ColorVal; 答:值爲4,ColorVal一個枚舉變量,而枚舉變量表明一個整數。 */ // // flag_e -全局操做基本行爲返回的枚舉,用於判斷返回值狀態的狀態碼 // >=0 標識Success狀態,< 0 標識Error狀態 // // 枚舉變量徹底能夠等同於int變量使用,枚舉值等同於宏INT常量使用,枚舉的默認值 // 是以1爲單位從上向下遞增。 // typedef enum { Success_Exit =2, //但願存在,設置以前已經存在了 Success_Close =1, //文件描述符讀取關閉,讀取完畢也返回這個 Success_Base =0, // 結果正確的返回宏 Error_Base =-1, //錯誤類型,全部錯誤都用它,在不清楚的狀況下 Error_Parm =-2, //調用參數錯誤 Error_Alloc =-3, //內存分配錯誤 Error_Fd =-4, //文件打開失敗 } flag_e; /* 12) signed 解釋: 變量聲明類型修飾符,有符號型,對比unsigned無符號型,變量聲明默認基本都是 singned ,全部多數別就省略了 */ signed int piyo = 0x12345678; /* 13) const 解釋: const 修飾的變量表示是一個不可修改的量,和常量有點區別, */ // 聲明不可修改的量 const int age = 24; // 修飾指針 const int *pa = NULL; //pa指向的值(*pa)是不能修改 int *const pt = NULL; //pt不能指向新的指針,pt指向的值(*pt)能夠修改 const int *const pc = NULL; //pc和pc指向的值(*pc)都不能修改 /* 14) volatile 解釋: 聲明變量修飾符,可變的,當變量前面有這個修飾符,編譯器再也不從寄存器中取值 直接內存讀取寫入,保證明時性,常在多線程代碼中 */ // 具體輪詢器 struct srl { mq_t mq; // 消息隊列 pthread_t th; // 具體線程 die_f run; // 每一個消息都會調用run(pop()) volatile bool loop; // true表示還在繼續 }; // 之後再使用loop的時候,其餘線程修改,當前線程也能正確的獲取它的值 /* 15) typedef 解釋: 類型重定義修飾符,從新定義新的類型,給變量去別名 */ // 聲明普通類型 typedef void* list_t; // 聲明不徹底類型,頭文件中不存在struct tree typedef struct tree * tree_t; // 重定義變量類型 typedef unsigned long int ub4; /* unsigned 4-byte quantities*/ typedef unsigned char ub1; /* unsigned 1-byte quantities*/ // 定義一個函數指針 typedef uint32_t (*crc_func)(uint32_t crc, const void *buf, size_t len); // 使用 crc_func crc32c; //聲明一個crc_func類型的變量,實際就是一個函數指針,指向函數的首地址 /* 16) auto 解釋: 變量類型聲明符,auto變量存放在動態存儲區,隨和聲明週期{開始},結束而當即釋放,存在在棧上 默認變量都是auto的,基本是不寫 */ //演示 { // 生存期開始 int a = 0; auto int p = 1; // 生存期結束 } /* 17) register 解釋: 修飾變量,這個關鍵字請求編譯器儘量的將變量存放在CPU內部寄存器中而不是經過內存尋址訪問 以提升效率,注意是儘量,不是絕對。 實用register修飾的注意點 雖然寄存器的速度很是快 ,可是實用register修飾符也要些限制的,register變量必須是能被cpu寄存器所能接受的類型 意味着register變量必須是一個單個的值,而且其長度應小於或等於整型的長度,並且register變量 可能不存在在內存中,因此不能用取地址運算符"&"來獲取register變量的地址。 */ #include <limits.h> register int i = 0; while (i < INT_MAX) { ++i; } /* 18) static 解釋: static用法很廣,修飾變量,函數,從字面上看static 很安靜,這個關鍵字在C++ 中作了擴展,在C語言中重要就前面提到的兩個做用。 1. 修飾變量 變量又分爲局部變量和全局變量,但它們都在內存的靜態區。 靜態全局變量,做用域僅限於變量被定義的文件中,其餘文件即便用extern聲明也沒有辦法使用 準確的說:做用域是從定義之處開始,到文件結尾處結束, 靜態局部變量:在函數體裏面定義的,就只能在這個函數裏用了,同一個文檔中的其餘函數也 用不了,因爲被static修飾的變量老是存在內存的靜態區,全部即便這個函數運行結束,這個 靜態變量的值也不會被銷燬,函數下次使用時任然能用這個值。 看下面一段代碼: static int j; void fun1(void) { static int i = 0; i++; } void fun2(void) { j = 0; j++; } int main() { int k = 0; for(k = 0; k < 10; k++) { fun1(); fun2(); } return 0; } 此時i和j的值分別是多少? 咱們來分析一下哈: 首先毫無疑問,j 是個全局靜態變量,調用一次fun2()後,它的值始終沒有變,全部調用10次值仍是1 i這個變量是一個局部靜態變量,值存放在內存的靜態區,調用一次fun1()結束後它的值不會被銷燬, 函數下次調用的時候任然使用這個值,全部調用10次它的值一次爲,1,2,3,4,5,6,7,8,9,10,11 2. 修飾函數 函數前面加static使得函數成爲靜態函數,但此處"static"的含義不是指存儲方式,而是指對函數做用域 僅侷限於本文件(全部稱內部函數)使用內部函數的好處是:不一樣的人編寫不一樣的函數時,不用擔憂本身定義 的函數是否會與其餘文件中的函數同名。 // C99以後加的static新用法,編譯器優化 / static 只能修飾函數第一維,表示數組最小長度,方便編譯器一下取出全部內存進行優化 */ int sum(int a[static 10]) { ... } /* 19) extern 解釋: extern (外面的,外來的)能夠置於變量或函數前,以代表變量或函數的定義在別的文件中 下面代碼用到的這些變量或函數是外來的,不是本文件中定義的,提示鏈接器遇到此變量 和函數時在其餘模塊中解析/綁定此標識符。 */ // 聲明引用全局變量 extern int G_arg; // 聲明引用全局函數,(主動聲明,但願外部能夠調用) extern int kill(int sig, int val); // 固然有時候extern不寫,對於變量不寫會出現重定義,對於函數時能夠缺省的 /* 20) break 解釋: 結束語句,主要用於循環的跳出,只能跳出到當前層級,也用於switch語句中 跳出swithc嵌套 */ // 演示 for循環 for(;;) { // 符合條件跳轉 if(n == 10) break; // 跳出for循環 } /* 21) 22) 23) switch & case & default 解釋: 21)switch :條件分支語句,很複雜的if else if時候能夠用switch 22)case : 語句中分支語句,肯定走哪一個分支 23)default: switch 分支的默認分支,全部case都沒有進入就到default分支 */ // 演示 switch (errcode) { case SSL_ERROR_ZERO_RETURN: /* Possibly a clean shutdown. */ if (SSL_get_shutdown(bev_ssl->ssl) & SSL_RECEIVED_SHUTDOWN) event = BEV_EVENT_EOF; else dirty_shutdown = 1; break; case SSL_ERROR_SYSCALL: /* IO error; possibly a dirty shutdown. */ if ((ret == 0 || ret == -1) && ERR_peek_error() == 0) dirty_shutdown = 1; break; case SSL_ERROR_SSL: /* Protocol error. */ break; case SSL_ERROR_WANT_X509_LOOKUP: /* XXXX handle this. */ break; case SSL_ERROR_NONE: case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_WRITE: case SSL_ERROR_WANT_CONNECT: case SSL_ERROR_WANT_ACCEPT: default: /* should be impossible; treat as normal error. */ event_warnx("BUG: Unexpected OpenSSL error code %d", errcode); break; } /* 24) continue 解釋: 跳過這次(本輪)循環,直接進行條件判斷操做,進入下一輪循環, for和while循環有些區別,for會執行第三個後面的語句 */ // 演示 for(int i = 0; i < 20; ++i) { if(i % 2 == 0) continue; // 知足if跳到 ++i,再到條件 i<20 } /* 25) do ... 26) while 27) for 解釋: C 語言中的三種循環 25) do: do循環,先執行循環體,在執行條件判斷,先保證循環體執行一遍在判斷條件 在一個菜單的程序設計的時候用do...while 比較好,首先給出選擇。 26) while: 循環,先判斷while後的條件,只有條件真才執行裏面的代碼塊, 經常使用的還有就是一種死循環的寫法 while(1) 27) for: 循環,for循環很容易的控制循環次數,多用於事先知道循環次數的狀況下 */ // 演示 /* do -while 循環 */ do { event_loop(EVLOOP_ONCE | EVLOOP_NONBLOCK); xcount++; } while (count != fired); /* while 循環 */ while(1) { if('#' == GetInputChar()) break; } /* for 循環 */ for (i = 0; i < 25; i++) { tv = run_once(); if (tv == NULL) exit(1); fprintf(stdout, "%ld\n", tv->tv_sec * 1000000L + tv->tv_usec); } /* 28) if ... 29) else 解釋: 28) if: if分支語句,能夠單獨使用,可嵌套 29) else: else分支,必須和if分支對應,和if分支條件相反 */ n = recv(fd, (char*)&ch, sizeof(ch), 0); if (n >= 0) count += n; else failures++; if (writes) { if (widx >= num_pipes) widx -= num_pipes; n = send(pipes[2 * widx + 1], "e", 1, 0); if (n != 1) failures++; writes--; fired++; } /* 30) goto 解釋: 關於goto這個關鍵字,褒貶不少,緣由就在於它太自由,能夠靈活的跳轉,在結構化 編程中它有了不少爭議,若是不加以限制,它的自由跳轉的確會破壞結構化設計的風格 全部使用它必定要慎重,下面咱們用一段真實代碼展現它的魅力, */ // 演示 if (evbuffer_expand_fast_(buf, to_alloc, 2) < 0) { goto done; } for (n = 0; n < n_vec; n++) { /* XXX each 'add' call here does a bunch of setup that's * obviated by evbuffer_expand_fast_, and some cleanup that we * would like to do only once. Instead we should just extract * the part of the code that's needed. */ if (evbuffer_add(buf, vec[n].iov_base, vec[n].iov_len) < 0) { goto done; } res += vec[n].iov_len; } done: EVBUFFER_UNLOCK(buf); return res; /* 31) return 解釋: return 用來終止一個函數並返回其後面跟着的值 使用return 不可返回指向「棧內存」的「指針」,由於該內存在函數體結束後就被自動釋放了 */ // 演示 #include <stdio.h> int main(int arg, char* argv[]) { ...... return EXIT_SUCCESS; } /* 32) sizeof 解釋: 也稱爲sizeof運算符,計算變量或類型的字節大小,它常被誤認爲是個函數 面試中也常常有它出現,下面咱們看看它的用法 */ // 演示 x86上 int *p = NULL; sizeof(p) 的值是多少 ---> 4 sizeof(*p)呢? ---> 4 int a[100]; sizeof(a)的值是多少 ---> 400 sizeof(a[100])的值 ---> 4 sizeof(&a)的值 ---> 4 sizeof(&a[0])的值 ---> 4 int b[100]; void func(int b[100]) { sizeof(b); // sizeof(b)的值 ---> 4 } // 經常使用一種寫法獲取數組長度 #define LEN(arr) (sizeof(arr) / sizeof(*(arr))) #define LEN(arr) (sizeof(arr) / sizeof(arr[0]))
到此 C89 的32個關鍵字都就介紹完了,對於 C99 和 C11的關鍵字,後續完善.....
學習
後記:
《意頹廢》
意頹廢,人難寐。
夜夜長街空買醉。
路茫茫,斷離腸。
夢牽魂索,獨守西窗,傷!傷!傷 。
花兒碎,風流淚。
曲悲絃斷連心肺。
戀成殤,心透涼。
一朝白頭,只爲情狂,愴!愴!愴。