C的0長數組以及__attribute__((packed))_

來源:互聯網
上載者:User

一、零長數組(另一篇文章參考這裡)

在標準 C 或者 C++ 中由於不支援 0 長度的數組,所以 int array[0]; 這樣的定義是非法的。不過有些編譯器(如GCC)的擴充功能支援 0 長度的數組。

    在 C 中,0 長度的數組的主要用途是用來作為結構體的最後一個成員,然後用它來訪問此結構體對象之後的一段記憶體(通常是動態分配的記憶體)。由於其非標準性,在程式中盡量避免使用 0 長度的數組。作為替換,可以使用 C99 標準中的不完整數組來替換 0 長度的數組定義。如:

        typedef struct _X {
            int a;
            char array[]; //注意,因為是不完整數組,因此不可以計算sizeof(X),無法編譯通過
        } X;

    在GNU的gcc-4.4.0的官方指南中的5.14節Arrays of Length Zero一節中,它是如此寫道:

        struct line {
            int length;
            char contents[0];
        };

        //...ommit code here

struct line *thisline = (struct line *) malloc (sizeof (struct line) + this_length);
        thisline->length = this_length;

    這個用法主要用於變長Buffer,struct line的大小為4,結構體中的contents[0]不佔用任何空間,甚至是一個指標的空間都不佔(待會兒下面的範例程式碼會驗證),contents在這兒只是表示一個常量指標,這個特性是用編譯器來實現的,即在使用 thisline->contents的時候,這個指標就是表示分配記憶體位址中的某塊buffer,比如malloc (sizeof (struct line) + this_length)返回的是0x8f00a40,thisline->contents指向的位置就是(0x8f00a40 + sizeof(struct line)),而這兒sizeof(struct line)僅僅是一個int的四位元組。

    對於這個用法,我們定義的結構體指標可以指向任意長度的記憶體buffer,這個技巧在變長buffer中使用起來相當方便。可能有朋友說,為什麼不把最後的contents直接定義為一個指標呢?這兒的差別是這樣的,如果定義為一個指標,它需要佔用4Bytes,並且在申請好記憶體後必須人為賦地址才可以。如果使用這個用法,這個常量指標不佔用空間,並且無需賦值。

    但是,方便並不是絕對的,在釋放分配的記憶體的時候,由於函數free會認為*thisline 只是指向一個4位元組的指標,即只會釋放length的空間,而對於後面佔據大頭的buffer卻視而不見,這個就需要人為幹預;而對於後面的聲明指標的方式,則可以直接用Free(thisline->contents)的方式釋放掉分配的記憶體。(這地方說的不明白,讓我理解之後,感覺此話是錯誤的。)

    如果將零長數組array換成指標*array來使用的話,指標必須重新分配一段記憶體之後才能使用,那麼當想要用socket發送結構體指標的時候,並不會將指標array申請的記憶體發送過去,因為是不連續的,所以接受socket發送來的資料後,會發現該資料並不是自己想要的

    ASSERT:除非必要,不要輕易使用這個功能,GNU C下可以編譯通過,所以你在使用vc++,那就不用嘗試了,編譯都無法通過。

C語言: 驗證0長數組和__attribute__((packed))

