找回密碼
 立即注冊

QQ登錄

只需一步,快速開始

帖子
查看: 7046|回復(fù): 1
打印 上一主題 下一主題
收起左側(cè)

C語言知識補(bǔ)漏(一)結(jié)構(gòu)體指針以及位域定義

[復(fù)制鏈接]
跳轉(zhuǎn)到指定樓層
樓主
ID:51025 發(fā)表于 2014-8-29 17:30 | 只看該作者 回帖獎(jiǎng)勵(lì) |倒序?yàn)g覽 |閱讀模式
一、結(jié)構(gòu)體指針
typedef struct
{
    unsignedlong int DATA;
    unsignedlong int DIRECTION;
    unsignedlong int INTERRUPT_MASK;
    unsignedlong int EDGE_CAPTURE;
}PIO_STR;
//define LED Register
#ifdef _LED
#define LED ((PIO_STR *)LED_BASE)  //表示定義了一個(gè)PIO_STR的指針,并強(qiáng)制這個(gè)指針的值
                                //為LED_PIO_BASE,意思是定義了一個(gè)常量指針,指針類型為PIO_STR
分析:
1. PIO_STR是一個(gè)結(jié)構(gòu)體,它是:
typedef struct
{
unsigned long int DATA;
unsigned long int DIRECTION;
unsigned long int INTERRUPT_MASK;
unsigned long int EDGE_CAPTURE;

}

2. PIO_STR *是表示指向這樣一個(gè)結(jié)構(gòu)體的指針
3. LED_BASE是一個(gè)數(shù)值:0x00001800,這里代表地址
4. (PIO_STR *) LED_BASE 表示對這樣一個(gè)地址進(jìn)行強(qiáng)制轉(zhuǎn)換,使得它變成指向結(jié)構(gòu)體的指針
5. #define LED ((PIO_STR *) LED_BASE)
所以LED是一個(gè)值為0x00001800(表示指向地址0x00001800)的,指向一個(gè)PIO_STR結(jié)構(gòu)體的指針。地址已經(jīng)確定,下一步就該在0x00001800這個(gè)地址上分配一個(gè)具體的PIO_STR結(jié)構(gòu)體了。

注意:LED這個(gè)指針永遠(yuǎn)指向一個(gè)地址在0x00001800的結(jié)構(gòu)體。它不能指向其他地方,因而是一個(gè)常量指針。但是它可以對指向的結(jié)構(gòu)體進(jìn)行修改。二、位域定義
一般在結(jié)構(gòu)體的定義中,我們會見到如下代碼:
   struct
    {
     unsigned charP8OUT0         : 1;
     unsigned charP8OUT1         : 1;
     unsigned charP8OUT2         : 1;
     unsigned charP8OUT3         : 1;
     unsigned charP8OUT4         : 1;
     unsigned charP8OUT5         : 1;
     unsigned charP8OUT6         : 1;
     unsigned charP8OUT7         : 1;
    }P8OUT_bit;
結(jié)構(gòu)體內(nèi)部的語句
          unsigned char P8OUT0         : 1;
表示定義了一個(gè)位域,P8OUT0是該位域的域名,而且P8OUT0只占用一個(gè)位。
所謂C中的位域,是指信息在存儲時(shí),并不需要占用一個(gè)完整的字節(jié),而只需占幾個(gè)或一個(gè)二進(jìn)制位。為了節(jié)省存儲空間,并使處理簡便,C語言提供了一種數(shù)據(jù)結(jié)構(gòu),稱為“位域”或“位段”。所謂“位域”是把一個(gè)字節(jié)中的二進(jìn)位劃分為幾個(gè)不同的區(qū)域,并說明每個(gè)區(qū)域的位數(shù)。每個(gè)域有一個(gè)域名,允許在程序中按域名進(jìn)行操作。這樣就可以把幾個(gè)不同的對象用一個(gè)字節(jié)的二進(jìn)制位域來表示。

分享到:  QQ好友和群QQ好友和群 QQ空間QQ空間 騰訊微博騰訊微博 騰訊朋友騰訊朋友
收藏收藏 分享淘帖 頂 踩
回復(fù)

使用道具 舉報(bào)

