[讀] C和指針 (Ch8 ~ Ch10)

Chapter 8

  • ⚠️數組名和指針常量類似,但並非指針!(也由於是指針常量,因此數組名也是un-assignable的)程序員

    • 編譯器用數組名來記住數組的屬性,只有當數組名在表達式中被使用時,編譯器才爲它產生一個指針常量
    • 只有兩種狀況下數組名不用指針常量來表示:數組

      • 用於sizeof:對數組名使用sizeof將返回整個數組的長度(以字節爲單位)
      • 用於&取址:獲得指向數組的指針(而不是指向第一個元素的指針的指針)
    • 下標引用能夠做用於任何指針,而不只僅是數組名,所以C編譯器不檢查下標的範圍,下標也能夠爲負
    • 經過指針來訪問數組中的元素可能比用下標的效率更高,由於經過下標訪問數組老是要作如下運算:安全

      arr + (index * sizeof(elementOfArr)) // 此處的乘法沒法避免

      而某些狀況(主要是循環體)下,對該偏移值的計算將被編譯器優化爲純加法(一個在現代基本沒什麼用的知識。。):函數

      for (int* arrPtr = arr; arrPtr < arr + 10; arrPtr ++) { ... } // arrPtr++ 所加上的值只須要計算第一次
    • 一個注意:

      指針不只在作加法時須要考慮元素大小,兩指針相減的結果也會被經過除法轉換爲元素個數(單純比較是否相等則不須要)優化

  • ⚠️函數接收多維數組作參數時,必須指定除第一維外其餘全部維度的大小,不然尋址時函數沒法計算偏移量指針

    void func(int x, int y, int arr[][]) { // 未指定第二維大小
        printf("%d", arr[x][y]); // 訪問arr的第(x * sizeOfRow + y)個元素,但函數不知道sizeOfRow
    }
  • 字符數組的初始化與字符串常量code

    char msg[] = "Hello";
    char *msg = "Hello"; // 字符串常量是read only的
  • ⚠️聲明一個指向數組的指針:內存

    int arr[3][10];
    int (*arrPtr)[10] = arr; // arrPtr是一個指向擁有10個int元素的數組的指針,arrPtr + 1將指向下一行的10個int的開始

    多維數組能夠經過如下形式傳遞給函數:element

    void func(int (*mat)[10]);
    void func(int mat[][10]);

    當多維數組各維度的大小並不總固定時(個人最愛~),能夠經過如下方式將它以一維數組的形式傳遞給函數:字符串

    int *arrPtr = &arr[0][0];
    int *arrPtr = arr[0];

    可是如下這種聲明可能會致使嚴重的問題:

    int arr[3][10];
    int (*arrPtr)[] = arr; // 此時指針arrPtr不知道數組arr的size信息,它將會把0看成arr的二維寬度(而不是3)
  • 下標引用[]的優先級高於間接訪問*

    int *p[10]; // p被聲明爲擁有10個int*元素的數組

Chapter 9

  • strlen的返回值爲size_t,是一個unsigned值(永遠大於等於0)
  • 標準庫中的字符串函數不負責安全性檢查,程序員必須保證字符串以NULL結尾(在必要的狀況下)且大小合適
  • strcpystrcat等函數會將目標字符串的地址做爲返回值,這個特性令它們能夠被嵌套使用
  • 即便是對轉換大小寫這種簡單的操做,使用庫函數也有利於提升程序在使用不一樣字符集的平臺間的移植性
  • strerror函數接收一個錯誤代碼,並返回一個指向描述該錯誤的字符串的指針

Chapter 10

  • ⚠️struct(忽然發現我根本不知道struct的標準語法。。)

    struct Tag { // struct關鍵字和花括號之間的部分爲該struct的標籤,爲可選部分,這樣聲明的結構體每次使用時都要加上struct關鍵字
        int val;
        char name[10];
    } tag1, *tag2; // 此處能夠順便聲明該類型的變量,但此後聲明新變量必須使用struct Tag tag;,由於Tag實際上不是一種類型
    
    typedef struct { // 將這種struct定義爲一種類型,所以之後使用時不用加struct關鍵字
        int val;
        char name[10];
    } Tag; // 之後能夠直接使用Tag tag;來聲明新變量

    因此struct {};纔是一個總體,它能夠被加上標籤做爲記號,也能夠被typedef爲一種新的結構體

    (我居然一直覺得是在主函數外聲明新變量就要在類型前加struct關鍵字。。因此我這麼久都寫的啥==)

  • 點操做符.的優先級高於間接訪問*,因此C提供了->來更爲方便地經過結構體指針訪問它的成員(原來如此!!)

    (*ptr).val = 0;
    ptr->val = 0; // 顯然下面這種寫法更爲直觀

    以及優先級上->高於.高於*

    *p->a.b; // 將會從p指向的結構體中取出a結構體的b成員並對它進行間接訪問,即*((p->a).b)
  • 不完整聲明:使兩個(或更多)結構體能夠互相包含對方的指針

    struct B; // 提早聲明B的存在
    
    struct A {
        struct B *bPtr;
        ...
    };
    struct B {
        struct A *aPtr;
        ...
    };
  • 從C2011開始typedef struct foo foo;是合法的了,可是這種寫法在以前的編譯器上(可能!)會出現兼容性問題
  • ⚠️C中的空結構體爲undefined行爲(但C++容許結構體爲空),以及結構體實際上容許嵌套聲明,所以可能無心間聲明空結構體:

    struct A {
        struct B { // 此處實際只是struct B的定義,而非變量聲明。所以沒有其餘成員的struct A實際爲空結構體
            int id;
            ...
        };
    };
  • ⚠️對結構體使用sizeof將返回它實際佔用的字節數,包括那些爲了邊界對其而跳過的字節。

    所以要肯定一個成員對於結構體起始位置的實際偏移量,應使用offset(type, member)宏(位於stddef.h),type爲結構類型,member爲該成員的名字。這個宏將返回一個size_t

  • 把整個結構體做爲函數的參數or返回值都會致使整個結構體被複制,使用指針效率更高
  • 位段(bit filed):(本質上是不可移植的。。hmmm)

    • 位段成員必須爲intunsigned int類型
    • 成員後跟一個冒號和一個用於指定該位段所佔用的bit數的整數
    struct CHAR {
        unsigned ch :   7;
        unsigned font : 16;
        unsigned size : 19;
    };
    struct CHAR c1;
  • ⚠️聯合(union):(經過訪問不一樣的聯合成員,內存中的同一塊內容能夠被解釋爲不一樣的東西)

    • struct類似,union也容許嵌套聲明

      struct POINTER { // 實際包含兩個變量,type和ptr
          enum {INT, FLOAT, STRING} type;
          union {
              int        *i;
              float    *f;
              char    *s;
          } ptr; // 匿名union
      };
    • 聯合中全部成員都擁有相同的起始地址,整個聯合的大小取決於最大的成員
    • 爲了不個別超長成員形成空間浪費,一般只儲存指向不一樣成員的指針,使用中再爲該成員動態分配內存
    • 初始化:初始值必須爲第一個成員的類型,且必須用花括號包圍

      union {
          int a;
          float b;
      } x = { 5 }; // 其餘類型的初始值(若是可能的話)將會被轉換爲一個int賦值給x.a
相關文章
相關標籤/搜索