久久久久久久999_99精品久久精品一区二区爱城_成人欧美一区二区三区在线播放_国产精品日本一区二区不卡视频_国产午夜视频_欧美精品在线观看免费

專注電子技術學習與研究
當前位置:單片機教程網 >> MCU設計實例 >> 瀏覽文章

漫談c語言結構體

作者:王衍   來源:本站原創   點擊數:  更新時間:2014年03月11日   【字體:

 相信大家對于結構體都不陌生。在此,分享出本人對C語言結構體的研究和學習的總結。如果你發現這個總結中有你以前所未掌握的,那本文也算是有點價值了。當然,水平有限,若發現不足之處懇請指出。代碼文件test.c我放在下面。

在此,我會圍繞以下2個問題來分析和應用C語言結構體:

 

1. C語言中的結構體有何作用

2. 結構體成員變量內存對齊有何講究(重點)

對于一些概念的說明,我就不把C語言教材上的定義搬上來。我們坐下來慢慢聊吧。

==============================================================================================================================================
 

1. 結構體有何作用

三個月前,教研室里一個學長在華為南京研究院的面試中就遇到這個問題。當然,這只是面試中最基礎的問題。如果問你你怎么回答?

我的理解是這樣的,C語言中結構體至少有以下三個作用:

 

(1)有機地組織了對象的屬性。

比如,在STM32RTC開發中,我們需要數據來表示日期和時間,這些數據通常是年、月、日、時、分、秒。如果我們不用結構體,那么就需要定義6個變量來表示。這樣的話程序的數據結構是松散的,我們的數據結構最好是“高內聚,低耦合”的。所以,用一個結構體來表示更好,無論是從程序的可讀性還是可移植性還是可維護性皆是:

typedef struct   //公歷日期和時間結構體

{

    vu16  year;

    vu8   month;

    vu8   date;

    vu8   hour;

    vu8   min;

    vu8   sec; 

}_calendar_obj; 

 _calendar_obj calendar;  //定義結構體變量

 

(2)以修改結構體成員變量的方法代替了函數(入口參數)的重新定義。

如果說結構體有機地組織了對象的屬性表示結構體“中看”,那么以修改結構體成員變量的方法代替函數(入口參數)的重新定義就表示了結構體“中用”。繼續以上面的結構體為例子,我們來分析。假如現在我有如下函數來顯示日期和時間:


void DsipDateTime( _calendar_obj  DateTimeVal)

那么我們只要將一個_calendar_obj這個結構體類型的變量作為實參調用DsipDateTime()即可,DsipDateTime()通過DateTimeVal的成變量來實現內容的顯示。如果不用結構體,我們很可能需要寫這樣的一個函數:


void DsipDateTime( vu16 yearvu8 monthvu8 datevu8 hourvu8 minvu8 sec)

顯然這樣的形參很不可觀,數據結構管理起來也很繁瑣。如果某個函數的返回值得是一個表示日期和時間的數據,那就更復雜了。這只是一方面。

另一方面,如果用戶需要表示日期和時間的數據中還要包含星期(),這個時候,如果之前沒有用機構體,那么應該在DsipDateTime()函數中在增加一個形參vu8 week

void DsipDateTime( vu16 yearvu8 monthvu8 datevu8 weekvu8 hourvu8 minvu8 sec)

可見這種方法來傳遞參數非常繁瑣。所以以結構體作為函數的入口參數的好處之一就是

函數的聲明void DsipDateTime( _calendar_obj  DateTimeVal)不需要改變,只需要增加結構體的成員變量,然后在函數的內部實現上對calendar.week作相應的處理即可。這樣,在程序的修改、維護方面作用顯著。

typedef struct   //公歷日期和時間結構體

{

    vu16  year;

    vu8   month;

    vu8   date;

    vu8  week;

    vu8   hour;

    vu8   min;

    vu8   sec; 

}_calendar_obj; 

 _calendar_obj calendar;  //定義結構體變量

 

    (3)結構體的內存對齊原則可以提高CPU對內存的訪問速度(以空間換取時間)
    并且,結構體成員變量的地址可以根據基地址
(以偏移量
offset)計算。我們先來看看下面的一段簡單的程序,對于此程序的分析會在第2部分結構體成員變量內存對齊中詳細說明。

}

 

