以下內容大多數來自百度百科,很容易理解的.
什麼是大端模式,什麼是小端模式?
所謂的大端模式(Big-endian),是指資料的高位元組,儲存在記憶體的低地址中,而資料的低位元組,儲存在記憶體的高地址中,這樣的儲存模式有點兒類似於把資料當作字串順序處理:地址由小向大增加,而資料從高位往低位放;
所謂小端模式(Little-endian), 是指資料的高位元組儲存在記憶體的高地址中,而資料的低位元組儲存在內在的低地址中,這種儲存模式將地址的高低和資料位 權有效結合起來,高地址部分權值高,低地址部分權值低,和我們的邏輯方法一致;
為什麼有大小端之分:
因為在計算機系統中,我們是以位元組為單位的,每個地址單元都對應著一個位元組,一個位元組為 8bit。但是在C語言中除了8bit的char之外,還有16bit的short型,32bit的long型(要看具體的編譯器),另外,對於位數大於 8位的處理器,例如16位或者32位的處理器,由於暫存器寬度大於一個位元組,那麼必然存在著一個如何將多個位元組安排的問題。因此就導致了大端儲存模式和小端儲存模式。我們常用的X86結構是小端模式,而KEIL C51則為大端模式。很多的ARM,DSP都為小端模式。有些ARM處理器還可以由硬體來選擇是大端模式還是小端模式。
用圖來形象地說明一下:
如何檢測自己電腦是大端還是小端模式:
輸入以下程式,即可以檢測:
#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); }
Reference:
- 部分來自百度百科,http://baike.baidu.com/link?url=yuwE0tRgKztqzgfgCsX7biil0BtXIXaTV3170MTlRa4QpWoyZD0WFEBiOQX9cdVdN9zWrEp7cvDRY0f-9jFE5q
- 兩張截圖來自 http://blog.csdn.net/zhaoshuzhaoshu/article/details/37600857/