C語言 ---- 第二章 類型和指針

第一節 地址運算符、間接運算符、指針初識

  • & 地址運算符 由地址運算符所組成的表達式稱爲地址表達式
  • * 間接運算符 由間接運算符所組成的表達式稱爲間接表達式
  • 共同特點:
    • 一元運算符,只需要一個右操作數
    • 優先級相同,從右向左結合
int main(){
    int a = 100;
    &a;  //地址  --> 指針
    *&a;  // *地址  -->  指針所指向的值
    int b = 100, c = 100;
    b + c; //表達式的值爲200,是一個"int"類型的整數
    &b;    //表達式的值爲(int*)0x22fe44,是一個"指向int"類型的指針
    /*
    long int b = 100, c = 100;
    &b;  //一個指向“long int”類型的指針
    */
    return 0;
}
  • &a;: 地址表達式,變量a的內存地址爲地址表達式的值,地址又被稱作“指針”
  • *&a; :間接表達式,地址所指向的值(100)爲間接表達式的值,地址所指向的值又被稱爲“指針所指向的值”
    • 間接表達式的運算順序爲從右向左,在此表達式裏,地址運算符的優先級高於間接運算符

第二節 聲明存儲指針的變量–整數

  • 如何聲明一個指針類型的變量?

    • 在聲明的變量前加一個*
  • int main(){
        int a;  //變量a,用來存儲"int類型"的整數
        int * b;  //變量b,聲明用來存儲"指向int類型"的指針
        b = &a;   //將指向"int類型"的指針賦值給b
        *b = 100;
        /*
        *b爲間接運算符,優先級高於賦值運算符
        先將變量b進行左值轉換,*b的值則爲變量a的值
        */
        return 0;
    }
  • int * b; //變量b,用來存儲"指向int類型"的指針
    b = &a; //將指向"int類型"的指針賦值給b,由於上面一行代碼聲明瞭變量b爲指針類型,故此行代碼才能正確運行

  • * : 根據地址對變量進行還原,*b = = a *b的值等於變量a的值

    • *b = 100;< == > a = 100;
    • 通過*運算符和變量b,間接影響變量a的值,所以將*運算符稱爲間接運算符
    • *&a : 先執行&a取出地址(指針),再執行*&a對取出的指針進行還原*&a < == > a

第三節 函數名-指針轉換、聲明存儲指針的變量–函數、使用變量調用函數

void swap(int * a, int * b){
    int temp = * b;
    * b = * a;
    * a = temp;
}
int main(void){
    int m = 10086, n = 10010;
    swap(& m, & n);
    
    void (* pf)(int *, int *) = swap;
    pf(& m, & n);  //處理器在進行處理的時候,會先將函數調用表達式`pf()中存儲的是一個指向swap的指針(地址)`進行一個左值轉換,通過變量pf調用swap函數
    //(*pf)(& m, & n); (&*pf)(& m, & n); (*&*pf)(& m, & n); (&*&*pf)(& m, & n);
    return 0;
}
  • 在main函數中,函數名(swap)會被隱式的轉換爲函數的地址(“指向函數的指針”),處理器會根據地址定位到swap函數所在的位置並執行,swap (& m, & n) < == > (&swap)(& m, & n);
  • 聲明pf變量,用於存儲“指向函數swap”的指針,只要符合聲明格式的函數,pf都可以進行存儲

第四節 類型匹配、數值的類型、整型常量的:前綴、後綴、類型判定方法

