數據在內存中存儲的方式:大端模式與小端模式


以下內容大多數來自百度百科,很容易理解的.

什么是大端模式,什么是小端模式?

所謂的大端模式(Big-endian),是指數據的高字節,保存在內存的低地址中,而數據的低字節,保存在內存的高地址中,這樣的存儲模式有點兒類似於把數據當作字符串順序處理:地址由小向大增加,而數據從高位往低位放;

所謂小端模式(Little-endian), 是指數據的高字節保存在內存的高地址中,而數據的低字節保存在內在的低地址中,這種存儲模式將地址的高低和數據位 權有效結合起來,高地址部分權值高,低地址部分權值低,和我們的邏輯方法一致;

為什么有大小端之分:

因為在計算機系統中,我們是以字節為單位的,每個地址單元都對應着一個字節,一個字節為 8bit。但是在C語言中除了8bit的char之外,還有16bit的short型,32bit的long型(要看具體的編譯器),另外,對於位數大於 8位的處理器,例如16位或者32位的處理器,由於寄存器寬度大於一個字節,那么必然存在着一個如何將多個字節安排的問題。因此就導致了大端存儲模式和小端存儲模式。我們常用的X86結構是小端模式,而KEIL C51則為大端模式。很多的ARM,DSP都為小端模式。有些ARM處理器還可以由硬件來選擇是大端模式還是小端模式。

用圖來形象地說明一下:

QQ截圖20160613120912QQ截圖20160613120931

如何檢測自己電腦是大端還是小端模式:

輸入以下程序,即可以檢測:

#include<stdio.h>
int main()
{
short int x;
char x0, x1;
x = 0x1122;
x0 = *((char *)&x);      //把x的低位地址的值賦給x0;
x1 = *((char *)&x + 1);      //把x的高位地址的值賦給x1;
if( x0 == 0x11 && x1 == 0x22)
printf(" This is big-endian \n");
else if( x0 == 0x22 && x1 == 0x11)
printf("This is little-endian \n");
else
printf("呵呵,你這個方法有誤啊\n");
return 0;
}

如何把數據轉換呢??

具體要看數據是如何存儲的啦,以我遇到的一個問題為例,在人工手寫體的數據庫中,60000張訓練圖片的文件為:train-labels-idex1-ubyte.首先說明的是它的存儲格式為大端模式,而我的計算機為小端模式,那我怎么辦??

我們首先要做的就是知道它內部是如何存放數據的,即多少個字字為一個數據單位.我現在有在matlab讀取文件的源代碼,如下:

function images = loadMNISTImages(filename)
%loadMNISTImages returns a 28x28x[number of MNIST images] matrix containing
%the raw MNIST images

fp = fopen(filename, 'rb');
assert(fp ~= -1, ['Could not open ', filename, ''])

magic = fread(fp, 1, 'int32', 0, 'ieee-be')
assert(magic == 2051, ['Bad magic number in ', filename, ''])

numImages = fread(fp, 1, 'int32', 0, 'ieee-be');
numRows = fread(fp, 1, 'int32', 0, 'ieee-be');
numCols = fread(fp, 1, 'int32', 0, 'ieee-be');

images = fread(fp, inf, 'unsigned char');
images = reshape(images, numCols, numRows, numImages);
images = permute(images,[2 1 3]);

fclose(fp);

% Reshape to #pixels x #examples
images = reshape(images, size(images, 1) * size(images, 2), size(images, 3));
% Convert to double and rescale to [0,1]
images = double(images) / 255;

end

從上面我們可以看出文件的開頭為4個 int32 類型的數,后面就是 unsigned char類型的數. 所以得出:int32 占4個字節即32個bit, 文件的前 4 * 4 個字節需要 由大端模式轉為小端模式,而后面的unsigned char 類型數據本身占8 個bit, 不需要轉換.以下是如何讀取文件的源代碼:

#include<stdio.h>
#include<stdlib.h>
int main()
{
    int temp, i, nClose;
    int num1[4];               //用於存放前四個int32的數;
    unsigned char num2[1000];    //用於存放讀出的1000個unsigned char類型的數;
    FILE *fp;
    fp = fopen("train-images-idx3-ubyte","rb");
    if ( NULL == fp )
    {
        printf("Open file error");
        exit(-1);
    }

    fread(num1, 4, 4, fp);        //讀取前4個int32類型的數據;
    for(i=0; i<4; i++)          
    {                             // 由大端模式轉換為小端模式,其實對於占4個字節的數據來說,由小端轉大端,也是一樣的代碼;
        temp = (num1[i]>>24 & 0x000000FF) | (num1[i] >> 8 & 0x0000FF00) | (num1[i] << 8 & 0x00FF0000 ) | (num1[i] << 24 & 0xFF000000); 
        num1[i] = temp;
    }

    fread(num2, 1, 1000, fp);          //讀取1000個char類型的數據;
    
    for(i=0; i<4; i++)              //輸出4個數;
        printf(" %d\n", num1[i]);
    for(i=0; i<1000; i++)           // 輸出1000個數;
        printf("%d    ", num2[i]);

    nClose = fclose(fp);         // 關閉文件;
    if(EOF == nClose)
    {
        printf("Close file Error\n");
        exit(-1);
    }
    return 0;
}

 

 

最再補充一個轉換32位的更精簡的方法,直接上代碼(2016年11.28補充),可以由大端轉為小端,也可以由小端轉為大端:

uint32_t swap_endian(uint32_t val) {
      val = ((val << 8) & 0xFF00FF00) | ((val >> 8) & 0xFF00FF);
      return (val << 16) | (val >> 16);
 }

  

更新一個檢測大端還是小端的代碼(來自linux內核設計與實現一書, 2019-06-19)

int x = 1;
if (*(char*) &x == 1)
    printf("小端");
else
    pirntf("大端");

 

 

 

 

 

Reference:

 

  

 


免責聲明!

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



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