国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁(yè) > 編程 > C++ > 正文

C++中 類 和 結(jié)構(gòu)體所占內(nèi)存大小

2019-11-08 19:41:00
字體:
來(lái)源:轉(zhuǎn)載
供稿:網(wǎng)友

類所占內(nèi)存:

類所占內(nèi)存的大小是由成員變量(靜態(tài)變量除外)決定的,成員函數(shù)(這是笼統(tǒng)的說(shuō),后面會(huì)細(xì)說(shuō))是不計(jì)算在內(nèi)的。

摘抄部分:

成員函數(shù)還是以一般的函數(shù)一樣的存在。a.fun()是通過fun(a.this)來(lái)調(diào)用的。所謂成員函數(shù)只是在名義上是類里的。其實(shí)成員函數(shù)的大小不在類的對(duì)象里面,同一個(gè)類的多個(gè)對(duì)象共享函數(shù)代碼。而我們?cè)L問類的成員函數(shù)是通過類里面的一個(gè)指針實(shí)現(xiàn),而這個(gè)指針指向的是一個(gè)table,table里面記錄的各個(gè)成員函數(shù)的地址(當(dāng)然不同的編譯可能略有不同的實(shí)現(xiàn))。所以我們?cè)L問成員函數(shù)是間接獲得地址的。所以這樣也就增加了一定的時(shí)間開銷,這也就是為什么我們提倡把一些簡(jiǎn)短的,調(diào)用頻率高的函數(shù)聲明為inline形式(內(nèi)聯(lián)函數(shù))。

(一)class CBase { }; sizeof(CBase)=1;

為什么空的什么都沒有是1呢?c++要求每個(gè)實(shí)例在內(nèi)存中都有獨(dú)一無(wú)二的地址。//注意這句話!!!!!!!!!!空類也會(huì)被實(shí)例化,所以編譯器會(huì)給空類隱含的添加一個(gè)字節(jié),這樣空類實(shí)例化之后就有了獨(dú)一無(wú)二的地址了。所以空類的sizeof為1。

(二)

class CBase { int a; char p; }; sizeof(CBase)=8;記得對(duì)齊的問題。int 占4字節(jié)//注意這點(diǎn)和struct的對(duì)齊原則很像!!!!!char占一字節(jié),補(bǔ)齊3字節(jié)

(三)class CBase { public: CBase(void); virtual ~CBase(void); PRivate: int  a; char *p; }; 再運(yùn)行:sizeof(CBase)=12

C++ 類中有虛函數(shù)的時(shí)候有一個(gè)指向虛函數(shù)的指針(vptr),在32位系統(tǒng)分配指針大小為4字節(jié)。無(wú)論多少個(gè)虛函數(shù),只有這一個(gè)指針,4字節(jié)。//注意一般的函數(shù)是沒有這個(gè)指針的,而且也不占類的內(nèi)存。

(四)class CChild : public CBase { public: CChild(void); ~CChild(void); 

virtual void test();private: int b; }; 輸出:sizeof(CChild)=16;可見子類的大小是本身成員變量的大小加上父類的大小。//其中有一部分是虛擬函數(shù)表的原因,一定要知道

父類子類共享一個(gè)虛函數(shù)指針

(五)

#include<iostream.h>

class a {};

class b{};

class c:public a{

virtual void fun()=0;

};

class d:public b,public c{};

int main()

{

cout<<"sizeof(a)"<<sizeof(a)<<endl;

cout<<"sizeof(b)"<<sizeof(b)<<endl;

cout<<"sizeof(c)"<<sizeof(c)<<endl;

cout<<"sizeof(d)"<<sizeof(d)<<endl;

return 0;}

程序執(zhí)行的輸出結(jié)果為:

sizeof(a) =1

sizeof(b)=1

sizeof(c)=4

sizeof(d)=8

前三種情況比較常見,注意第四種情況。類d的大小更讓初學(xué)者疑惑吧,類d是由類b,c派生邇來(lái)的,它的大小應(yīng)該為二者之和5,為什么卻是8 呢?這是因?yàn)闉榱颂岣邔?shí)例在內(nèi)存中的存取效率.類的大小往往被調(diào)整到系統(tǒng)的整數(shù)倍.并采取就近的法則,里哪個(gè)最近的倍數(shù),就是該類的大小,所以類d的大小為8個(gè)字節(jié).

總結(jié):

空的類是會(huì)占用內(nèi)存空間的,而且大小是1,原因是C++要求每個(gè)實(shí)例在內(nèi)存中都有獨(dú)一無(wú)二的地址。

(一)類內(nèi)部的成員變量:

