1. 程式人生 > >C語言自定義型別解析----結構體

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;
}

注意我在其中的註釋:一個結構體的對齊數為它內部的最大對齊數

四:結構體傳參

結構體傳參切記一點:用結構體指標傳參
如果一個結構體過大,用結構體傳參的話將會拷貝一份一模一樣的結構體,會造成資源浪費,更重要的是時間效率嚴重下降
所以遇到結構體傳參毫不猶豫選擇結構體指標傳參