#include<stdio.h>
 
int main()
{
    struct	  //聲明結構體char_short_long
    {
        char  c;
        short s;
        long  l;
    }char_short_long;
 
    struct	  //聲明結構體long_short_char
    {
        long  l;
        short s;
        char  c;
    }long_short_char;
 
    struct	  //聲明結構體char_long_short
    {
        char  c;
        long  l;
        short s;
    }char_long_short;
 
printf(" \n");
printf(" Size of char   = %d bytes\n",sizeof(char));
printf(" Size of shrot  = %d bytes\n",sizeof(short));
printf(" Size of long   = %d bytes\n",sizeof(long));
printf(" \n");	//char_short_long
printf(" Size of char_short_long       = %d bytes\n",sizeof(char_short_long));
printf("     Addr of char_short_long.c = 0x%p (10進制:%d)\n",&char_short_long.c,&char_short_long.c);
printf("     Addr of char_short_long.s = 0x%p (10進制:%d)\n",&char_short_long.s,&char_short_long.s);
printf("     Addr of char_short_long.l = 0x%p (10進制:%d)\n",&char_short_long.l,&char_short_long.l);
printf(" \n");
 
printf(" \n");	//long_short_char
printf(" Size of long_short_char       = %d bytes\n",sizeof(long_short_char));
printf("     Addr of long_short_char.l = 0x%p (10進制:%d)\n",&long_short_char.l,&long_short_char.l);
printf("     Addr of long_short_char.s = 0x%p (10進制:%d)\n",&long_short_char.s,&long_short_char.s);
printf("     Addr of long_short_char.c = 0x%p (10進制:%d)\n",&long_short_char.c,&long_short_char.c);
printf(" \n");
 
printf(" \n");	//char_long_short
printf(" Size of char_long_short       = %d bytes\n",sizeof(char_long_short));
printf("     Addr of char_long_short.c = 0x%p (10進制:%d)\n",&char_long_short.c,&char_long_short.c);
printf("     Addr of char_long_short.l = 0x%p (10進制:%d)\n",&char_long_short.l,&char_long_short.l);
printf("     Addr of char_long_short.s = 0x%p (10進制:%d)\n",&char_long_short.s,&char_long_short.s);
printf(" \n");
return 0;
}


 

 

程序的運行結果如下(注意:括號內的數據是成員變量的地址的十進制形式)


 

2. 結構體成員變量內存對齊

首先,我們來分析一下上面程序的運行結果。前三行說明在我的程序中,char型占1個字節,short型占2個字節,long型占4個字節。char_short_longlong_short_charchar_long_short是三個結構體成員相同但是成員變量的排列順序不同。并且從程序的運行結果來看, 

       Size   of   char_short_long    = 8 bytes
    Size of long_short_char = 8 bytes
    Size of char_long_short = 12 bytes  //比前兩種情況大4 byte 

    并且,還要注意到,1 byte (char)+ 
2 byte (short)+ 4 byte (long) = 7 byte,而不是8 byte。

   所以,結構體成員變量的放置順序影響著結構體所占的內存空間的大小。一個結構體變量所占內存的大小不一定等于其成員變量所占空間之和。如果一個用戶程序或者操作系統(比如uC/OS-II)中存在大量結構體變量時,這種內存占用必須要進行優化,也就是說,結構體內部成員變量的排列次序是有講究的。

結構體成員變量到底是如何存放的呢?

在這里,我就不賣關子了,直接給出如下結論,在沒有#pragma pack宏的情況下:

 

原則1  結構(struct或聯合union)的數據成員,第一個數據成員放在offset0的地方,以后每個數據成員存儲的起始位置要從該成員大小的整數倍開始(比如int32位機為4字節,則要從4的整數倍地址開始存儲)。