01 #include <stdio.h>
02 #include <stdlib.h>
03 #include <string.h>
04
05 #define offsetof(S,t)     (size_t)&(((S *)0)->t)    //求結構體中位移量的宏,C++中存在此宏,C中需自己定義
06
07 struct zero_arry_t{
08 unsigned int i;
09 char arry[];
10 };
11
12 struct Test{
13 int len;
14 char content[0];
15 };
16
17 typedef struct _S1{
18 char a;
19 char b;
20 double c;
21 }S1;
22
23 typedef struct _S2{
24 char a;
25 char b;
26 double c;
27 }__attribute__((packed)) S2;    //__attribute__((packed))的作用就是告訴編譯器取消結構在編譯過程中的最佳化對齊
28
29 typedef struct _Y
30 {
31 int a;
32 int b;
33 char c;
34 char content[0];
35 } Y;
36
37
38 int main()
39 {
40 //驗證0長度數組   
41 char c0 = 'a', c1 = 'b', c2='c', c3='d';
42 printf("c0=%c, c1=%c, c2=%c, c3=%c\n&c0=%p, &c1=%p, &c2=%p, &c3=%p\n", c0, c1, c2, c3, &c0, &c1, &c2, &c3);                          
43 struct Test t;
44 t.len = 0x01020304;
45 char *q = t.content;
46 printf("sizeof(t)=%u, sizeof(t.content)=%u\n", sizeof(t),sizeof(t.content));    //列印4 0, content本身不佔空間
47 printf("&t=%p, &t.len=%p, t.content=%p, &t.content=%p\n", &t, &t.len, t.content, &t.content);//
48 strcpy(t.content, "123");
49 //發現 c0 c1 c2 c3的位置的內容被p->content所修改
50 printf("c0=%c, c1=%c, c2=%c, c3=%c\n&c0=%p, &c1=%p, &c2=%p, &c3=%p\n", c0, c1, c2, c3, &c0, &c1, &c2, &c3);
51
52 char buf[1024] = {0};
53 struct Test *p = (struct Test *)buf;
54     p->len = 0x01020304;
55 strcpy( p->content, "abcd");
56 printf("\np=&buf=%p, p->content=%p, p->content=%s\n", buf, p->content, p->content);
57 int k;
58 for(k=0; k<10; ++k)    //注意觀察這十個位置的值
59 printf("address %p: buf[%d]=%d\n", buf+k, k, buf[k]);
60
61 //關於offsetof宏,以及 __attribute__((packed))屬性
62 printf("\nsizeof(S1)=%u, offsetof(S1,c)=%u\n", sizeof(S1),offsetof(S1,c));
63 //使用__attribute__後,結構體大小和成員的位移量都發生變化
64 printf("sizeof(S2)=%u, offsetof(S2,c)=%u\n", sizeof(S2),offsetof(S2,c));
65 //對於有padding(補齊)的結構體Y,其sizeof(Y)和offsetof(Y, content)的大小不一致,參考這個文章
66 printf("sizeof(Y)=%u, offsetof(Y, content)=%u, offsetof(Y, c)=%u\n", sizeof(Y), offsetof(Y, content), offsetof(Y, c));
67
68 getchar();
69 return 0;
70 }

    運行結果如下:

二、__attribute__

    1. __attribute__ ((packed)) 的作用就是告訴編譯器取消結構在編譯過程中的最佳化對齊,按照實際佔用位元組數進行對齊,是GCC特有的文法。這個功能是跟作業系統沒關係,跟編譯器有關,gcc編譯器不是緊湊模式的,我在windows下,用vc的編譯器也不是緊湊的,用tc的編譯器就是緊湊的。例如:

    在GCC下:struct my{ char ch; int a;} sizeof(int)=4;sizeof(my)=8;(非緊湊模式)
    在GCC下:struct my{ char ch; int a;}__attrubte__ ((packed)) sizeof(int)=4;sizeof(my)=5

    2. __attribute__關鍵字主要是用來在函數或資料聲明中設定其屬性。給函數賦給屬性的主要目的在於讓編譯器進行最佳化。函式宣告中的 __attribute__((noreturn)),就是告訴編譯器這個函數不會返回給調用者,以便編譯器在最佳化時去掉不必要的函數傳回碼。

    GNU C的一大特色就是__attribute__機制。__attribute__可以設定函數屬性(Function Attribute)、變數屬性(Variable Attribute)和類型屬性(Type Attribute)。

    __attribute__書寫特徵是:__attribute__前後都有兩個底線,並且後面會緊跟一對括弧,括弧裡面是相應的__attribute__參數。

    __attribute__文法格式為:

    __attribute__ ((attribute-list))

    其位置約束:放於聲明的尾部“;”之前。

    函數屬性(Function Attribute):函數屬性可以協助開發人員把一些特性添加到函式宣告中,從而可以使編譯器在錯誤檢查方面的功能更強大。__attribute__機制也很容易同非GNU應用程式做到相容之功效。

    GNU CC需要使用 –Wall編譯器來擊活該功能,這是控制警告資訊的一個很好的方式。

    packed屬性:使用該屬性可以使得變數或者結構體成員使用最小的對齊,即對變數是一位元組對齊,對域(field)是位對齊。
    舉例:
        /* __attribute__ ((packed)) 的位置約束是放於聲明的尾部“;”之前 */
        struct str_struct{
            __u8    a;
            __u8    b;
            __u8    c;
            __u16   d;
        } __attribute__ ((packed));

        /* 當用到typedef時,要特別注意__attribute__ ((packed))放置的位置,相當於:
         * typedef struct str_stuct str;
         * 而struct str_struct 就是上面的那個結構。
        */
        typedef struct {
            __u8    a;
            __u8    b;
            __u8    c;
            __u16   d;
        } __attribute__ ((packed)) str;

        /* 在下面這個typedef結構中,__attribute__ ((packed))放在結構名str_temp之後,其作用是被忽略的,注意與結構str的區別。*/
        typedef struct {
            __u8    a;
            __u8    b;
            __u8    c;
            __u16   d;
        }str_temp __attribute__ ((packed));    //這樣不起作用,還可能編譯不通過

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.