以下內容大多數來自百度百科,很容易理解的.
什么是大端模式,什么是小端模式?
所謂的大端模式(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); }
更新一個檢測大端還是小端的代碼(來自linux內核設計與實現一書, 2019-06-19)
int x = 1; if (*(char*) &x == 1) printf("小端"); else pirntf("大端");
Reference:
- 部分來自百度百科,http://baike.baidu.com/link?url=yuwE0tRgKztqzgfgCsX7biil0BtXIXaTV3170MTlRa4QpWoyZD0WFEBiOQX9cdVdN9zWrEp7cvDRY0f-9jFE5q
- 兩張截圖來自 http://blog.csdn.net/zhaoshuzhaoshu/article/details/37600857/