位段和大小端


1、位段

面試中興的時候,被問到了位段的內容,當時對位段毫不了解,今天就來個總結吧。

首先,位段是結構體為了節省內存的一種定義方式,在計算機網絡中應用比較多,以下舉例說明。

比如,我們現在有三個整形變量,變量的范圍分別為0~15,0~10,0~254,我們知道 unssingned char表示的數字范圍為0~254,所以,我們可以用三個unsigned char類型的成員來保存這三個變量,定義如下:

struct S {
    unsigned char val1;
    unsigned char val2;
    unsigned char val3 ;
};

我們可以計算出上述結構體的內存占用大小為3字節,共24位,我們知道15只需要4位就可以表示,10也只需要4位就可以表示,所以,誕生了位段,上述結構體的代碼我們可以定義如下,並輸出其內存大小:

#include <stdio.h>

struct S {
    unsigned char val1 : 4;
    unsigned char val2 : 4;
    unsigned char val3 ;
};

int main()
{
    struct S s;
    s.val1 = 15;
    s.val2 = 10;
    s.val3 = 127;
    printf("sizeof(struct S) = %d \n", sizeof(struct S));
    printf("s.val1 = %d \n", s.val1);
    printf("s.val2 = %d \n", s.val2);
    printf("s.val3 = %d \n", s.val3);
    return 0;
}

上述代碼運行結果為:

通過位段的定義,結構體的大小由原先的3字節節省到2字節,通過賦值驗證,上述定義完全可以實現變量的需求。在計算機網絡中,TCP的報文封裝應用到了位段。

位段是將一變量類型,通常為char, int,  unsigned int , insigned char 類型分成多位來操作,如unsigned int 類型可以分成32位,但是,C語言沒有規定32位從左往右取,還是從右往左取,所以使用位段需要注意移植問題,位段使用盡量不要跨平台。

2、大小端問題

提到位,還有一個比較重要的考察知識點就是大小端問題,大端存儲:就是低字節內容存放在高地址處,高字節內容存放在低地址處。也就是低對高,高對低。小端模式則剛好相反。

如:int val = 0x11223344;

如果為小端存儲模式,內存中的字節存儲方式為 44 33 22 11(從左到右地址由低到高)如果為大端存儲模式,內存中的存儲方式為11 22 33 44。通常,X86架構為小端存儲模式,網絡中的通常使用大端模式,所以在Linux系統中,網絡編程處理數據需要大小端轉換,Linux內核也集成了大小端轉換函數ntohl()和htonl()。下面通過一個實例來說明大小端存儲模式:

#include <stdio.h>

int main()
{
    int val1 = 0x11223344;
    char val2 = (char)val1;
    printf("%x\n", val2);
    
    return 0;
}

代碼運行結果為:

上述代碼說明我的編譯器為小端存儲,對於整形數的4字節存儲方式為44 33 22 11 ,低地址存儲0x44,所以,將valu1賦值給val2的char類型只取了低字節,答案為0x44,通常也可以使用這種方式來判斷當前機器為大端存儲,或者小端存儲,以下給出完整驗證代碼:

#include <stdio.h>

int main()
{
    int val1 = 1;
    if (1 == *(char*)(&val1)) {
        printf("小端模式\n");
    } 
    if (0 == *(char*)(&val1)) {
        printf("大端模式\n");
    }
    
    return 0;
}

當然也可以通過公用體來進行驗證,下面代碼說明:

#include <stdio.h>

union UN{
    int val1;
    char ch;
};
int main()
{
    union UN un;
    un.val1 = 1;
    if (1 == un.ch) {
        printf("小端模式\n");
    } else if (0 == un.ch) {
        printf("大端模式\n");
    }
    return 0;
}

代碼運行結果:

 3、位運算優先級

因為平常寫代碼的時候,涉及到優先級問題的時候,通常都可以用括號解決,當時面試的時候忽然被問起,搞的很懵,所以特別強調以下,位運算的優先級:~  > & > ^ > | ,也就是非的運算級大於與大於異或大於或。邏輯運算符的優先級順序也是如此,即:!> && > ||

 我們通過位運算來實現大小端的轉換:

#include <stdio.h>

int main() {
    int val = 0x11223344;//小端存儲模式,從低到高地址,內存中單字節內存中存放的內容為44 33 22 11 
    int convertVal = 0;   
    convertVal = ((val & 0xff) << 24)  //取低8位,左移16位,按位&的優先級低於左移運算符的優先級,所以一定要加括號
                   | ((val & 0xff00) << 8) //取次低8位,左移8位
                   | ((val & 0xff0000) >> 8) //取次高8位,右移8位
                   | ((val & 0xff000000) >> 24);//取高8位,右移16位
    
    printf("convertVal = 0x%x",convertVal);
    return 0;
        
}

代碼運行結果為:

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM