C語言位域常見問題分析

2021-04-20 06:54:12 字數 3247 閱讀 5139

原文:http://blog.csdn.net/idoiwill/archive/2008/09/21/2956890.aspx

c語言裡的位域是乙個比較複雜的問題,涉及的方面也比較多,關於位域的基礎內容可以參考以下文章:理解c語言位域

分析**如下:

#include "stdio.h"

#include "memory.h"

struct bitseg1;

struct bitseg2;

int main()

輸出結果為:

第一次賦值後: a的值為:1     b的值為:2

第二次賦值後: a的值為:4     b的值為:-2

第二次賦值後: a的值為:0     b的值為:3

bitseg1的位元組數為: 4

bitseg2的位元組數為: 1

**中的bigseg1定義了兩個int型別的字段,而且它們分別只占用4位和3位的空間。當bitseg1中的a,b分別賦值為1和2時,輸出的結果也如我們所料。當第二次賦值為100和30時,輸出的結果卻是4和-2,為什麼呢?

1.賦值問題

出現上述問題,是由於賦值與位域效果共同形成的,a和b雖然都是int型別,但是在bigseg1結構裡,它們只有4位和3位為實際有效位。也就是bigseg1中的前4位是a的,接著的3位是b的(這裡沒有位元組的跨越問題)。執行ba1.a=100語句,其中100的二進位制**是:01100100,程式只把這100的二進位制數的前面4位(已用紅色字型表示)賦值給a,那麼ba1中的a只是0100(b),結果當然是4咯。然後是執行b1.b=30語句,其中30的二進位制**為:00011

110, 同樣的程式只把前3位(注意b定義有效位數是3位)賦值給b,那麼ba1中的b就是110(b),結果是-2,為什麼?是這樣的,我們定義b為int類 型,也就是有符號的整型,如果想定義為無符號整型我們必須這樣寫unsigned int,而有符號整型的第一位是符號位,用於表示正負的(1表示負數,0表示正數),那麼對於b,程式就會把b的第一位(即1)做為符號位,即b應該是負 數,而後面的是它的數值(即10(b)),注意計算機裡負數是按補碼的形式表示的,這種賦值下b的確是110(它是補碼,按「即反加一」的法則,即十進位制 的-2),結果就是-2了。而剛才的a給賦值為0100(b)時,第一位是0,解釋為正數。再舉一例,若使ba1.b=7,那麼ba1.b的值是多少 呢?7的二進位制是0111,前面3位直接給到b,因為是負數,讀出來時按補碼形式讀,那麼就是-1了。

總之一句話:用位為理解位域。

接下來是用memcpy對ba1進行記憶體copy,就更應該用位來考慮位域了。下面我們分析一下:

首先,sizeof(bitseg1)的值是4個位元組,先記住,後面會對此問題進行詳細解釋。

執行memcpy(&ba1,str,sizeof(bitseg1)),把str的內容中的前面4個位元組的記憶體裡的內容複製到ba1中,我們先來看一下str的記憶體位資訊(用16進製表示):

0x0012ff74:30  31  32 33

其中0x0012ff74時str陣列的位址起始位置,30,31,32,33等16進製制值分別表示字元'0','1','2','3',它們當然是acii值啦。

copy之後的ba1的記憶體位資訊如下:

0x0012ff7c:30     31     32    33

因為ba1也是佔4個位元組的空間的,所以不會出現記憶體溢位。memcpy只是把相應記憶體複製到了ba1上,位資訊與str上的資訊一樣的。

現在,我們把30(h)的二進位制寫出來,是:00110000,ba1的a佔前面4 位,b佔接下來的3位,直觀地看,a應該是0011(b)即十進位制的3,b是000(b)即十進位制的0,但看輸出的結果卻是a=0,b=3,這又是為什麼 呢?其實很簡單,處理器定義位元組的前面4位是指該位元組從右往左4位,而不是從左往右的4位,所以a應該是0000(b),b應該是011(b)。

