C語言中結構體在函數中的應用

1、結構體與函數參數
結構體做函數參數可分爲傳值與傳指針。
1.傳值時結構體參數會被拷貝一份,在函數體內修改結構體參數成員的值其實是修改調用參數的一個臨時拷貝的成員的值,這不會影響到調用參數。在這種狀況下,因爲涉及到結構體參數的拷貝,程序空間及時間效率都會受到影響,因此這種方法基本不用。
例如:
typedef struct tagSTUDENT{
char name[20];
int age;
}STUDENT;

void fun(STUDENT stu)
{
printf(「stu.name=%s,stu.age=%d/n」,stu.name,stu.age);
}



2.傳指針時直接將結構體的首地址傳遞給函數體,在函數體中經過指針引用結構體成員,能夠對結構體參數成員的值形成實際影響。這種用法效率高,常常採用。
例如:
typedef struct tagSTUDENT{
char name[20];
int age;
}STUDENT;

void fun(STUDENT* pStu)
{
printf(「pStu->name=%s,pStu->age=%d/n」,pStu->name,pStu->age);
}

2、結構體與函數返回值
對於某些版本的C語言編譯器,返回值僅能爲基本數據類型如int、char以及指針,所以結構體做爲一種組合數據類型,不能以值的方式返回,而在有些版本的C編譯器中又能夠直接返回結構體變量 ,在C++中也是能夠直接返回結構體變量的。

直接返回結構體變量示例以下;
typedef struct tagSTUDENT{
char name[20];
int age;
}STUDENT;

STUDENT fun();
int _tmain(int argc, _TCHAR* argv[])
{
STUDENT p=fun();
printf("p.name=%s",p.name);
return 0;
}

STUDENT fun()
{
STUDENT stu;
stu.age=18;
strcpy(stu.name,"xiaoming");
return stu;
}

以指針方式返回結構體示例以下:
typedef struct tagSTUDENT{
char name[20];
int age;
}STUDENT;


