C語言自定義型別解析----結構體
C語言當中有一部分型別是自定義型別,比如結構體、陣列、列舉、聯合,其實指標也是自定義型別,我們可以定義各種各樣型別的指標,這在我之前的文章中有指標的解析。本文著重於講解結構體型別,以及它的一個重要特徵------記憶體對齊
一.結構體的宣告
下面是一個描述學生的結構體宣告:
struct Stu
{
char name[20];
int age;
char sex[5];
char id[20]; //學號
};
可以用typedef來聲名結構體(更加方便):
typedef struct Stu //這裡的Stu完全可以省去,省去後更加簡潔 { char name[20]; int age; char sex[5]; }Stu; int main() { Stu student; //這裡就不用再 :struct Stu student; return 0; }
記住:
1. 末尾務必要加分號;
2.Stu為結構體型別名稱,而不是結構體變數名;
3.Stu可以省略,同時宣告時就定義的結構體叫匿名結構體;但是不建議這樣做,意義不大;
4.可以在聲名結構體的時候就定義一個結構體變數,但是也不建議這樣做,因為結構體聲名是在main函式之外,這樣在這裡定義的結構體變數將會是一個全域性變數,我們應該儘量避免使用全域性變數;
注意1:兩個結構體型別即使成員一模一樣,它們也是兩個截然不同的結構體,例如下面程式碼:
#include<stdio.h> struct { char name[20]; int age; char sex[5]; }x; struct { char name[20]; int age; char sex[5]; }a[10], *p; int main() { p = &x; return 0; }
這裡會有警告:“=”: 從“*”到“*”的型別不相容
從而說明上面兩個結構體雖然成員一模一樣,但這兩個結構體並不等同;
注意2:結構體的自引用
在結構體中包含一個型別為該結構本身的成員能不能行?
答案是:不可行。這樣會形成死遞迴;
正確引用方式為引用一個結構體指標;
二.結構體變數的定義和初始化
上面其實就介紹到了結構體變數的定義了,我們要區分開來結構體型別名和結構體變數名。
下面來簡單說說結構體的初始化,下面直接上一段示例程式碼:
typedef struct { char name[20]; int age; char sex[5]; }Stu; int main() { Stu student = {"wangmazi", 10, 'man'}; printf("%s", student.name); return 0; }
結構體初始化還有其他幾種方法,都大同小異,就不再一一列舉了。
三.結構體記憶體對齊(計算結構體的大小)
一:為什麼存在記憶體對齊:
下面呈上一些收集來的官方語言:
1.平臺原因(移植原因):不是所有的硬體平臺都能任意訪問任意地址上的任意資料的;某些硬體平臺只能在某些地址處取某些特定型別的資料,否則丟擲硬體異常;
2.效能原因: 資料結構(尤其是棧)應該儘可能地在自然邊界上對齊。 原因在於,為了訪問未對齊的記憶體,處理 器需要作兩次記憶體訪問;而對齊的記憶體訪問僅需要一次訪問。
總結出結構體記憶體對齊的最大意義:這是拿空間來換取效率(時間)的做法;
這樣做究竟值不值呢?毋庸置疑,肯定值得的;
二:如何計算結構體大小(記憶體對齊規則)
我們要計算一個結構體大小就是它的每個成員大小相加的總和嗎?
當然不是,這裡就必須要先掌握結構體記憶體對齊的規則:
1. 第一個成員在與結構體變數偏移量為0的地址處。
2. 其他成員變數要對齊到某個數字(對齊數)的整數倍的地址處。 對齊數 = 編譯器預設的一個對齊數 與 該成員大小的較小值。 (VS中預設的值為8 ,Linux中的預設值為4)
3. 結構體總大小為最大對齊數(每個成員變數都有一個對齊數)的整數倍。
注意:(可用 #pragma pack(NUM) 語句來將預設對其數修改為NUM )
下面舉一個計算結構體大小的例子:
#include<stdio.h>
typedef struct
{
double d;
char c;
int i;
}S1;
typedef struct
{
char c1;
S1 s1; //一個結構體的對齊數為它內部的最大對齊數
double d;
}S2;
int main()
{
printf("%d\n", sizeof(S1)); //16
printf("%d\n", sizeof(S2)); //32
return 0;
}
注意我在其中的註釋:一個結構體的對齊數為它內部的最大對齊數
四:結構體傳參
結構體傳參切記一點:用結構體指標傳參
如果一個結構體過大,用結構體傳參的話將會拷貝一份一模一樣的結構體,會造成資源浪費,更重要的是時間效率嚴重下降
所以遇到結構體傳參毫不猶豫選擇結構體指標傳參。