回首C語言關鍵字(~回首向來蕭瑟處~)

開篇廢話: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的關鍵字,後續完善.....

學習

 

後記

 

《意頹廢》

意頹廢,人難寐。
夜夜長街空買醉。
路茫茫,斷離腸。
夢牽魂索,獨守西窗,傷!傷!傷 。

花兒碎,風流淚。
曲悲絃斷連心肺。
戀成殤,心透涼。
一朝白頭,只爲情狂,愴!愴!愴。

 

相關文章
相關標籤/搜索