int main()
{
    //“變量”在聲明的時候,我們規定了它的類型
    int a;  //變量a爲int類型
    //在給變量進行賦值的時候,必須賦值給它相對應類型的數值
    a = 1.5;  //類型不匹配,存儲就會出現問題
    
    //“常量”也是有類型的
    //整數類型的常量  -->  簡稱整型常量
    
    //1.整型常量3種進制的寫法(前綴)
    a = 125;     //十進制寫法:以非0的數字開頭
    a = 0125;    //八進制寫法:以0開頭
    a = 0x125;   //十六進制寫法:以0x/0X開頭
    
     
    a = 0b00001; //二進制寫法:以0b/0B開頭
    //注意”C語言並沒有規定二進制的寫法,這是編譯器自行添加的
    //所以,更換編譯器的時候,程序有可能會報錯
    
    //2.整型常量的3個後綴:u(unsigned)、l(long )、ll(long long)(不分大小寫)
    a = 125u;
    a = 125L;
    a = 125LL;
    a = 125ul;  //u和l:誰前睡後都可以
    a = 125ull; //u和ll:誰前誰後都可以
    
    //怎樣判定常量是什麼類型?
    //前綴 + 後綴 + 具體的C實現 這三個因素決定了一個常量的類型
    //具體判定方法:C語言提供了整型常量類型對照表,詳見下表
    return 0;
}
  • 整型常量對照表
  • [外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-J0sqbcTI-1578483847139)(E:\workspace\TyporaProjects\C筆記\C語言\images\第二章 類型和指針\4.png)]

第五節 整數類型轉換爲_Bool類型、隱式類型轉換

int main(void){
    _Bool a, b, c, d;
    a = 0;             //int類型的0  ---->  _Bool類型的0
    b = 1;			   //int類型的1  ---->  _Bool類型的1
    c = 100;  		   //int類型的100 ----> _Bool類型的1
    d = 100000000000;  //long long int類型的100000000000 --> _Bool類型的1
    
    return 0;
}
  • 由於a,b,c,d都是int或long long int型的數據,與_Bool類型不一致,故需要進行類型轉換。
    • 類型轉換不需要人爲進行參與,自動進行,所以也稱爲隱式類型轉換,轉換的結果爲上述代碼段中的註釋
  • 將“整數類型”轉換爲“_Bool類型”時:
    • 如果數值在_Bool類型存儲範圍以內,只有類型會發生改變,數值將不會發生改變
    • 如果數值不在_Bool類型存儲範圍以內,類型和數值將全部發生改變,數值將被轉換爲1

第六節 整數類型轉換爲非_Bool整數類型、隱式類型轉換

int main(void){
    short int a = 100;  //將int類型的100  -->  隱式轉換爲short int類型的100
    int b = 3700u;      //將unsigned int類型的3700  ---->  隱式轉換爲int類型的3700
    //由於a和b的類型不一致,需要進行隱式類型轉換
    
    //新類型爲無符號整數
    unsigned char c = -1;  //int類型的-1隱式轉換爲unsigned char類型的255
    //由於-1太小,不在新類型的存儲範圍之內,所以需要加上一個數(256)得到一個新值
    //256由來:新類型(char)的最大存儲值爲1個字節(255),進行加1 ----> -1 + 256 = 255
    
    
    unsigned char d = 257; //將int類型的257隱式轉換爲unsigned char類型的1
    //由於257太大,不在新類型的存儲範圍之內,所以需要減去一個數(256)得到一個新值
   //256由來:新類型(char)的最大存儲值爲1個字節(255),進行加1後得到一個新值,做減法運算  ---->  257 - 256 = 1
    
    //有符號整數  -->  不同的C實現會有不同的處理結果
    
    return 0;
}
  • 由於類型的不一致,需要進行隱式類型轉換
  • 將“整數類型”轉換爲“非_Bool整數類型”時:
    • 如果數值在新類型的存儲範圍以內,只有類型會發生改變,數值將不會發生改變
    • 如果數值不在新類型的存儲範圍之內,類型和數值都將全部發生改變
    • 數值的改變分爲兩種情況:
      • 新類型是無符號整數、新類型是有符號整數

第七節 表達式值的類型

