變數的宣告,會配置其所需的記憶體。每種變數所需的大小不一樣,比如說char是1 bytes,int 是4 bytes,double則是8 bytes。如果是這樣,那這些大小在記憶體中是如何擺放呢?是直覺的一個接一個放嗎?其實不然。這牽扯到performance / efficiency的問題! 在32 bits的架構上,一次的資料存取也就是32 bits (4 bytes)。而這4 bytes 不是隨便從哪個點抓都可以,而是以4 bytes為單位,不管需要的是其中那個byte,就抓那4個bytes。比如說,抓第0,4,8 ,12....等,而不會是從3,7,9開始抓4個bytes。

這代表什麼呢?這表示了對齊 (alignment)的重要,因為會影響到幾次才抓的完。如果要抓一個4 bytes 的int,而這個int是從6擺到10,那就表示要抓兩次,自然效能較差了。因此,在struct的宣告是存在對齊這件事的。見下例:

#include <stdio.h>

int main(int argc, char* argv[])
{
        struct align_test
        {
                char x; //1 byte
                int y;  //4 bytes
                short int z;  //2bytes
        };
        struct align_test test;
        printf("test size is %d\n", sizeof(test));
        printf("x size is %d\n", sizeof(test.x));
        printf("y size is %d\n", sizeof(test.y));
        printf("z size is %d\n", sizeof(test.z));
        printf("address of test is %p\n", &test);
        printf("address of x is %p\n", &test.x);
        printf("address of y is %p\n", &test.y);
        printf("address of z is %p\n", &test.z);
        return 0;
}

align_test有3個成員,這樣算一算,一個align_test的structure應該是佔1+4+2=7個bytes,但結果如下:

test size is 12
x size is 1
y size is 4
z size is 2
address of test is 0xbfd450a8
address of x is 0xbfd450a8
address of y is 0xbfd450ac
address of z is 0xbfd450b0

注意看其位址

  |----|----|----|----|----|----|----|----|----|----|----|----|

 8       9                         c                                   0                                 4

 x        p       p       p       y       y       y       y      z       z       p       p

以4 bytes alignment來說x用了8,y要4 bytes,只好從c開始放,因為9,10,11放不下。然後z 2 byte,放在0,1。沒用到的,就是所謂的padding了,因此如果是這樣宣告,其實是變成下面這樣

        struct align_test
        {
                char x; //1 byte

                char padding0[3];
                int y;  //4 bytes
                short int z;  //2bytes

                char padding1[2];
        };

這樣的存取效率會比較好。但有沒有辦法做改變?比如說大家擠一下,不要浪費空間。當然是有的,可以這樣做


        #pragma pack(push)  /* push current alignment to stack */
        #pragma pack(16)     /* set alignment to 1 16 byte boundary */
        struct align_test
        {
                char x; //1 byte
                int y;  //4 bytes
                short int z;  //2bytes
        };
        #pragma pack(pop)   /* restore original alignment from stack */

結果如下,從其address也看的出來:

test size is 7
x size is 1
y size is 4
z size is 2
address of test is 0xbfa06d6d
address of x is 0xbfa06d6d
address of y is 0xbfa06d6e
address of z is 0xbfa06d72

progma是設定其做幾個byte的對齊,但只能往下設定,以該結構中成員size最大的為上限。比如說,int是4byte,只能改成1,2,或4。因此,改成8,16都會被忽略,而用4來配置。通常會發生在控制硬體時改變其pragma,以便根據其大小來正確的取址,而不用煩惱padding。

當然,也可以硬幹,把程式寫的聰明一點,擺放整齊。比如說,換個位置:

 

        struct align_test
        {
                char x; //1 byte
                short int y;  //2bytes
                int z;  //4 bytes
        };

結果如下:

test size is 8
x size is 1
y size is 2
z size is 4
address of test is 0xbfa565bc
address of x is 0xbfa565bc
address of y is 0xbfa565be
address of z is 0xbfa565c0

 

Reference

Data structure alignment

kezeodsnx 發表在 痞客邦 PIXNET 留言(3) 人氣()


留言列表 (3)

發表留言
  • YY
  • #pragma pack(16) /* set alignment to 1 byte boundary */

    應該是1不是16.
    謝謝你的文章!
  • 李子
  • 我想應該是改後面註解的"1 byte"===>"16 bytes"
    依照他接下來的說明
    要講的是設定16沒有用
    會直接變成最大值int 的"4 bytes"

    很有用的文章!!
  • 123
  • 看就知道是門外漢寫的文章,6到10居然說有4個byte,而且明明就是抓3次卻說抓2次。
    還有大哥,你那個例子的memory位址示意圖整個歪掉了都沒發現?