STUDENT* fun()
{
STUDENT* p=malloc(sizeof(STUDENT));
p->age=18;
strcpy(p->name,"xiaoming");
return p;
}
關於結構體,看內核又遇到了,關於賦值中存在·的奇怪用法,在網上沒有找到,卻把之前一直弄的比較模糊的對齊問題給翻出來了。以下爲轉發內容:
有人給對齊原則作過總結,具體在哪裏看到如今已記不起來,這裏引用一下前人的經驗(在沒有#pragma pack宏的狀況下):
原則一、數據成員對齊規則:結構(struct或聯合union)的數據成員,第一個數據成員放在offset爲0的地方,之後每一個數據成員存儲的起始位置要從該成員大小的整數倍開始(好比int在32位機爲4字節,則要從4的整數倍地址開始存儲)。
原則二、結構體做爲成員:若是一個結構裏有某些結構體成員,則結構體成員要從其內部最大元素大小的整數倍地址開始存儲。(struct a裏存有struct b,b裏有char,int,double等元素,那b應該從8的整數倍開始存儲。)
原則三、收尾工做:結構體的總大小,也就是sizeof的結果,必須是其內部最大成員的整數倍,不足的要補齊。

這三個原則具體怎樣理解呢?咱們看下面幾個例子,經過實例來加深理解。
例1:struct{
              short a1;
              short a2;
              short a3;
             }A;

        struct{
            long a1;
            short a2;
           }B;
sizeof(A) = 6; 這個很好理解,三個short都爲2。
sizeof(B) = 8; 這個比是否是比預想的大2個字節?long爲4,short爲2,整個爲8,由於原則3。
例2:struct A{
             int a;
             char b;
             short c;
             };

        struct B{
            char b;
            int a;
            short c;
             };
sizeof(A) = 8; int爲4,char爲1,short爲2,這裏用到了原則1和原則3。
sizeof(B) = 12; 是否超出預想範圍?char爲1,int爲4,short爲2,怎麼會是12?仍是原則1和原則3。
深究一下,爲何是這樣,咱們能夠看看內存裏的佈局狀況。
                            a         b         c
A的內存佈局:1111,     1*,       11
                            b          a        c
B的內存佈局:1***,     1111,   11**
其中星號*表示填充的字節。A中,b後面爲什麼要補充一個字節?由於c爲short,其起始位置要爲2的倍數,就是原則1。c的後面沒有補充,由於b和c正好佔用4個字節,整個A佔用空間爲4的倍數,也就是最大成員int類型的倍數,因此不用補充。
B中,b是char爲1,b後面補充了3個字節,由於a是int爲4,根據原則1,起始位置要爲4的倍數,因此b後面要補充3個字節。c後面補充兩個字節,根據原則3,整個B佔用空間要爲4的倍數,c後面不補充,整個B的空間爲10,不符,因此要補充2個字節。
再看一個結構中含有結構成員的例子:
例3:struct A{
              int a;
              double b;
              float c;
             };
         struct B{
              char e[2];
              int f;
              double g;
              short h;
              struct A i;
             };
sizeof(A) = 24; 這個比較好理解,int爲4,double爲8,float爲4,總長爲8的倍數,補齊,因此整個A爲24。
sizeof(B) = 48; 看看B的內存佈局。
                          e         f             g                h                                    i
B的內存佈局:11* *,   1111,   11111111, 11 * * * * * *,        1111* * * *, 11111111, 1111 * * * *
i其實就是A的內存佈局。i的起始位置要爲24的倍數,因此h後面要補齊。把B的內存佈局弄清楚,有關結構體的對齊方式基本就算掌握了。

以上講的都是沒有#pragma pack宏的狀況,若是有#pragma pack宏,對齊方式按照宏的定義來。好比上面的結構體前加#pragma pack(1),內存的佈局就會徹底改變。sizeof(A) = 16; sizeof(B) = 32;
有了#pragma pack(1),內存不會再遵循原則1和原則3了,按1字節對齊。沒錯,這不是理想中的沒有內存對齊的世界嗎。
                           a                b             c
A的內存佈局:1111,     11111111,   1111
                          e        f             g          h                     i
B的內存佈局:11,   1111,   11111111, 11 ,            1111, 11111111, 1111
那#pragma pack(2)的結果又是多少呢?#pragma pack(4)呢?留給你們本身思考吧,相信沒有問題。



還有一種常見的狀況,結構體中含位域字段。位域成員不能單獨被取sizeof值。C99規定int、unsigned int和bool能夠做爲位域類型,但編譯器幾乎都對此做了擴展,容許其它類型類型的存在。
使用位域的主要目的是壓縮存儲,其大體規則爲:
1) 若是相鄰位域字段的類型相同,且其位寬之和小於類型的sizeof大小,則後面的字段將緊鄰前一個字段存儲,直到不能容納爲止;
2) 若是相鄰位域字段的類型相同,但其位寬之和大於類型的sizeof大小,則後面的字段將重新的存儲單元開始,其偏移量爲其類型大小的整數倍;
3) 若是相鄰的位域字段的類型不一樣,則各編譯器的具體實現有差別,VC6採起不壓縮方式,Dev-C++採起壓縮方式;
4) 若是位域字段之間穿插着非位域字段,則不進行壓縮;
5) 整個結構體的總大小爲最寬基本類型成員大小的整數倍。
 仍是讓咱們來看看例子。
例4:struct A{
               char f1 : 3;
              char f2 : 4;
              char f3 : 5;
              };
                          a         b             c
A的內存佈局:111,    1111 *,   11111 * * *
位域類型爲char,第1個字節僅能容納下f1和f2,因此f2被壓縮到第1個字節中,而f3只能從下一個字節開始。所以sizeof(A)的結果爲2。
例5:struct B{
             char f1 : 3;
             short f2 : 4;
             char f3 : 5;
             };
因爲相鄰位域類型不一樣,在VC6中其sizeof爲6,在Dev-C++中爲2。
例6:struct C{
              char f1 : 3;
              char f2;
             char f3 : 5;
             };
非位域字段穿插在其中,不會產生壓縮,在VC6和Dev-C++中獲得的大小均爲3。

考慮一個問題,爲何要設計內存對齊的處理方式呢?若是體系結構是不對齊的,成員將會一個挨一個存儲,顯然對齊更浪費了空間。那麼爲何要使用對齊呢?體系結構的對齊和不對齊,是在時間和空間上的一個權衡。對齊節省了時間。假設一個體繫結構的字長爲w,那麼它同時就假設了在這種體系結構上對寬度爲w的數據的處理最頻繁也是最重要的。它的設計也是從優先提升對w位數據操做的效率來考慮的。有興趣的能夠google一下,人家就能夠跟你解釋的,一大堆的道理。
最後順便提一點,在設計結構體的時候,通常會尊照一個習慣,就是把佔用空間小的類型排在前面,佔用空間大的類型排在後面,這樣能夠相對節約一些對齊空間函數

相關文章
相關標籤/搜索