int max(int a, int b)
{
    if(a >= b)
        //關係表達式的類型  -->  int(固定)
        return a;
    	return b;
}
int main(void)
{
    //每個表達式都有一個值,這個值也是有類型的
    //"表達式值的類型",簡稱"表達式的類型"
    int x = 1, y = 1;
    unsigned char z;
    x += z = 56;
    //整型常量表達式:56  -->  int類型(判斷方法見本章第三節)
    //賦值表達式:z = 56: 值 -> 56、類型 -> unsigned char
    //複合表達式的值: x += z = 56:值 -> 57、類型 -> int
    //賦值表達式  --> 值 -> 左操作數的值、類型 -> 左操作數的類型
    
    z += max(x, y);
    //函數調用表達式:max(x, y):值 -> 函數的返回值、類型 -> 函數的返回類型
    //複合賦值表達式: z += max(x, y):類型 -> unsigned char 
    
    y = ++ x;
    //前綴遞增表達式: ++ x:類型 -> int -> 操作數的原型
    //賦值表達式: y = ++ x: 類型 -> int
    
    return 0;
}

第八節 隱式類型轉換和運算符的關係、整型轉換階、整型提升

int main(void)
{
    //類型的轉換是由雲端夫來發起的
    //賦值運算符:將右操作數轉換爲和左操作數一樣的類型
    singned char a = 1, b = 2;
    //遞增運算符:不改變操作數的類型
    ++ a;
    
    /*
    其他的大多數運算符都需要將操作數轉換爲相同的類型,然後再進行運算
    具體的轉換規則:以加性運算符爲例
    */
    
    a + 380L;
    //第一步:整型提升 --> 將轉換階低於int或者unsigned int的類型轉換爲int或者unsigned int類型
    //第二步:整型提升後,如果操作數類型不一致  --> 將轉換階低的操作數轉換爲和轉換階高的操作數一樣的類型
    //詳細轉換見整型的轉換階
    /*
    注意:
    	1.整型的提升是對第一步的描述,和第二步無關
    	2.整型提升以int類型爲首選
    	3.當進行整型提升時,如果數值不在int類型的存儲範圍之內,那麼就將它轉換爲unsigned int --> 當short int 和 int的存儲範圍相同時
    */
    
    a + b;
    //整型提升,只要在int或unsigned int類型以下的,都需要進行整型提升
    
    int c = -1;
    unsigned int d = 100;
    c + d;
    //整型提升後,兩個類型在同一階
    //將“有符號類型”轉換爲“無符號類型”
    
    return 0;
}
  • 整型的轉換階
  • [外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-yRxqZk7L-1578483847141)(E:\workspace\TyporaProjects\C筆記\C語言\images\第二章 類型和指針\8.jpg)]

第九節 負號運算符、負號表達式

int main(void)
{
    unsigned char a;  
    a = -1;  //1爲int型,4個字節"0000 0000 0000 0000 0000 0000 0000 0001",在此處,a的值爲255    
    // - : 負號運算符  -->  負號表達式  -->  在C語言中,負數是通過運算所得到的
    // 一元運算符,只需要一個右操作數
    
    //運算規則:
    // 1.如果操作數是一個整數,負號運算符會發起類型轉換  --> 對操作數進行整型提升
    // 2.對操作數進行“取補碼”操作,得到 "1111 1111 1111 1111 1111 1111 1111 1111"
    
    signed char b = - a ++;   //b的值爲1
    
    return 0;
}

第十節 顯式類型轉換、轉型運算符、轉型表達式、()的多種用法

int main(void)
{
    //顯式/強制類型轉換
    //語法規則:(類型名)表達式
    //() --> 轉型運算符 --> 將表達式的類型轉換爲括號中所指定的類型
    
    long long a;
    a = 33;  //處理器在執行運算時會先進行隱式的運算,將int型的33轉換成long long int型的33並賦值給a
    a = (long long int)33;  //int --> long long int
    
    a = (long long)33 + 100;
    a = (long long)(33 + 100);  //(33 + 100)  --> 基本表達式
    //()運算符的三種用法
    //1.函數調用運算符
    //2.轉型運算符
    //3.構成基本表達式
    
    return 0;
}

