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

 找回密碼
 立即注冊

QQ登錄

只需一步,快速開始

搜索
查看: 1545|回復: 0
打印 上一主題 下一主題
收起左側

內存對齊問題

[復制鏈接]
跳轉到指定樓層
樓主
ID:107189 發表于 2016-3-6 00:17 | 只看該作者 回帖獎勵 |正序瀏覽 |閱讀模式

內存對齊問題
什么是內存對齊

   考慮下面的結構:

struct foo
{
    char c1;
    short s;
    char c2;
    int i;
};


         
    假設這個結構的成員在內存中是緊湊排列的,假設c1的地址是0,那么s的地址就應該是1,c2的地址就是3,i的地址就是4。也就是:

    c1 00000000, s 00000001, c2 00000003, i 00000004。

    可是,我們在Visual c/c++ 6中寫一個簡單的程序:

    struct foo a;
    printf("c1 %p, s %p, c2 %p, i %p\n",
        (unsigned int)(void*)&a.c1 - (unsigned int)(void*)&a,
        (unsigned int)(void*)&a.s - (unsigned int)(void*)&a,
        (unsigned int)(void*)&a.c2 - (unsigned int)(void*)&a,
        (unsigned int)(void*)&a.i - (unsigned int)(void*)&a);


     
    運行,輸出:

    c1 00000000, s 00000002, c2 00000004, i 00000008。

    為什么會這樣?這就是內存對齊而導致的問題。




