結構體
????????結構體的定義與聲明
? ? ? ? ? ? ? ? 結構體其實和數組一樣,都是一些值的集合,只不過數組是一系類相同類型的值,而結構體里邊的成員可以是不同的數據類型。
? ? ? ? ? ? ? ? 關于它的聲明,所用到的關鍵字是struct。
? ? ? ? ? ?聲明的語法如下:
? ? ? ? ? ?struct 結構體名
? ? ? ? ? {
????????????????成員變量列表;
? ? ? ? ? ?}變量名;
????????????????我先舉個例子,就比如一個學生有名字、年齡、成績等信息。那么此時就可以用結構體來描述它。
struct Stu
{char name[20];//名字int age;double score;
};
????????結構體變量的定義和初始化及其訪問
????????????????定義
????????????????在剛才創建完成了結構體之后,我們就可以創建結構體的變量了。
? ? ? ? ? ? ? ? 一共有三種方式:下面以代碼加注釋的方式給出。
struct Stu
{char name[20];//名字int age;double score;
}stu1;//在定義結構體的時候就創建的全局變量struct Stu stu2;//創建的全局結構體變量int main()
{struct Stu stu3;//創建的局部結構體變量return 0;
}
????????????????初始化
? ? ? ? ? ? ?剛才上邊的代碼已經創建了幾個Stu類型的結構體變量,現在我給結構體變量stu3初始化,代碼如下
? ? ? ? ? ? ?//
? ? ? ? ? ? ?stu3 = {"xxc",18,99.0};//注意,我們給它初始化的時候需要按照順序,如果你想要按照自己的順序去初始化,那就要用到結構體成員訪問操作符?( . )
? ? ? ? ? ? ?stu3 = {.age = 20 , .name = "zhangsan" , .score = 98.5};
????????????????訪問結構體變量
????????????????訪問結構體的成員變量有兩種方式,第一種比較的直接,我們用結構體變量.成員名的方式去訪問結構體里的成員。第二種方式就要用到指針了,通過結構體指針->成員名的方式。代碼如下:
#include<stdio.h>struct Stu
{char name[20];int age;float score;
};int main()
{//第一種方式打印struct Stu stu = { "zhangsan",21,98.4f };printf("%d %f\n", stu.age, stu.score);//第二種方式打印,先得到結構體的地址,再用指針的方式去打印struct Stu* p = &stu;printf("%d %f\n", p->age, p->score);return 0;
}
????????嵌套結構體與匿名結構體類型
????????????????嵌套結構體:
? ? ? ? ? ? ? ? 嵌套結構體,顧名思義就是結構體充當了結構體的成員變量,請看下邊的代碼(同時,我會將訪問其成員變量也附加進去):
//注意:以下的代碼只是演示,沒有什么實際的含義
#include<stdio.h>
struct id
{char ID[25];int x;
};struct Stu
{char name[20];struct id identity;
};int main()
{struct Stu stu1 = { "zhangsan",{"123456",2} };return 0;
}
????????????????匿名結構體:
????????????????匿名結構體就是結構體在創建的時候沒給它名字,如下
struct
{
char name[20];
int age;
};? ? ? ? ? ?這樣的話就不能創建結構體的變量了,只能用三種創建方法的第一種來創建,就是在定義的時候直接創建。并且你會發現當你取出他的地址賦值給一個匿名結構體類型的指針變量的時候是會失敗的。因為C標準認為它們是兩種不同的類型。所以往往匿名結構體類型只能用一次。
????????結構體的內存對齊
? ? ? ? ? ? ? ? 每一種數據只要存在在內存當中就肯定有大小,結構體也不例外,接下來所要討論的就是結構體的內存大小,C語言給出了一套計算它大小的規則,我們稱之為結構體的內存對齊。
? ? ? ? ? ?對齊規則:
????????????????1.結構體的第一個成員對齊到和結構體變量起始位置偏移量為0的地址處
? ? ? ? ? ? ? ? 2.其他成員變量要對齊到某個數字(對齊數)的整數倍的地址處
? ? ? ? ? ? ? ? 3.結構體的總大小為最大對齊數的整數倍
? ? ? ? ? ? ? ? 接下來我們先來解釋一下幾個名詞的含義:
????????????????偏移量:我們可以借助offsetof宏來幫助我們理解,offsetof可以計算出結構體的成員相較于結構體起始位置的偏移量。offsetof有兩個參數,第一個參數是結構體變量名字,第二個參數是結構體內部的成員名。??
? ? ? ? ? ? ? ? 由上圖我們可以知道每個結構體成員的偏移量是多少,如果你還沒有概念,請不要著急,我會把所有名詞解釋完之后再次回過頭來全部解釋一下。
? ? ? ? ? ? ? ? 對齊數: 在我們常見的編譯器vs里邊,默認的對齊數是8,但在計算結構體大小的時候,我們需要將默認的對齊數與該結構體成員變量的大小做比較,取較小的那個作為我們計算大小時候的依據。
? ? ? ? ? ? ? ? 最大對齊數:結構體里邊的每一個成員變量都有一個對齊數,它們之中最大的那個就叫做最大對齊數。
? ? ? ? ? ? ? ? 了解了以上的規則與概念之后,我們再將上邊截圖里邊的結構體拿出來,來根據規則計算一下它的大小。
????????????????結構體嵌套結構體的內存對齊
????????????????當我們想要計算的結構體里邊有結構體嵌套的時候,它的大小又該如何計算呢?
? ? ? ? ? ? ? ? 這時候,在上邊規則的三條規則之下還有一條規則。
? ? ? ? ? ?4.當存在嵌套結構體的時候,該結構體成員對齊到自己內部成員的最大對齊數的整數倍處,結構體的大小就是全部成員(包括嵌套結構體的成員)的最大對齊數的整數倍。
? ? ? ? ? ? ? ? 下邊來看一個例子:
????????????????修改默認對齊數:
? ? ? ? ? ? ? ? 我們在上邊已經提到過了,在vs里邊的默認對齊數為8,當我們覺得這個默認對齊數不合適的時候,我們就可以用預處理指令#pragma來修改默認的對齊數。
#include<stdio.h>
#pragma pack(1)//設置默認對齊數為1
struct test
{char c1;char c2;
};
#pragma pack()//取消設置的默認對齊數,還原為默認對齊數為8的狀態。
int main()
{struct test test2;printf("%d\n", sizeof(test2));return 0;
}
????????結構體傳參
????????????????函數的傳參分為兩種,一種是傳值,一種是傳址。下邊的代碼將結構體的兩種傳參方式一并演示。
#include<stdio.h>
struct s1
{char a;int b;char c;
};void test1(struct s1 s)
{printf("test1的打印:%d\n", s.b);
}void test2(struct s1* ps)
{printf("test2的打印:%d\n", ps->b);
}int main()
{struct s1 sss;sss.b = 10;test1(sss);test2(&sss);return 0;
}
? ? ? ? ? ? ? ? 這里再補充一個點,利用地址傳參可以很好的提升程序的運行效率,因為如果是傳值調用的話,程序會在內存里又開辟一塊空間來臨時拷貝實參。
????????結構體實現位段
? ? ? ? 首先明確位段在定義時候的兩個注意點
? ? ? ? 1.位段的成員必須是int,unsigned int,signed int,char,在C99中,位段成員的類型還可以是其他類型
? ? ? ? 2.? 位段的成員名后邊有一個冒號和一個數字。
? ? ? ? 如下就是一個基本的位段的定義
struct s
{int a : 2;int b : 4;int c : 6;
};
????????????????位段的內存分配:
? ? ? ? ? ? ? ? 在定義位段的時候,位段的每一個成員的冒號后邊的數字代表的是比特位的意思,也就是在定義位段的成員變量的時候,已經把它所占的空間大小也限定住了。
? ? ? ? ? ? ? ? 位段的空間是按照4個字節或者1個字節的大小來開辟空間的。還有一個值得注意的點就是位段不具有移植性,當需要跨平臺使用的不推薦使用位段。
? ? ? ? ? ? ? ? 接下來用一個例子來說明位段的內存分配:
? ? ? ? ? ? ? ? 由以上的例子可見,位段的作用本質上還是在節約內存空間的消耗。