普通的變量:是要占用內(nèi)存的,但是要注意對(duì)齊原則(這點(diǎn)和struct類型很相似)。static修飾的靜態(tài)變量:不占用內(nèi)容,原因是編譯器將其放在全局變量區(qū)。

(二)類內(nèi)部的成員函數(shù):

普通函數(shù):不占用內(nèi)存。虛函數(shù):要占用4個(gè)字節(jié),用來(lái)指定虛函數(shù)的虛擬函數(shù)表的入口地址。所以一個(gè)類的虛函數(shù)所占用的地址是不變的,和虛函數(shù)的個(gè)數(shù)是沒有關(guān)系的結(jié)構(gòu)體所占內(nèi)存:

結(jié)構(gòu)體存在內(nèi)存對(duì)齊,類(對(duì)象)也如此,甚至于所有變量在內(nèi)存中的存儲(chǔ)也有對(duì)齊一說(shuō)(只是這些對(duì)程序員是透明的,不需要關(guān)心)。實(shí)際上,這種對(duì)齊是為了在空間與復(fù)雜度上達(dá)到平衡的一種技術(shù)手段,簡(jiǎn)單的講,是為了在可接受的空間浪費(fèi)的前提下,盡可能的提高對(duì)相同運(yùn)算過程的最少(快)處理。先舉個(gè)例子:

    假設(shè)機(jī)器字長(zhǎng)是32位的(即4字節(jié),下面示例均按此字長(zhǎng)),也就是說(shuō)處理任何內(nèi)存中的數(shù)據(jù),其實(shí)都是按32位的單位進(jìn)行的。現(xiàn)在有2個(gè)變量:    

char A; int B; 

     假設(shè)這2個(gè)變量是從內(nèi)存地址0開始分配的,如果不考慮對(duì)齊,應(yīng)該是這樣存儲(chǔ)的(見下圖,以intel上的little endian為例,為了形象,每16個(gè)字節(jié)分做一行,后同):

    因?yàn)橛?jì)算機(jī)的字長(zhǎng)是4字節(jié)的,所以在處理變量A與B時(shí)的過程可能大致為:

    A:將0x00-0x03共32位讀入寄存器,再通過左移24位再右移24位運(yùn)算得到a的值(或與0x000000FF做與運(yùn)算)

    B:將0x00-0x03這32位讀入寄存器,通過位運(yùn)算得到低24位的值;再將0x04-0x07這32位讀入寄存器,通過位運(yùn)算得到高8位的值;再與最先得到的24位做位運(yùn)算,才可得到整個(gè)32位的值。

    上面敘述可知,對(duì)a的處理是最簡(jiǎn)處理,可對(duì)b的處理,本身是個(gè)32位數(shù),處理的時(shí)候卻得折成2部分,之后再合并,效率上就有些低了。

    想解決這個(gè)問題,就需要付出幾個(gè)字節(jié)浪費(fèi)的代價(jià),改為下圖的分配方式:

    按上面的分配方式,A的處理過程不變;B卻簡(jiǎn)單得多了:只需將0x04-0x07這32位讀入寄存器就OK了。

    我們可以具體談結(jié)構(gòu)體或類成員的對(duì)齊了:

    結(jié)構(gòu)體在編譯成機(jī)器代碼后,其實(shí)就沒有本身的集合概念了,而類,實(shí)際上是個(gè)加強(qiáng)版的結(jié)構(gòu)體,類的對(duì)象在實(shí)例化時(shí),內(nèi)存中申請(qǐng)的就是一些變量的空間集合(類似于結(jié)構(gòu)體,同時(shí)也不包含函數(shù)指針)。這些集合中的每個(gè)變量,在使用中,都需要涉及上述的加工原則,自然也就需要在效率與空間之間做出權(quán)衡。

    為了便捷加工連續(xù)多個(gè)相同類型原始變量,同時(shí)簡(jiǎn)化原始變量尋址,再匯總上述最少處理原則,通常可以將原始變量的長(zhǎng)度做為針對(duì)此變量的分配單位,比如內(nèi)存可用64個(gè)單元,如果某原始變量長(zhǎng)度為8字節(jié),即使機(jī)器字長(zhǎng)為4字節(jié),分配的時(shí)候也以8字節(jié)對(duì)齊(看似IO次數(shù)是相同的),這樣,尋址、分配時(shí),均可以按每8字節(jié)為單位進(jìn)行,簡(jiǎn)化了操作,也可以更高效。

    系統(tǒng)默認(rèn)的對(duì)齊規(guī)則,追求的至少兩點(diǎn):1、變量的最高效加工 2、達(dá)到目的1的最少空間 

    舉個(gè)例子,一個(gè)結(jié)構(gòu)體如下:  