為什么會有內存對齊

    以下內容節選自《Intel Architecture 32 Manual》。
    字,雙字,和四字在自然邊界上不需要在內存中對齊。(對字,雙字,和四字來說,自然邊界分別是偶數地址,可以被4整除的地址,和可以被8整除的地址。)
    無論如何,為了提高程序的性能,數據結構(尤其是棧)應該盡可能地在自然邊界上對齊。原因在于,為了訪問未對齊的內存,處理器需要作兩次內存訪問;然而,對齊的內存訪問僅需要一次訪問。
    一個字或雙字操作數跨越了4字節邊界,或者一個四字操作數跨越了8字節邊界,被認為是未對齊的,從而需要兩次總線周期來訪問內存。一個字起始地址是奇數但卻沒有跨越字邊界被認為是對齊的,能夠在一個總線周期中被訪問。
    某些操作雙四字的指令需要內存操作數在自然邊界上對齊。如果操作數沒有對齊,這些指令將會產生一個通用保護異常(#GP)。雙四字的自然邊界是能夠被16整除的地址。其他的操作雙四字的指令允許未對齊的訪問(不會產生通用保護異常),然而,需要額外的內存總線周期來訪問內存中未對齊的數據。




編譯器對內存對齊的處理

    缺省情況下,c/c++編譯器默認將結構、棧中的成員數據進行內存對齊。因此,上面的程序輸出就變成了:

    c1 00000000, s 00000002, c2 00000004, i 00000008。

    編譯器將未對齊的成員向后移,將每一個都成員對齊到自然邊界上,從而也導致了整個結構的尺寸變大。盡管會犧牲一點空間(成員之間有空洞),但提高了性能。
    也正是這個原因,我們不可以斷言sizeof(foo) == 8。在這個例子中,sizeof(foo) == 12。




如何避免內存對齊的影響

    那么,能不能既達到提高性能的目的,又能節約一點空間呢?有一點小技巧可以使用。比如我們可以將上面的結構改成:

struct bar
{
    char c1;
    char c2;
    short s;
    int i;
};


    這樣一來,每個成員都對齊在其自然邊界上,從而避免了編譯器自動對齊。在這個例子中,sizeof(bar) == 8。

    這個技巧有一個重要的作用,尤其是這個結構作為API的一部分提供給第三方開發使用的時候。第三方開發者可能將編譯器的默認對齊選項改變,從而造成這個結構在你的發行的DLL中使用某種對齊方式,而在第三方開發者哪里卻使用另外一種對齊方式。這將會導致重大問題。
    比如,foo結構,我們的DLL使用默認對齊選項,對齊為:

    c1 00000000, s 00000002, c2 00000004, i 00000008,同時sizeof(foo) == 12。
   
    而第三方將對齊選項關閉,導致:

    c1 00000000, s 00000001, c2 00000003, i 00000004,同時sizeof(foo) == 8。




如何使用c/c++中的對齊選項

    vc6中的編譯選項有 /Zp[1|2|4|8|16] ,/Zp1表示以1字節邊界對齊,相應的,/Zpn表示以n字節邊界對齊。n字節邊界對齊的意思是說,一個成員的地址必須安排在成員的尺寸的整數倍地址上或者是n的整數倍地址上,取它們中的最小值。也就是:
    min ( sizeof ( member ),  n)
    實際上,1字節邊界對齊也就表示了結構成員之間沒有空洞。
    /Zpn選項是應用于整個工程的,影響所有的參與編譯的結構。
    要使用這個選項,可以在vc6中打開工程屬性頁,c/c++頁,選擇Code Generation分類,在Struct member alignment可以選擇。

    要專門針對某些結構定義使用對齊選項,可以使用#pragma pack編譯指令。指令語法如下:
#pragma pack( [ show ] | [ push | pop ] [, identifier ] , n  )
    意義和/Zpn選項相同。比如:

#pragma pack(1)
struct foo_pack
{
    char c1;
    short s;
    char c2;
    int i;
};
#pragma pack()






棧內存對齊

    我們可以觀察到,在vc6中棧的對齊方式不受結構成員對齊選項的影響。(本來就是兩碼事)。它總是保持對齊,而且對齊在4字節邊界上。

驗證代碼


#include <stdio.h>

struct foo
{
    char c1;
    short s;
    char c2;
    int i;
};

struct bar
{
    char c1;
    char c2;
    short s;
    int i;
};

#pragma pack(1)
struct foo_pack
{
    char c1;
    short s;
    char c2;
    int i;
};
#pragma pack()


int main(int argc, char* argv[])
{
    char c1;
    short s;
    char c2;
    int i;

    struct foo a;
    struct bar b;
    struct foo_pack p;

    printf("stack c1 %p, s %p, c2 %p, i %p\n",
        (unsigned int)(void*)&c1 - (unsigned int)(void*)&i,
        (unsigned int)(void*)&s - (unsigned int)(void*)&i,
        (unsigned int)(void*)&c2 - (unsigned int)(void*)&i,
        (unsigned int)(void*)&i - (unsigned int)(void*)&i);

    printf("struct foo c1 %p, s %p, c2 %p, i %p\n",
        (unsigned int)(void*)&a.c1 - (unsigned int)(void*)&a,
        (unsigned int)(void*)&a.s - (unsigned int)(void*)&a,
        (unsigned int)(void*)&a.c2 - (unsigned int)(void*)&a,
        (unsigned int)(void*)&a.i - (unsigned int)(void*)&a);

    printf("struct bar c1 %p, c2 %p, s %p, i %p\n",
        (unsigned int)(void*)&b.c1 - (unsigned int)(void*)&b,
        (unsigned int)(void*)&b.c2 - (unsigned int)(void*)&b,
        (unsigned int)(void*)&b.s - (unsigned int)(void*)&b,
        (unsigned int)(void*)&b.i - (unsigned int)(void*)&b);

    printf("struct foo_pack c1 %p, s %p, c2 %p, i %p\n",
        (unsigned int)(void*)&p.c1 - (unsigned int)(void*)&p,
        (unsigned int)(void*)&p.s - (unsigned int)(void*)&p,
        (unsigned int)(void*)&p.c2 - (unsigned int)(void*)&p,
        (unsigned int)(void*)&p.i - (unsigned int)(void*)&p);

    printf("sizeof foo is %d\n", sizeof(foo));
    printf("sizeof bar is %d\n", sizeof(bar));
    printf("sizeof foo_pack is %d\n", sizeof(foo_pack));
   
    return 0;
}


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

使用道具 舉報

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

本版積分規則

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

Powered by 單片機教程網

快速回復 返回頂部 返回列表
主站蜘蛛池模板: 在线观看视频一区二区三区 | 视频一区在线 | 久在线观看| 日本欧美国产在线观看 | 天天干在线播放 | 欧美日韩高清 | 一区二区三区久久 | 青青久在线视频 | 精品国产乱码一区二区三 | 久久这里只有精品首页 | 亚洲精品一区二区网址 | 99在线国产 | 久久综合国产精品 | 天天碰日日操 | 成人午夜激情 | 国产一区二区久久 | 天天干天天干 | 久久精品一区 | 免费看的黄网站 | 中文日韩字幕 | 久久精品69 | 国产 亚洲 网红 主播 | 日韩中文字幕视频 | 欧美5区 | 在线观看国产视频 | 狠狠干av | 久久机热 | 精品国产青草久久久久96 | 欧美精品一区二区蜜桃 | 欧美一级三级 | 欧美激情亚洲天堂 | 香蕉视频黄色 | 欧美三级久久久 | 久久6| 成人免费网站www网站高清 | 欧美日韩中文国产一区发布 | 视频一区二区三区在线观看 | 亚洲国产一区二区三区 | 久草视频在线播放 | 51ⅴ精品国产91久久久久久 | 午夜精品久久久久久久星辰影院 |