原則2  結構體的總大小,也就是sizeof的結果,必須是其內部最大成員的整數倍,不足的要補齊。

   *原則3  結構體作為成員時,結構體成員要從其內部最大元素大小的整數倍地址開始存儲。(struct a里存有struct bb里有charintdouble等元素,那b應該從8的整數倍地址處開始存儲,因為sizeof(double) = 8 bytes

 

這里,我們結合上面的程序來分析(暫時不討論原則3)

先看看char_short_longlong_short_char這兩個結構體,從它們的成員變量的地址可以看出來,這兩個結構體符合原則1和原則2。注意,在 char_short_long的成員變量的地址中, char_short_long.s的地址是1244994,也就是說,1244993是“空的”,只是被“占位”了!

再看看char_long_short這個結構體,char_long_short的地址分布情況如下表:

 

 

成員變量

成員變量十六進制地址

成員變量十進制地址

char_long_short.c

0x0012FF2C

1244972

char_long_short.l

0x0012FF30

1244976

char_long_short.s

0x0012FF34

1244980

 

 

可見,其內存分布圖如下,共12 bytes

 

地址

1244972

1244973

1244974

1244975

1244976

1244977

1244978

1244979

1244980

1244981

1244982

1244983

成員

.c

 

 

 

.l

.s

 

 

 

 

首先,1244972能被1整除,所以char_long_short.c放在1244972處沒有問題(其實,就char型成員變量自身來說,其放在任何地址單元處都沒有問題),根據原則1,在之后的1244973~1244975中都沒有能被4(因為sizeof(long)=4bytes)整除的,1244976能被4整除,所以char_long_short.l應該放在1244976處,那么同理,最后一個.s(sizeof(short)=2 bytes)是應該放在1244980處。

是不是這樣就結束了?不是,還有原則2。根據原則2的要求,char_long_short這個結構體所占的空間大小應該是其占內存空間最大的成員變量的大小的整數倍。如果我們到此就結束了,那么char_long_short所占的內存空間是1244972~1244981共計10bytes,不符合原則2,所以,必須在最后補齊2個 bytes(1244982~1244983)

至此,一個結構體的內存布局完成了。

下面我們按照上述原則,來驗證這樣的分析是不是正確。按上面的分析,地址單元124497312449741244975以及12449821244983都是空的(至少char_long_short未用到,只是“占位”了)。如果我們的分析是正確的,那么,定義這樣一個結構體,其所占內存也應該是12 bytes

struct //聲明結構體char_long_short_new

{

     char  c;

     char  add1;  //補齊空間
         char  add2;  //補齊空間
         char  add3;  //補齊空間

 

     long  l;

     short s;

     
         char  add4;  //補齊空間
         char  add5;  //補齊空間

 

}char_long_short_new;

 

運行結果如下:


 

可見,我們的分析是正確的。至于原則3,大家可以自己編程驗證,這里就不再討論了。


    所以,無論你是在VC6.0還是Keil C51,還是Keil MDK中,當你需要定義一個結構體時,只要你稍微留心
結構體成員變量內存對齊這一現象,就可以在很大程度上節約MCURAM。這一點不僅僅應用于實際編程,在很多大型公司,比如IBM、微軟、百度、華為的筆試和面試中,也是常見的。

本例完整的程序代碼下載: http://www.zg4o1577.cn/f/cjgt.rar 

關閉窗口

相關文章

主站蜘蛛池模板: 亚洲色图网址 | 亚洲精品久久久久久久久久久久久 | 欧美久久久电影 | 成人黄色电影在线播放 | 国产精品成av人在线视午夜片 | 成人午夜免费在线视频 | 精品美女 | 一区二区三区精品 | 国产欧美精品 | 日韩一区二区三区在线观看 | 国产亚洲一区在线 | 亚洲一二三区不卡 | 精品国产一区二区三区日日嗨 | 久久青青| 成人精品一区二区 | 日韩欧美一级精品久久 | 亚洲国产成人久久综合一区,久久久国产99 | 特级毛片www | 爱爱视频日本 | 五月婷婷色 | 91精品国产高清一区二区三区 | 做a视频| 婷婷激情综合 | 精品久久久久久久久亚洲 | 羞羞网站免费观看 | 免费成人高清在线视频 | 黄色片在线 | 欧美一区2区三区4区公司 | 最新国产精品精品视频 | 成人性视频在线 | 中文字幕一区在线观看视频 | 国产综合精品 | 久久亚洲一区二区 | 日韩高清一区 | 欧美日韩亚洲国产综合 | 亚洲欧洲在线看 | 国产最好的av国产大片 | 国产激情在线 | av片免费 | 国产高清精品在线 | 日韩在线不卡视频 |