C|按位、位元組級、字長級資料處理
構成電子計算機基本邏輯單元的電晶體可以表示兩種狀態,用二進位制描述就是0或1,稱為一個二進位制位(bit),多個電晶體的組合可以實現邏輯電路,資料和指令都可以以二進位制的序列來表示。
通常以8個二進位制位組成一個位元組(byte),以位元組為單位進行編址。
CPU在單位時間內能一次處理的二進位制數的位數叫字長(word size)。字長指明指標資料的標稱大小(nominal size)。對於一個字長為n位的機器而言,虛擬地址的範圍為0-2^n-1,程式最多可以訪問2^n個位元組。如32位機器的虛擬地址範圍為0x~0xFFFFFFFF。
32位CPU表示該CPU處理的字長為32位,即一次處理4個位元組。32位作業系統表示支援32位的CPU。
64位CPU —指的是該CPU處理的字長為64位,即一次處理8個位元組。64位作業系統表示支援64位的CPU,作業系統通常向後相容,所以也支援32位作業系統。
使用PAE36技術的32位CPU是36根地址線,使用PAE40技術的Intel x86-64 CPU是40根地址線,使用PAE52技術的AMD x86-64 CPU是52根地址線。
C語言支援位級(bitwise)操作,其資料型別型別有一個位元組長度的char型別,一個字長的int型別,指標本身的儲存也使用一個字長。
1 位級(bitwise)處理
C語言支援按位與、或、非操作,也支援移位操作。位操作的一個顯著用途就是節省儲存空間。
如在公曆轉農曆的程式中,可以使用一個unsigned int來儲存一個年份中的諸多資訊:
如2019年的資訊用0x0A9345表示,其二進位制位為0000 1010 1001 00110 10 00101。
(16進位制解析,每一個16進位制位(0-f)是4個二進位制位:0000-1111)
20-23位,其十進位制值表示閏月月份,值為0 表示無閏月(第23位代表最高位,最左邊)
7到19位,分別代表農曆每月(在閏年有13個月)的大小,每一位代表一個月份。
(1表示大月為30天,0表示小月29天)
5到6位,其十進位制值表示春節所在公曆月份(此處是2月)
0到4位,其十進位制值表示春節所在公曆日期(此處是5日)
解析出不同的位組便可以得到該年不同的資訊。
不同的年份可以儲存到一個數組中:
unsigned int LunarCalendarTable[199] ={0x069349,0x7729BD,0x06AA51,0x0AD546,0x54DABA,0x04B64E,0x0A5743,0x452738,0x0D264A,0x8E933E,/*2081-2090*/0x0D5252,0x0DAA47,0x66B53B,0x056D4F,0x04AE45,0x4A4EB9,0x0A4D4C,0x0D1541,0x2D92B5 /*2091-2099*/};
點陣數字和字元資訊也可以用字元陣列儲存和處理:
/*輸出點陣數字:8個char即可儲存64個位的資料,例如3:a[3]({0x00,0x1e,0x30,0x30,0x1c,0x30,0x30,0x1e}, //3包括有8個十六進位制的數,每行一個十六進位制數,並且換成二進位制的表示,會是什麼樣的呢?00000000 //0x0000011110 //0x1e00110000 //0x3000110000 //0x3000011100 //0x1c00110000 //0x3000110000 //0x3000011110 //0x1e請看1出現的地方,可以藉著滑鼠按1出現的軌跡跟著劃一劃,不就是 數字3字型的輪廓嗎?只不過,耳朵狀的3是反著的(這自有道理,看完程式1自會明白)。————————————————*/#include 浮點編碼可以透過位域來解析: #include 位域、共用體和可以解析漢字的GBK或GB2312編碼: void cngb(){ union{ struct { unsigned int i:4; unsigned int j:4; unsigned int k:4; unsigned int L:4; unsigned int m:4; unsigned int n:4; }; char hanzi[3]; }hz; fflush(stdin); puts(“查詢gb2312碼,請輸入一個漢字:”); gets(hz。hanzi); //strcpy(hz。hanzi,“中”); printf(“%X%X%X%X\n”,hz。j,hz。i,hz。L,hz。k);} 2 位元組級(byte)處理 典型型別:char,char的長度是一個位元組。 typedef unsigned char byte; 顯示double的位元組編碼: void showBytes(unsigned char* start, int len){ for(int i=0;i memmove()函式實現: memmove()由src所指定的記憶體區域賦值count個字元到dst所指定的記憶體區域。 src和dst所指記憶體區域可以重疊,但複製後src的內容會被更改。函式返回指向dst的指標。 void * my_memmove(void * dst,const void * src,int count){ void * ret = dst; if(dst <= src || (char *)dst >= ((char *)src + count)) { while(count——) { *(char *)dst = *(char *)src; dst = (char *)dst + 1; src = (char *)src + 1; } } else { dst = (char *)dst + count - 1; src = (char *)src + count - 1; while(count——) { *(char *)dst = *(char *)src; dst = (char *)dst - 1; src = (char *)src - 1; } } return(ret);}int main(){ char a[12]; puts((char *)my_memmove(a,“ammana_babi”,16)); system(“pause”); return 0;} 二進位制檔案瀏覽: #include 位級與位元組級結合處理的例項: 考慮用一個short型別儲存一個有效日期(年份取末兩位): /* 考慮用一個short型別儲存一個有效日期(年份取末兩位):Year(0-99) 7 bitsMonth(1-12)4 bitsDay(l-31) 5 bits如2021/11/22 1234567890123456 00000000000000000000000000010101 // year左移9位留下7位有效位 0000000000001011 // Month左移5位留給Day 0000000000010110*/// 向整數中壓縮資料 #include 按16進位制顯示資料: #include 3 字級(word)處理 典型型別:int,int的長度是一個字長,32位CPU或作業系統是4個位元組,64位是8個位元組。 typedef unsigned int word; 3.1 暫存器的長度是一個字長 當讀寫double資料型別時,需要兩條mov指令: 10: double dd = 15。751;00401044 mov dword ptr [ebp-18h],126E978Dh0040104B mov dword ptr [ebp-14h],402F8083h 當讀寫一個字長或以下的資料時,只需要一個暫存器,一條mov指令。 同樣的,當返回值是一個字長或以下的資料時,可用暫存器返回。如果是double,則用浮點棧返回,如果是複合型別,則需要壓入一個儲存返回值的起始地址,將返回值返回到這個起始地址標識的被調函式的棧幀空間。 3。2 指標的標度是一個字長 printf(“%d\n”,sizeof(void*)); // 4,32位系統 3.3 棧按一個字長對齊 其根源還是在於暫存器的長度是一個字長,一次訪問一個字長的記憶體空間,如果不對齊,有可能就需要更多次的訪問,適當的浪費一點記憶體空間來換取效率(以空間換時間)是可取的。 #include 看函式的棧幀: 棧幀圖示: 如果輸入超過15個字元(其中有‘\0’),則會破壞ebp,引發執行錯誤。 結構體也需要同樣的對齊(包括成員的對齊及整體的對齊): #include 函式記憶體映像: -End-