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

 找回密碼
 立即注冊

QQ登錄

只需一步,快速開始

搜索
查看: 4272|回復(fù): 2
收起左側(cè)

漫談C語言結(jié)構(gòu)體

[復(fù)制鏈接]
ID:255072 發(fā)表于 2018-11-15 16:30 | 顯示全部樓層 |閱讀模式
  相信大家對于結(jié)構(gòu)體都不陌生。在此,分享出本人對C語言結(jié)構(gòu)體的學(xué)習(xí)心得。如果你發(fā)現(xiàn)這個總結(jié)中有你以前所未掌握的,那本文也算是有點價值了。當(dāng)然,水平有限,若發(fā)現(xiàn)不足之處懇請指出。代碼文件test.c我放在下面。
  在此,我會圍繞以下2個問題來分析和應(yīng)用C語言結(jié)構(gòu)體:
  1. C語言中的結(jié)構(gòu)體有何作用
  2. 結(jié)構(gòu)體成員變量內(nèi)存對齊有何講究(重點)
  對于一些概念的說明,我就不把C語言教材上的定義搬上來。我們坐下來慢慢聊吧。
  =============================================================================================================
  1. 結(jié)構(gòu)體有何作用
  三個月前,教研室里一個學(xué)長在華為南京研究院的面試中就遇到這個問題。當(dāng)然,這只是面試中最基礎(chǔ)的問題。如果問你你怎么回答?
  我的理解是這樣的,C語言中結(jié)構(gòu)體至少有以下三個作用:
  (1)有機地組織了對象的屬性。
  比如,在STM32的RTC開發(fā)中,我們需要數(shù)據(jù)來表示日期和時間,這些數(shù)據(jù)通常是年、月、日、時、分、秒。如果我們不用結(jié)構(gòu)體,那么就需要定義6個變量來表示。這樣的話程序的數(shù)據(jù)結(jié)構(gòu)是松散的,我們的數(shù)據(jù)結(jié)構(gòu)最好是“高內(nèi)聚,低耦合”的。所以,用一個結(jié)構(gòu)體來表示更好,無論是從程序的可讀性還是可移植性還是可維護(hù)性皆是:
1.png


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

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


  那么我們只要將一個_calendar_obj這個結(jié)構(gòu)體類型的變量作為實參調(diào)用DsipDateTime()即可,DsipDateTime()通過DateTimeVal的成變量來實現(xiàn)內(nèi)容的顯示。如果不用結(jié)構(gòu)體,我們很可能需要寫這樣的一個函數(shù):
3.png


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

  另一方面,如果用戶需要表示日期和時間的數(shù)據(jù)中還要包含星期(周),這個時候,如果之前沒有用機構(gòu)體,那么應(yīng)該在DsipDateTime()函數(shù)中在增加一個形參vu8 week:
4.png


  可見這種方法來傳遞參數(shù)非常繁瑣。所以以結(jié)構(gòu)體作為函數(shù)的入口參數(shù)的好處之一就是

  函數(shù)的聲明void DsipDateTime( _calendar_obj DateTimeVal)不需要改變,只需要增加結(jié)構(gòu)體的成員變量,然后在函數(shù)的內(nèi)部實現(xiàn)上對calendar.week作相應(yīng)的處理即可。這樣,在程序的修改、維護(hù)方面作用顯著。
5.png


  (3)結(jié)構(gòu)體的內(nèi)存對齊原則可以提高CPU對內(nèi)存的訪問速度(以空間換取時間)。

  并且,結(jié)構(gòu)體成員變量的地址可以根據(jù)基地址(以偏移量offset)計算。我們先來看看下面的一段簡單的程序,對于此程序的分析會在第2部分結(jié)構(gòu)體成員變量內(nèi)存對齊中詳細(xì)說明。
6.png


  程序的運行結(jié)果如下(注意:括號內(nèi)的數(shù)據(jù)是成員變量的地址的十進(jìn)制形式):
7.jpg


  2. 結(jié)構(gòu)體成員變量內(nèi)存對齊

  首先,我們來分析一下上面程序的運行結(jié)果。前三行說明在我的程序中,char型占1個字節(jié),short型占2個字節(jié),long型占4個字節(jié)。char_short_long、long_short_char和char_long_short是三個結(jié)構(gòu)體成員相同但是成員變量的排列順序不同。并且從程序的運行結(jié)果來看,