沙發(fā)
ID:51025 發(fā)表于 2014-8-29 17:31 | 只看該作者
有些信息在存儲時(shí),并不需要占用一個(gè)完整的字節(jié), 而只需占幾個(gè)或一個(gè)二進(jìn)制位。例如在存放一個(gè)開關(guān)量時(shí),只有0和1兩種狀態(tài),用一位二進(jìn)位即可。為了節(jié)省存儲空間,并使處理簡便,C語言又提供了一種數(shù)據(jù)結(jié)構(gòu),稱為“位域”或“位段”。所謂“位域”是把一個(gè)字節(jié)中的二進(jìn)位劃分為幾個(gè)不同的區(qū)域,并說明每個(gè)區(qū)域的位數(shù)。每個(gè)域有一個(gè)域名,允許在程序中按域名進(jìn)行操作。這樣就可以把幾個(gè)不同的對象用一個(gè)字節(jié)的二進(jìn)制位域來表示。
一、位域的定義和位域變量的說明
位域定義與結(jié)構(gòu)定義相仿,其形式為:
struct 位域結(jié)構(gòu)名
{ 位域列表 };
其中位域列表的形式為: 類型說明符 位域名:位域長度
例如:
struct bs
{
    inta:8;
    intb:2;
    intc:6;
};
位域變量的說明與結(jié)構(gòu)變量說明的方式相同。 可采用先定義后說明,同時(shí)定義說明或者直接說明這三種方式。例如:
struct bs
{
    inta:8;
    intb:2;
    intc:6;
}data;
說明data為bs變量,共占兩個(gè)字節(jié)。其中位域a占8位,位域b占2位,位域c占6位。對于位域的定義尚有以下幾點(diǎn)說明:
1.一個(gè)位域必須存儲在同一個(gè)字節(jié)中,不能跨兩個(gè)字節(jié)。如一個(gè)字節(jié)所�?臻g不夠存放另一位域時(shí),應(yīng)從下一單元起存放該位域。也可以有意使某位域從下一單元開始。
例如:
struct bs
{
    unsigneda:4;
    unsigned:0;
    unsignedb:4;
    unsignedc:4;
}
在這個(gè)位域定義中,a占第一字節(jié)的4位,后4位填0表示不使用,b從第二字節(jié)開始,占用
4位,c占用4位。
2. 由于位域不允許跨兩個(gè)字節(jié),因此位域的長度不能大于一個(gè)字節(jié)的長度,也就是說不能超過8位二進(jìn)位。
3. 位域可以無位域名,這時(shí)它只用來作填充或調(diào)整位置。無名的位域是不能使用的。例如:
struct k
{
    inta:1;
    int:2;
    intb:3;
    intc:2;
};
從以上分析可以看出,位域在本質(zhì)上就是一種結(jié)構(gòu)類型, 不過其成員是按二進(jìn)位分配的。

二、位域的使用
位域的使用和結(jié)構(gòu)成員的使用相同,其一般形式為:
位域變量名·位域名
位域允許用各種格式輸出。
main(){
    structbs
       {
            unsigned a:1;
            unsigned b:3;
            unsigned c:4;
       } bit,*pbit;
   bit.a=1;
   bit.b=7;
   bit.c=15;
   printf("%d,%d,%dn",bit.a,bit.b,bit.c);
   pbit=&bit;
   pbit->a=0;
   pbit->b&=3;
   pbit->c|=1;
   printf("%d,%d,%dn",pbit->a,pbit->b,pbit->c);
}
上例程序中定義了位域結(jié)構(gòu)bs,三個(gè)位域?yàn)閍,b,c。說明了bs類型的變量bit和指向bs類型的指針變量pbit。這表示位域也是可以使用指針的。程序的9、10、11三行分別給三個(gè)位域賦值。(應(yīng)注意賦值不能超過該位域的允許范圍)程序第12行以整型量格式輸出三個(gè)域的內(nèi)容。第13行把位域變量bit的地址送給指針變量pbit。第14行用指針方式給位域a重新賦值,賦為0。第15行使用了復(fù)合的位運(yùn)算符"&=",該行相當(dāng)于:pbit->b=pbit->b&3位域b中原有值為7,與3作按位與運(yùn)算的結(jié)果為3(111&011=011,十進(jìn)制值為3)。同樣,程序第16行中使用了復(fù)合位運(yùn)算"|=".之所以要有透析基礎(chǔ)知識這么個(gè)分欄,就是告訴大家重在細(xì)節(jié)的道理,粗略的東西誰都懂,修煉內(nèi)功為高手的必經(jīng)之路.
前面的內(nèi)容存在缺陷,具體還要參考如下文章:
C99規(guī)定int、unsigned int和bool可以作為位域類型,但編譯器幾乎都對此作了擴(kuò)展,允許其它類型類型的存在。