2.位元組對齊

回到上面留下的位元組數的問題,即sizeof(bitseg1)的結果為4個位元組。按理來說,bitseg1的有效位數是7位,但為了程式的快速運 行,乙個重要的手段是減少記憶體的讀寫次數,所以一樣的處理器都是以位元組的倍數將記憶體中的資料讀到暫存器中,所以程式把資料以位元組的形式對齊了就可以有效的 減少記憶體的讀寫時間,你可想想要處理器唯讀記憶體中的7位是如何做的,一次乙個位?那倒不如一次讀8位。

在做位元組對齊的時候也是有規則的,在32位的系統裡,編譯器會按型別進行位元組的對齊,以它們的位寬為基準,在vc下:

char

偏移量必須為sizeof(char)即1的倍數

int

偏移量必須為sizeof(int)即4的倍數

float

偏移量必須為sizeof(float)即4的倍數

long

偏移量必須為sizeof(long)即4的倍數

double

偏移量必須為sizeof(double)即8的倍數

short

偏移量必須為sizeof(short)即2的倍數

bitseg1裡的兩個變數都是int型別,所以是4個位元組對齊的。

而使用位域的主要目的是壓縮儲存,減少記憶體的占有量,其大致規則為:  

1)  如果相鄰位域字段的型別相同,且其位寬之和小於型別的sizeof大小,則後面的字段將緊鄰前乙個字段儲存,直到不能容納為止; 

2)  如果相鄰位域字段的型別相同,但其位寬之和大於型別的sizeof大小,則後面的字段將從新的儲存單元開始,其偏移量為其型別大小的整數倍; 

3)  如果相鄰的位域字段的型別不同,則各編譯器的具體實現有差異,vc6採取不壓縮方式,dev-c++採取壓縮方式; 

4)  如果位域字段之間穿插著非位域字段,則不進行壓縮; 

5)  整個結構體的總大小為最寬基本型別成員大小的整數倍。

再看乙個例子:

struct test1

;int len = sizeof(test1);

對於上述例子,len的值應該是12。解釋如下:

首先以最長的型別位寬做為偏移量,最長的是long型,佔4位,所以不同型別之間應該是4個位元組的偏移,即test1應該是4位元組的整數倍。

char a:1; //用乙個位元組去儲存

char :2;  //空域。因為與前面的a的型別相同,而兩個位域的位寬相加仍然少於8位,所以依然用1個位元組表示

long b:3; //long型別的位寬是4個位元組,與前面的char型別不同,所以b與a之間偏移4個位元組,它們之間自動補充3個位元組

char c:2; //因為c與b又不同型,以test1中的最長的long型別的位寬進行偏移,所以雖然char只用1個位元組就夠了

//但依然要佔4個位元組。

總共是12位元組。

C語言常見問題

在c語言中,有乙個 流 的概念 流可以分為兩種型別 文字流 檔案 和二進位製流 檔案 文字流是解釋性的,最長可達255個字元 二進位製流是非解釋性的,一次處理乙個字元。在用c語言程式設計的時候,我們都是用 include指令包含型別為 h 的檔案,那麼可以用該指令包含型別不為 h 的檔案嗎?答案是 ...

C語言常見問題

1 嵌入式與微控制器的區別 從軟體上,行業裡經常把晶元中不帶mmu memory management unit記憶體管理單元 從而不支援虛擬位址,只能裸奔或執行rtos 實時作業系統,例如ucos 華為liteos rt thread freertos 的system,叫做微控制器 例如stm32...

跨域常見問題

這裡要說的是,跨域的幾個場景 1 ajax跨域 ajax跨域跟前端沒有什麼關係,只是後端伺服器的安全限制,但也可在前端處理。常見的處理方式分為 1 前端處理 通過jsonp的方式,這裡不細講。大概的思路是,借助標籤不存在跨域的 bug 模擬向另乙個服務發請求 2 後端處理 有幾種方式 2 ifram...