8.png


  并且,還要注意到,1 byte (char)+ 2 byte (short)+ 4 byte (long) = 7 byte,而不是8 byte。
  所以,結(jié)構(gòu)體成員變量的放置順序影響著結(jié)構(gòu)體所占的內(nèi)存空間的大小。一個結(jié)構(gòu)體變量所占內(nèi)存的大小不一定等于其成員變量所占空間之和。如果一個用戶程序或者操作系統(tǒng)(比如uC/OS-II)中存在大量結(jié)構(gòu)體變量時,這種內(nèi)存占用必須要進(jìn)行優(yōu)化,也就是說,結(jié)構(gòu)體內(nèi)部成員變量的排列次序是有講究的。
  結(jié)構(gòu)體成員變量到底是如何存放的呢?
  在這里,我就不賣關(guān)子了,直接給出如下結(jié)論,在沒有#pragma pack宏的情況下:
  原則1 結(jié)構(gòu)(struct或聯(lián)合union)的數(shù)據(jù)成員,第一個數(shù)據(jù)成員放在offset為0的地方,以后每個數(shù)據(jù)成員存儲的起始位置要從該成員大小的整數(shù)倍開始(比如int在32位機為4字節(jié),則要從4的整數(shù)倍地址開始存儲)。
  原則2 結(jié)構(gòu)體的總大小,也就是sizeof的結(jié)果,必須是其內(nèi)部最大成員的整數(shù)倍,不足的要補齊。
  *原則3 結(jié)構(gòu)體作為成員時,結(jié)構(gòu)體成員要從其內(nèi)部最大元素大小的整數(shù)倍地址開始存儲。(struct a里存有struct b,b里有char,int,double等元素時,那么b應(yīng)該從8的整數(shù)倍地址處開始存儲,因為sizeof(double) = 8 bytes)
  這里,我們結(jié)合上面的程序來分析(暫時不討論原則3)。
  先看看char_short_long和long_short_char這兩個結(jié)構(gòu)體,從它們的成員變量的地址可以看出來,這兩個結(jié)構(gòu)體符合原則1和原則2。注意,在 char_short_long的成員變量的地址中, char_short_long.s的地址是1244994,也就是說,1244993是“空的”,只是被“占位”了!

  再看看char_long_short這個結(jié)構(gòu)體,char_long_short的地址分布情況如下表:
9.png


  首先,1244972能被1整除,所以char_long_short.c放在1244972處沒有問題(其實,就char型成員變量自身來說,其放在任何地址單元處都沒有問題),根據(jù)原則1,在之后的1244973~1244975中都沒有能被4(因為sizeof(long)=4bytes)整除的,1244976能被4整除,所以char_long_short.l應(yīng)該放在1244976處,那么同理,最后一個.s(sizeof(short)=2 bytes)是應(yīng)該放在1244980處。
  是不是這樣就結(jié)束了?不是,還有原則2。根據(jù)原則2的要求,char_long_short這個結(jié)構(gòu)體所占的空間大小應(yīng)該是其占內(nèi)存空間最大的成員變量的大小的整數(shù)倍。如果我們到此就結(jié)束了,那么char_long_short所占的內(nèi)存空間是1244972~1244981共計10bytes,不符合原則2,所以,必須在最后補齊2個 bytes(1244982~1244983)。
  至此,一個結(jié)構(gòu)體的內(nèi)存布局完成了。

  下面我們按照上述原則,來驗證這樣的分析是不是正確。按上面的分析,地址單元1244973、1244974、1244975以及1244982、1244983都是空的(至少char_long_short未用到,只是“占位”了)。如果我們的分析是正確的,那么,定義這樣一個結(jié)構(gòu)體,其所占內(nèi)存也應(yīng)該是12 bytes:
10.png


  運行結(jié)果如下:
11.jpg


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

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





回復(fù)

使用道具 舉報

ID:138119 發(fā)表于 2018-11-29 17:24 | 顯示全部樓層
不錯!不錯!正好幫助理解!
回復(fù)

使用道具 舉報

ID:223999 發(fā)表于 2018-12-29 09:42 | 顯示全部樓層
不錯學(xué)習(xí)
回復(fù)

使用道具 舉報

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

本版積分規(guī)則

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

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

快速回復(fù) 返回頂部 返回列表
主站蜘蛛池模板: 黄色一级大片在线免费看产 | 在线看片国产 | 羞羞视频在线观看 | 日本黄色大片免费 | 日韩在线91| 在线视频三区 | 亚洲精品一区二区三区蜜桃久 | 日本精品一区二区三区在线观看视频 | 国产综合av | 久久精品国产亚洲 | 久久久精品视频免费看 | 在线黄| 日本不卡一区二区三区 | 五月婷婷激情网 | 免费视频成人国产精品网站 | 亚洲欧美在线免费观看 | 成人欧美一区二区三区在线播放 | 久久久精 | 国产精品一区在线 | 亚洲国产激情 | 亚洲激情一区二区三区 | 成人国产一区二区三区精品麻豆 | 五月天激情综合网 | 久久久精彩视频 | 婷婷91 | 日韩精品无码一区二区三区 | 国产一区二区三区久久久久久久久 | 精品欧美一区二区三区久久久 | 天天操天天摸天天干 | 国产aa| 国产精品一区二区av | 精品欧美 | 欧美伊人影院 | 国产精品揄拍一区二区久久国内亚洲精 | 亚洲一区二区视频在线播放 | 国产精品大片在线观看 | 一区二区三区影院 | 在线日韩| 一级黄色片在线看 | 久久久av| 喷水毛片 |