1. 程式人生 > >讀書筆記-C語言關鍵字

讀書筆記-C語言關鍵字

C語言 C語言關鍵字

001 關鍵字
  • C語言一共32個關鍵字

    1. 聲明和定義

  • 在開始認識關鍵字前,必須要明白什麽是聲明,什麽事定義:
    • 定義:(編譯器)創建一個對象,為這個對象分配一段內存並給他取上一個名字。在一個作用域內,一個變量或者對象只能定義一次,並且定以後為它分配的內存不可變;
    • 聲明:1、告訴編譯器這個名字已經和一片內存匹配上了,並且這個內存是在其他地方定義的,聲明可以多次;2、告訴編譯器,這個名字已經占用,不能再用來定義其他變量或者對象了。
  • 備註:定義創建了對象,並為它分配了內存,聲明沒有分配內存

2. 關鍵字

2.1. auto(最寬宏大量的關鍵字)

  • 在默認情況下,編譯器默認所有變量都是auto的,所以我們可以當它不存在。

2.2. register(最快的關鍵字)

  • 請求編譯器,盡可能的把使用register定義的變量發到CPU的寄存器中,不保證一定放。(CPU的寄存器有限)
  • 註意:register變量長度應該小於等於int,並且不能使用&(register變量不存放在內存中)

2.3. static (最名不符實的關鍵字)

  • 修飾變量
    • 修飾全局變量,從定義開始到文件結束可用,其他文件即使使用extern 也不能使用
    • 修飾局部變量,只能在函數體裏面使用
    • 由於被static修飾的變量都是存放在內存的靜態區,所以即使函數運行結束,這個變量也不會被銷毀,函數下次使用時任然能用到這個值。
  • 修飾函數
    • 函數使用static修飾表示作用域僅限於本文件,又稱內部函數

2.4. short、int、long、char、float、double(基本數據類型)

技術分享圖片

  • 變量的命名規則
    • min length && max information
    • 望而知意,便於記憶
    • 由多個單詞組成是,要麽用下劃線隔開,要麽每個單詞首字母大寫
    • 盡量避免出現數字編號,除非邏輯上需要
    • 所有宏定義、枚舉常數、只讀變量全部用大寫字母命名,用下劃線分割單詞
    • 循環變量 n 、m、i、j、k;char c; int a[]; int *p 。

2.5. sizeof (最冤枉的關鍵字)

  • sizeof 是 32 個關鍵字中的一個,而不是一個函數。
  • sizeof(int) * p 表示什麽意思呢?

2.6. signed unsigned 關鍵字

  • 先來分析一段代碼
#include <stdio.h>
#include <string.h>

int main()
{
    signed char a[1000];
    int i = 0;

    for (i = 0; i < 1000; i++)
    {
        a[i] = -1 - i;
    }
    printf("%d\n", strlen(a));

    return 0;
}

當 i = 128 的時候,a[128] = -1 - 128 = -129 ,signed char 的取值範圍是 -128~127,所以超出了範圍,-129 源碼: 1 1000 0001 ,補碼:1 0111 1111 ,低 8 位 0111 1111 ,即 127。

當 i = 255 的時候,a[255] = -1 - 255 = -256 , -256 的原碼 11 0000 0000 補碼:11 0000 0000 ,低 8 位全為 0, strlen 遇到 ‘\0‘ 就結束,所以上述代碼輸出 255。

所以,char 類型如果直接使用就用來表示字符,如果加了 signed 和 unsigned 限定符的話就用來表示數字。

2.7. if else 組合

  • bool變量與“零值”比較
bool b = FALSE;

if(b)
    printf("TRUE");
if(!b)
    printf("FALSE");

上面這種寫法是值得推薦的,其他方式會有問題。

  • float 變量與“零值”比較
    if(test > -EPSINON) || (test < EPSINON); // EPSINON 為定義好的精度
  • 指針變量與“零值”比較
    if(NULL == p)
    if(NULL != p)
  • 先處理正常情況後處理異常情況或者說先處理概率大的,再處理概率小的
  • 賦值運算不能用在產生 bool 值得表達式上
    if((x = y) != 0 )
    printf("------");

    這樣寫是錯誤的,應該這樣寫

    x = y;
    if(x != 0)
    printf("-----");