使用位域的主要目的是壓縮存儲,其大致規(guī)則為:
1) 如果相鄰位域字段的類型相同,且其位寬之和小于類型的sizeof大小,則后面的字
段將緊鄰前一個(gè)字段存儲,直到不能容納為止;
2) 如果相鄰位域字段的類型相同,但其位寬之和大于類型的sizeof大小,則后面的字
段將從新的存儲單元開始,其偏移量為其類型大小的整數(shù)倍;
3) 如果相鄰的位域字段的類型不同,則各編譯器的具體實(shí)現(xiàn)有差異,VC6采取不壓縮方
式,Dev-C++采取壓縮方式;
4) 如果位域字段之間穿插著非位域字段,則不進(jìn)行壓縮;
5) 整個(gè)結(jié)構(gòu)體的總大小為最寬基本類型成員大小的整數(shù)倍。

typedef struct  AA
{
       unsigned char b1:5;
       unsigned char b2:5;
       unsigned char b3:5;
       unsigned char b4:5;
       unsigned char b5:5;
}AA;

sizeof(AA) = 5; 但實(shí)際上只用了25位,即4個(gè)字節(jié),

(1)typedef struct  AA
{
       unsigned int b1:5;
       unsigned int b2:5;
       unsigned int b3:5;
       unsigned int b4:5;
       unsigned int b5:5;
}AA;

(2)typedef struct  AA
{
       unsigned int b1:5;
       unsigned int b2:5;
       unsigned int b3:5;
       unsigned int b4:5;
       unsigned int b5:5;
       unsigned int b6:5;
       unsigned int b7:5;
}AA;

(1)是5個(gè)成員,按第一條規(guī)則,共占25位,按第五條規(guī)則,即sizeof(AA)=4
現(xiàn)把成員加到7個(gè),參考(2),按第一條規(guī)則,共占35位,按第五條規(guī)則,即sizeof(AA)=8,

再看一個(gè)例子:
struct test1
{
char a:1;
char :2;
long b:3;
char c:2;
};
int len = sizeof(test1);
對于上述例子,len的值應(yīng)該是12.解釋如下: 
首先以最長的類型位寬做為偏移量,最長的是long型,占4位,所以不同類型之間應(yīng)該是4個(gè)字節(jié)的偏移,即test1應(yīng)該是4字節(jié)的整數(shù)倍�!�
char a:1; //用一個(gè)字節(jié)去存儲
char :2;  //空域。因?yàn)榕c前面的a的類型相同,而兩個(gè)位域的位寬相加仍然少于8位,所以依然用1個(gè)字節(jié)表示
long b:3;//long類型的位寬是4個(gè)字節(jié),與前面的char類型不同,所以b與a之間偏移4個(gè)字節(jié),它們之間自動補(bǔ)充3個(gè)字節(jié) 
char c:2;//因?yàn)閏與b又不同型,以test1中的最長的long類型的位寬進(jìn)行偏移,所以雖然char只用1個(gè)字節(jié)就夠了
//但依然要占4個(gè)字節(jié)。
總共是12字節(jié)。


///////////////////////
struct s1
  {
  int i: 8;
  int j: 4;
  int a: 3;
  double b;
  };
  struct s2
  {
  int i: 8;
  int j: 4;
  double b;
  int a:3;
  };
  printf("sizeof(s1)= %dn", sizeof(s1));
  printf("sizeof(s2)= %dn", sizeof(s2));
  result: 16, 24
  第一個(gè)struct s1
  {
  int i: 8;
  int j: 4;
  int a: 3;
  double b;
  };
  理論上是這樣的,首先是i在相對0的位置,占8位一個(gè)字節(jié),然后,j就在相對一個(gè)字節(jié)的位置,由于一個(gè)位置的字節(jié)數(shù)是4位的倍數(shù),因此不用對齊,就放在那里了,然后是a,要在3位的倍數(shù)關(guān)系的位置上,因此要移一位,在15位的位置上放下,目前總共是18位,折算過來是2字節(jié)2位的樣子,由于double是8字節(jié)的,因此要在相對0要是8個(gè)字節(jié)的位置上放下,因此從18位開始到8個(gè)字節(jié)之間的位置被忽略,直接放在8字節(jié)的位置了,因此,總共是16字節(jié)。
回復(fù)

使用道具 舉報(bào)

您需要登錄后才可以回帖 登錄 | 立即注冊

本版積分規(guī)則

小黑屋|51黑電子論壇 |51黑電子論壇6群 QQ 管理員QQ:125739409;技術(shù)交流QQ群281945664

Powered by 單片機(jī)教程網(wǎng)

快速回復(fù) 返回頂部 返回列表