C語言小知識(基於Linux)——個人筆記,不定時更新

為何愛學習 發表於 2021-04-22

一、switch case語法,在case中定義變數時,需要在case的有效範圍內使用花括號包起來,否則會編譯報錯;

switch (name){

  case "zhangSan":{

    int age = 13;

    break;

  }

  case "liSi":{

    int age = 14;

    break;

  }

  default:{

    break;  

  }

}

 

二、規定結構體以n位元組對齊

  在C語言結構體中,位元組對齊方式預設為最大型別位元組對齊;比如:

 

  struct Class{

    char age;

    int id;

  }class;

 

  這個結構體有兩個成員,分別為1位元組的age與4位元組的id(32位編譯時);實際佔用了5個位元組的空間,但是此時sizeof(class) == 8;因為最大的成員 id 佔了4個位元組,所以會以4位元組對齊;age就會補充3個位元組的保留位來位元組對齊。

  但是在資料流的處理中,為節約資源,一般每個位元組甚至每個bit都需物盡其用;所以上述結構只需要5位元組有效空間;

  此時就可以使用#pragma pack(n)來進行n位元組對齊;

 

#pragma pack(1)

  struct Class{

    char age;

    int id;

  }class;

#pragma pack()

 

  現在sizeof(class)就會等於5了;age只佔1位元組;在結構體之後加上#pragma pack()是為了限制1位元組對齊的範圍;#pragma pack()之後的資料結構又會以預設最大成員型別來進行位元組對齊;

 

三、結構中按bit定義變數

  可以在定義變數時,變數名之後,分號;之前 加上 : n 來規定此變數佔用的位數;

 

#pragma pack(1)

  struct ZhangSan{

    unsigned char gender_flag : 1;

    unsigned char age : 7;

    unsigned short  height : 6;

    unsigned short  weight : 6;

    unsigned short  id : 4;

  }zhangSan;

#pragma pack()

 

  此時gender_flag 成員與age 成員就共用一個位元組的空間,height 與 weight 與 id 共用兩個位元組的空間;sizeof(zhangSan) == 3;

 

四、資料大小端

  1.位序

    有如下結構:   

 

      struct ZhangSan{

        unsigned char gender_flag : 1;

        unsigned char age : 7;

      }zhangSan;

 

    其中 gender_flag 與 age 共用了一個位元組(8bit)的空間,一個佔1bit,一個佔7bit;但是bit佔的位置卻是與平臺相關的;就有可能出現如下兩種排列情況

                           C語言小知識(基於Linux)——個人筆記,不定時更新

    為了解決這種差異化,一般規定傳輸時的位序都為大端;即在基本型別裡宣告多個位域時,每個位域看做一個整體,先宣告的排在前面,後宣告的排在後面;在宣告結構體時就可以依據本機的位序來宣告位域,從而在接收資料流時能順利的取出每一個位域。

  Linux系統中一般可以通過判斷是否定義了巨集 __LITTLE_ENDIAN_BITFIELD__BIG_ENDIAN_BITFIELD 來區分當前的位序定義;

  若這兩個巨集同時都沒有定義,還可以新增 對位元組序的判斷來得出本機的位序定義,如下:

 

    #if  !defined(__LITTLE_ENDIAN_BITFIELD) && !defined(__BIG_ENDIAN_BITFIELD)

      #if (__BYTE_ORDER__==__ORDER_LITTLE_ENDIAN__)

        #define __LITTLE_ENDIAN_BITFIELD

      #elif (__BYTE_ORDER__==__ORDER_BIG_ENDIAN__)

        #define __BIG_ENDIAN_BITFIELD

      #endif

    #endif

 

    struct ZhangSan{

        #if defined(__LITTLE_ENDIAN_BITFIELD)

        unsigned char gender_flag : 1;

        unsigned char age : 7;

        #elif defined(__BIG_ENDIAN_BITFIELD)

        unsigned char age : 7;
        unsigned char gender_flag : 1;

        #endif

    }zhangSan;

  加了如上修飾之後,即可保證接收到的大端資料流中的位域都能成功的對映到本機所定義的位域;

 

  2.位元組序

  在資料流跨平臺的傳遞中,經常會遇到大小端不對齊的情況;比如一個int資料在大端機器中的儲存方式是高位在前,低位在後

    比如0x33224411在記憶體中的存放方式就是先存0x33,再存0x22,以此類推。如下:

 

      buf[0] = 0x33;

      buf[1] = 0x22;

      buf[2] = 0x44;

      buf[3] = 0x11;

 

  而小端平臺就剛好相反,是低位在前,高位在後;同樣的資料0x33224411在小端中的存放方式就是先存0x11,再存0x44,以此類推。如下:

      buf[0] = 0x11;

      buf[1] = 0x44;

      buf[2] = 0x22;

      buf[3] = 0x33;

  為了使資料統一,所以一般規定了在網路中傳輸的資料都以大端模式傳遞,收到資料之後再把資料從大端模式轉為本機的模式即可正常使用;

    在linux系統的#include <endian.h>中提供了一系類位元組序轉換的函式:

 

      #include <endian.h>

      // 將主機位元組序轉換為大端位元組序
      uint16_t   htobe16(uint16_t   host_16bits);
      uint32_t   htobe32(uint32_t   host_32bits);
      uint64_t   htobe64(uint64_t   host_64bits);

      // 將主機位元組序轉換為小端位元組序
      uint16_t   htole16(uint16_t   host_16bits);
      uint32_t   htole32(uint32_t   host_32bits);
      uint64_t   htole64(uint64_t   host_64bits);
 
      // 將大端位元組序轉換為主機位元組序
      uint16_t   be16toh(uint16_t   big_endian_16bits);
      uint32_t   be32toh(uint32_t   big_endian_32bits);
      uint64_t   be64toh(uint64_t   big_endian_64bits);

      // 將小端位元組序轉換為主機位元組序
      uint16_t   le16toh(uint16_t   little_endian_16bits);      
      uint32_t   le32toh(uint32_t   little_endian_32bits);      
      uint64_t   le64toh(uint64_t   little_endian_64bits);