//by www.datahf.net zhangyutypedef struct T{     char c; //本身長(zhǎng)度1字節(jié)     __int64 d;  //本身長(zhǎng)度8字節(jié)    int e;  //本身長(zhǎng)度4字節(jié)    short f;  //本身長(zhǎng)度2字節(jié)    char g;  //本身長(zhǎng)度1字節(jié)    short h;  //本身長(zhǎng)度2字節(jié)}; 

    假設(shè)定義了一個(gè)結(jié)構(gòu)體變量C,在內(nèi)存中分配到了0x00的位置,顯然:

 

    對(duì)于成員C.c  無(wú)論如何,也是一次寄存器讀入,所以先占一個(gè)字節(jié)。

    對(duì)于成員C.d  是個(gè)64位的變量,如果緊跟著C.c存儲(chǔ),則讀入寄存器至少需要3次,為了實(shí)現(xiàn)最少的2次讀入,至少需要以4字節(jié)對(duì)齊;同時(shí)對(duì)于8字節(jié)的原始變量,為了在尋址單位上統(tǒng)一,則需要按8字節(jié)對(duì)齊,所以,應(yīng)該分配到0x08-0xF的位置。

    對(duì)于成員C.e  是個(gè)32位的變量,自然只需滿足分配起始為整數(shù)個(gè)32位即可,所以分配至0x10-0x13。

    對(duì)于成員C.f  是個(gè)16位的變量,直接分配在0x14-0x16上,這樣,反正只需一次讀入寄存器后加工,邊界也與16位對(duì)齊。

    對(duì)于成員C.g  是個(gè)8位的變量,本身也得一次讀入寄存器后加工,同時(shí)對(duì)于1個(gè)字節(jié)的變量,存儲(chǔ)在任何字節(jié)開始都是對(duì)齊,所以,分配到0x17的位置。

    對(duì)于成員C.h  是個(gè)16位的變量,為了保證與16位邊界對(duì)齊,所以,分配到0x18-0x1A的位置。

    分配圖如下(還不正確,耐心讀下去):

    結(jié)構(gòu)體C的占用空間到h結(jié)束就可以了嗎?我們找個(gè)示例:如果定義一個(gè)結(jié)構(gòu)體數(shù)組 CA[2],按變量分配的原則,這2個(gè)結(jié)構(gòu)體應(yīng)該是在內(nèi)存中連續(xù)存儲(chǔ)的,分配應(yīng)該如下圖:  

    分析一下上圖,明顯可知,CA[1]的很多成員都不再對(duì)齊了,究其原因,是結(jié)構(gòu)體的開始邊界不對(duì)齊。

    那結(jié)構(gòu)體的開始偏移滿足什么條件才可以使其成員全部對(duì)齊呢。想一想就明白了:很簡(jiǎn)單,保證結(jié)構(gòu)體長(zhǎng)度是原始成員最長(zhǎng)分配的整數(shù)倍即可。     上述結(jié)構(gòu)體應(yīng)該按最長(zhǎng)的.d成員對(duì)齊,即與8字節(jié)對(duì)齊,這樣正確的分配圖如下:

    當(dāng)然結(jié)構(gòu)體T的長(zhǎng)度:sizeof(T)==0x20;

     再舉個(gè)例子,看看在默認(rèn)對(duì)齊規(guī)則下,各結(jié)構(gòu)體成員的對(duì)齊規(guī)則:  

//by www.datahf.net zhangyutypedef struct A {     char c;  //1個(gè)字節(jié)    int d;  //4個(gè)字節(jié),要與4字節(jié)對(duì)齊,所以分配至第4個(gè)字節(jié)處    short e;  //2個(gè)字節(jié), 上述兩個(gè)成員過后,本身就是與2對(duì)齊的,所以之前無(wú)填充 }; //整個(gè)結(jié)構(gòu)體,最長(zhǎng)的成員為4個(gè)字節(jié),需要總長(zhǎng)度與4字節(jié)對(duì)齊,所以, sizeof(A)==12 typedef struct B {     char c;  //1個(gè)字節(jié)    __int64 d;  //8個(gè)字節(jié),位置要與8字節(jié)對(duì)齊,所以分配到第8個(gè)字節(jié)處    int e;  //4個(gè)字節(jié),成員d結(jié)束于15字節(jié),緊跟的16字節(jié)對(duì)齊于4字節(jié),所以分配到16-19    short f;  //2個(gè)字節(jié),成員e結(jié)束于19字節(jié),緊跟的20字節(jié)對(duì)齊于2字節(jié),所以分配到20-21    A g;  //結(jié)構(gòu)體長(zhǎng)為12字節(jié),最長(zhǎng)成員為4字節(jié),需按4字節(jié)對(duì)齊,所以前面跳過2個(gè)字節(jié),//到24-35字節(jié)處    char h;  //1個(gè)字節(jié),分配到36字節(jié)處    int i;  //4個(gè)字節(jié),要對(duì)齊4字節(jié),跳過3字節(jié),分配到40-43 字節(jié)}; //整個(gè)結(jié)構(gòu)體的最大分配成員為8字節(jié),所以結(jié)構(gòu)體后面加5字節(jié)填充,被到48字節(jié)。故://sizeof(B)==48;

    具體的分配圖如下:

 

 上述全部測(cè)試代碼如下:

 