2.8. switch case 組合

當嵌套比較少量的分支的時候可以使用 if else 組合,但是當嵌套的分支多的時候就要使用 switch case 組合了,但是也不要刻意去創造一個 switch 變量。

  • 每個case 的結尾不要忘了 break; 除非你有意讓多個分支重合。
  • 最後必須使用 default 分支,也需要 break; 。
  • 在 switch case 組合中禁止使用 return 語句。
  • switch 表達式不應該是有效的 bool 值,例如下面的表達式是不允許的。
    switch (x == 0)
    {
    ......
    }
  • case 後面的值只能是×××或者字符型的常量或者常量表達式。
  • case 語句的拍立順序
    • 按照字母順序排列
    • 把正常情況放在前面,把異常情況放在後面,並做好註釋,哪裏是開始,哪裏是結束。
    • 把常執行的情況放在前面,不常執行的情況放在後面。
  • 沒種情況的相關代碼要盡量簡潔,必要時可以使用函數。

2.9. do while for 關鍵字

  • break 和 continue 的區別
    • break 跳出循環
    • continue 終止本次循環,進入下一次循環
  • 多重循環的時候應該把最長的循環放在最內層,最短的循環放在最外層,這樣可以有效降低CPU切換循環層數的次數。
  • for 循環中,半開區間比閉區間直觀,能寫成 a < 10,絕不寫成 a <= 9。
  • 不要在 for 循環體內改變循環變量,否則會導致循環失控,像下面這種代碼是不好的。
    for(int i = 0; i < n; i++)
    {
    n = 10;
    }
  • 循環嵌套控制在3層以內。
  • for 循環的控制語句不能包含任何浮點類型的變量。

2.10. goto 關鍵字

goto 關鍵字可以在代碼中靈活的跳轉,存在很大的爭議,有的建議慎用 goto ,有的建議不要用。我覺得用得好還是可以用。

int *p = NULL;
...
goto error;
...
error:
    return -1;

2.11. void 關鍵字

void *p = NULL;
int *p_int = NULL;
p = p_int;    //不會報錯,是正確的
p_int = p;    //報錯,是錯誤的
  • void 修飾函數返回值和函數參數
    • 如果函數沒有返回值應該將其聲明為 void 類型,函數沒有參數也應該將其聲明為 void 類型
    • 如果函數的參數可以是任意類型的指針,則聲明為 void *
      void *memcpy(void *dest, const void *src, size_t len)
  • void 不能代表一個真實的變量,例如下面的代碼是錯的
    void a;
    fun(void a);

2.12. return 關鍵字

  • return 用來終止一個函數,並返回其後跟的值。
    return(value);    //括號可以省略,但一般不省略,尤其是返回一個表達式的值得時候
  • return 不能返回指向棧內存的指針,因為該內存在函數體結束的時候就被銷毀了。

2.13. const 關鍵字

  • const 修飾的只讀變量必須在定義的同事初始化(首先 const 修飾的變量,其次是只讀,兩層含義)
  • 想一想 switch case 中 case 語句後面就可以是 const 修飾的只讀變量嗎?

技術分享圖片

先忽略類型,看 const 離誰近就修飾誰

const int *p;           // const *p ,修飾 *p , p 可以改變, p 指向的內容不可變
int const *p;           // const *p ,同上
int * const p;          // * const p ,修飾 p,p 不可以改變,p 指向的內容可以改變
const int * const p;    // const * const p ,第一個 const 修飾 *p,第二個 const 修飾 p,所以 p 和 p 指向的內容都不能改變
  • 修飾函數的參數,當不希望函數的參數在函數體中被改變的時候可以使用 const 修飾函數的參數。
  • 修飾函數的返回值(const int fun(void))。
  • 在另一個文件引用 const 變量時使用 extern const int i; 聲明即可,而不是定義。

2.14. volatile 最易變的關鍵字

  • volatile 關鍵字和 const 一樣,是一種類型修飾符。它修飾的變量表示可以被某些編譯器未知的因素更改,比如操作系統,硬件或其他線程。遇到這個關鍵字修飾的變量,編譯器就不對它做優化,每次需要訪問的時候都重新去內存中讀取。