第十一節 整數-指針的轉換、間接運算符的性質、再識指針

int main(void)
{
    //間接運算符  -->  根據指針(地址)對變量進行還原  -->  它的操作數必須是指針,並且它不會發起類型轉換
    
    int a = 0;
    int b = (int) &a;//&a的值爲變量a的地址,類型爲指向int類型的指針;此行代碼的作用是將變量a的地址存儲到變量b中
    /*
    錯誤:間接運算符的操作數類型錯誤
        * b = 100;
    */
    *(int *)b = 100; //(int *)b先將b進行左值轉換,此時b中存儲的是a的地址,類型爲指向int類型的指針
    //此行代碼執行完以後,a的值變爲100
    //*(int *)b < == > a
    
    return 0;
}

第十二節 研究指針三劍客、類型的本質

int main(void)
{
    int a = 0;  //假設:變量a的內存地址爲123456
    
    &a;
    int b = (int)&a;
    
    return 0;
}
  • &a : 值爲123456,類型爲指向int類型的指針
  • b : 值爲123456,類型爲int整型
  • &a和變量b : 數值相同,類型不同
  • 分爲不同類型的原因:
    • 不同類型,對應着不同的使用規則
  • * b ----> 編譯器會報錯
  • * &a ----> 將&a的值解析爲地址,然後訪問這個地址所對應的內存單元
  • C語言爲什麼要引入地址運算符&間接運算符*指針這幾種類型?
    • 讓它們互相配合,使操作者可以通過地址來訪問內存
    • & : 獲取一個數值,並將這個數值的類型規定爲指針
    • * : 通過指針類型的數值,來訪問內存單元
  • 彙編語言可以通過地址來任意訪問內存 ----> 強大
  • 高級語言把內存訪問的細節屏蔽掉 ----> 簡單
  • C語言 ----> 既強大又簡單

第十三節 指針-指針轉換、複雜表達式分析

int abc(int a, int b)
{
    return 0;
}
int main(void)
{
    int a, (* b)(int, int) = abc;
    // 變量  ---->  存儲數據
    //變量a  ---->  int整型
    //變量b  ---->  指針  ---->  指向函數  ---->  兩個int類型的參數,一個int類型的返回值
    /*
    	abc  ---->  函數名  ---->  函數名-指針轉換  ---->  值:函數abc的地址  ---->  類型  ---->  指針  ---->  指向函數  ---->  兩個int類型的參數,一個int類型的返回值
    */
    //變量b中的值爲多少?  ---->  函數abc在內存中的地址
    //變量b中的值是什麼類型?  ---->  int (*) (int , int)
    
    void (* c)(void) = (void (*) (void)) b;
    //變量  ---->  存儲數據
    //變量c  ---->  指針  ---->  指向函數  ---->  沒有返回值,沒有參數
    //b  ---->  變量  ---->  左值轉換  ---->  值:函數abc的地址、類型:int (*) (int, int)
    //變量c中的值是多少?  ---->  函數abc在內存中的地址
    //變量c中的值是什麼類型?  ---->  void (*) (void)
    a = ((int (*)(int , int )) c)(1,2);//函數調用表達式,調用函數abc,並傳入值1和2
    //((int (*)(int , int )) c)最外層()是爲了讓基本表達式作爲一個整體(轉型運算符)參與運算
    //基本表達式:(int (*)(int , int )) c
    //處理器會將c的值進行左值轉換,將c的原類型void (*) (void)強制轉換爲int (*)(int , int )
    return 0;
}

第十四節 從內存的角度搞定 – 多級指針

int main(void)
{
    int i, * pi = &i, * * ppi = & pi;
    * * ppi = 1;
    * ppi = 1;
    //--------------------------------------------
    int j;
    pi = & j;
     ** ppi = 3;
    
    return 0;
}

在這裏插入圖片描述
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章