//by www.datahf.net zhangyu#include "stdio.h" typedef struct A {     char c;     int d;     short e;  }; typedef struct B {     char c;     __int64 d;     int e;     short f;     A g;     char h;     int i; }; typedef struct C {     char c;     __int64 d;     int e;     short f;     char g;     short h; }; typedef struct D {     char a;     short b;     char c; }; int main() {      B *b=new B;     void *s[32];     s[0]=b;     s[1]=&b->c;     s[2]=&b->d;     s[3]=&b->e;     s[4]=&b->f;     s[5]=&b->g;     s[6]=&b->h;     s[7]=&b->g.c;     s[8]=&b->g.d;     s[9]=&b->g.e;     s[10]=&b->i;     b->c= 0x11;     b->d= 0x2222222222222222;     b->e= 0x33333333;     b->f=0x4444;     b->g.c=0x50;     b->g.d=0x51515151;     b->g.e=0x5252;     b->h=0x66;     int i1=sizeof(A);     int i2=sizeof(B);     int i3=sizeof(C);     int i4=sizeof(D);     printf("i1:%d/ni2:%d/ni3:%d/ni4:%d/n",i1,i2,i3,i4);//12 48 32 6 } 

運(yùn)行時(shí)的內(nèi)存情況如下圖:

 

最后,簡(jiǎn)單加工一下轉(zhuǎn)載過來(lái)的內(nèi)存對(duì)齊正式原則:

 

  先介紹四個(gè)概念:

1)數(shù)據(jù)類型自身的對(duì)齊值:基本數(shù)據(jù)類型的自身對(duì)齊值,等于sizeof(基本數(shù)據(jù)類型)。

2)指定對(duì)齊值:#pragma pack (value)時(shí)的指定對(duì)齊值value。

3)結(jié)構(gòu)體或者類的自身對(duì)齊值:其成員中自身對(duì)齊值最大的那個(gè)值。

4)數(shù)據(jù)成員、結(jié)構(gòu)體和類的有效對(duì)齊值:自身對(duì)齊值和指定對(duì)齊值中較小的那個(gè)值。

  有效對(duì)齊值N是最終用來(lái)決定數(shù)據(jù)存放地址方式的值,最重要。有效對(duì)齊N,就是表示“對(duì)齊在N上”,也就是說(shuō)該數(shù)據(jù)的"存放起始地址%N=0".而數(shù)據(jù)結(jié)構(gòu)中的數(shù)據(jù)變量都是按定義的先后順序來(lái)排放的。第一個(gè)數(shù)據(jù)變量的起始地址就是 數(shù)據(jù)結(jié)構(gòu)的起始地址。結(jié)構(gòu)體的成員變量要對(duì)齊排放,結(jié)構(gòu)體本身也要根據(jù)自身的有效對(duì)齊值圓整(就是結(jié)構(gòu)體成員變量占用總長(zhǎng)度需要是對(duì)結(jié)構(gòu)體有效對(duì)齊值的整 數(shù)倍)

 

#pragma pack (value)來(lái)告訴編譯器,使用我們指定的對(duì)齊值來(lái)取代缺省的。

如#pragma pack (1)  /*指定按2字節(jié)對(duì)齊*/

#pragma pack () /*取消指定對(duì)齊,恢復(fù)缺省對(duì)齊*/


發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表

圖片精選

主站蜘蛛池模板: 伊金霍洛旗| 呈贡县| 巴马| 宝丰县| 邢台市| 阜新| 环江| 余干县| 平山县| 福贡县| 松溪县| 宜黄县| 盐山县| 钟祥市| 湖南省| 平凉市| 平顺县| 都昌县| 淅川县| 江油市| 慈利县| 鄱阳县| 盘山县| 桃园县| 西宁市| 永川市| 重庆市| 十堰市| 上虞市| 会宁县| 丹棱县| 高邮市| 皮山县| 天祝| 阿拉善右旗| 元氏县| 阳泉市| 油尖旺区| 平阴县| 同仁县| 镇平县|