技術分享圖片

在這裏 volatile 只是告訴編譯器 a 的值可能會被改變,需要訪問時每次都要重新到內存中去取。

2.15. extern 最會戴帽子的關鍵字

  • 修飾函數和變量,表示函數和變量不是在本文件定義的,在本文件只是使用 extern 聲明。

2.16. struct 關鍵字

空結構體占多大內存?大多數編譯器是 1,gcc 是 0。所以不要太相信書本上的東西,一定要自己親自驗證。

  • 柔性數組
    • 不要驚訝,C語言中確實有柔性數組這個說法的。
    • C99 中,結構體最後一個成員允許是未知大小的數組,這就是柔性數組。但是柔性數組成員前必須至少有一個其他成員。sizeof 計算的大小不包含柔性數組所占的空間。為包含柔性數組的結構體分配內存要使用 malloc ,並且分配的內存應該大於 sizeof計算的值。
typedef struct st_type{
    int i;
    a[0];
}type_a;
//有些編譯器會報錯,可以改成
typedef struct st_type{
    int i;
    int a[];
}type_a;
//可以使用下面的代碼為柔性數組分配內存,但是分配好之後使用 sizeof 計算結構體的大小依然是不包含柔性數組所占的內存的。
type_a *p = (type_a *)malloc(sizeof(type_a) + 100 * sizeof(int));

//記得用完之後要 free
free(p);
  • 結構體裏面能不能放函數呢?

技術分享圖片

2.17. union 關鍵字

  • union 中所有數據成員共用一塊內存,同一時間只能存儲其中一個數據成員。union 的大小是最大成員所占內存的大小。

    2.17.1. 大小端對 union 類型數據的影響

union {
        int i;
        char ch;
}c;

c.i = 1;

//這時候 ch 只需要一個字節存儲,在低地址,如果 ch 的值等於 1, 說明 i 的值得低字節 1 存儲在低地址,是小端模式。Ubuntu、Windows 等 x86 架構都是小端模式。
int main()
{
    int a[5] = { 1, 2, 3, 4, 5 };
    int *p1 = (int *)(&a + 1);
    int *p2 = (int *)((int)a + 1);
    printf("%x %x\n", p1[-1], *p2);

    return 0;
}
// 5 2000000 小端模式 大端模式 5 2

2.18. enum 關鍵字

enum enum_type_name{
    ENUM_CONST_1,
    ENUM_CONST_2,
    ENUM_CONST_3,
    ...
    ENUM_CONST_n
} enum_variable_name;
  • enum_type_name 是自定義的枚舉類型,而 enum_variable_name 是 enum_type_name 類型的一個變量。
  • sizeof(enum_variable_name) 的值是多少呢? 想想枚舉成員的類型就會知道是 4 了, 空枚舉類型的大小也是 4。

2.19. typedef 關鍵字,最偉大的縫紉師

  • typedef 是給已經存在的數據類型取一個別名,而不是重新定義新的數據類型。
typedef struct student{
    int age;
    char name;
} stu_1, *stu_2;

const stu_2 student_1;    // 其實是 const (struct student *)student_1; 所以 修飾的是 student_1 本身,而不是指向的內容
stu_2 const student_2;    // 其實是 (struct student *)const student_2; 所以 修飾的也是 student_2 這個指針本身,而不是指向的內容
  • typedef 和 #define 之間的區別
#defien INT32 int
typedef int INT32_t

unsigned INT32 i = 10;    //沒問題,只是替換
unsigned INT32_t j = 10;  //錯誤,不支持

#defien PCHAR char*
typedef char* pchar

PCHAR ch1, ch2;    //ch1 是 char * 類型,ch2 卻是 char 類型
pchar ch2, ch4;    //ch3, ch4 都是 char * 類型

技術分享圖片

3. 總結

不復習不知道,一復習嚇一跳,原來 C語言 關鍵字還有這麽多知識點,以前也沒怎麽註意,當然不止這些,我只是記錄了我認為比較重要而且容易搞混淆的。

如果你覺得我的讀書筆記對你有用,可以關註微信公眾號 kalier 哦,最新的文章和讀書筆記都將在這裏首發。

讀書筆記-C語言關鍵字