目錄
引入結構體?
?結構的聲明
創建和初始化
?內部元素的使用;
特殊聲明:
結構體在內存中的對齊
練習:?
引入結構體?
? ? ? ? C語言有各種數據類型,我們已經對一些數據類型很熟悉:
整型(int)- 存儲整數值,包括有符號和無符號兩種類型。
浮點型(float、double、long double)- 存儲實數值,包括單精度(float)、雙精度(double)和長雙精度(long double)三種類型。
字符型(char)- 存儲單個字符,包括有符號和無符號兩種類型。
指針類型(pointer)- 存儲內存地址,可以指向其他數據類型。
? ? ? ? ?但是,僅僅有這些類型是無法描述一些對象的特征的,比如:一個人的多個信息,一個家庭的多個成員等等,那么這時候,就要用到結構體類型來描述一個比較復雜的對象。
? ? ? ? 結構體類型就像其他簡單數據類型(int,float,double)一樣,也是一種數據類型,但是它比較復雜,可以包含多個 簡單的數據類型, 這些被包含的數據類型可以是不同的類型。
?
?????????總之,結構體是為了描述復雜對象而引入的一種新的數據類型。
?結構的聲明
????????C語言中,結構體(struct)是一種自定義的數據類型,可以將不同的數據類型組合在一起形成一個?新的?數據類型。結構體的定義格式如下:
struct 結構體名稱
{數據類型1 成員變量1;數據類型2 成員變量2;...
} 創建的同類型結構體名稱;
?????????有了結構體類型,我們就可以描述復雜對象的特征。例如,要描述一個家庭的成員的信息,可以:
? ? ? ? 創建結構體類型Person——包含變量字符串型name, 整型age, 雙精度浮點型heignt;
? ? ? ? 創建結構體類型Family——包含結構體類型Person, 字符串型location;
?代碼如下:
?//聲明Person結構體類型struct Person {char name[20];int age;double height;
};//聲明Family結構體類型struct Family
{struct Person;char location[10];
}Family_of_my;?
從上述代碼可以得出:
????????結構體名稱 根據具體的實際意義來決定。
????????成員變量可以有多個,每個成員變量的數據類型可以是任意數據類型,包括簡單數據類型和復雜數據類型。也就是說,一個結構體內部可以再嵌套包含一個結構體類型。
?
? ? ? ? 總結,結構體內部的數據類型多樣,可以是簡單類型,也可以是復雜類型。
?
創建和初始化
? ? ? ? 結構體的初始化可以按照不同方式進行:
? ? ? ? 1.按照結構體內部元素順序
? ? ? ? 2.按照指定順序
#include <stdio.h>
struct Stu
{char name[20];//名字int age;//年齡char sex[5];//性別char id[20];//學號};int main()
{//按照結構體成員的順序初始化struct Stu s = { "張三", 20, "男", "20230818001" };printf("name: %s\n", s.name);printf("age : %d\n", s.age);printf("sex : %s\n", s.sex);printf("id : %s\n", s.id);//按照指定的順序初始化struct Stu s2 = { .age = 18, .name = "lisi", .id = "20230818002", .sex = "?printf("name: %s\n", s2.name);printf("age : %d\n", s2.age);printf("sex : %s\n", s2.sex);printf("id : %s\n", s2.id);return 0;}
? ? ? ? 總結,?初始化可以按照正常順序,還可按照指定順序。
?內部元素的使用;
????????要用到結構體操作符。?
?
?
#include <stdio.h>
struct Stu
{char name[20];//名字int age;//年齡char sex[5];//性別char id[20];//學號};int main()
{//按照結構體成員的順序初始化struct Stu s = { "張三", 20, "男", "20230818001" };printf("name: %s\n", s.name);printf("age : %d\n", s.age);printf("sex : %s\n", s.sex);printf("id : %s\n", s.id);//按照指定的順序初始化struct Stu* s2 = &s;printf("name: %s\n", s2->name);printf("age : %d\n", s2->age);printf("sex : %s\n", s2->sex);printf("id : %s\n", s2->id);return 0;}??
?
? ? ? ? ?總結,要使用結構體內部元素,要用到結構體操作符:
.? 和 ->
用例:
結構體.成員名
結構體指針->成員名
特殊聲明:
????????在聲明結構的時候,可以不完全的聲明。也就是,可以省略結構體的標簽。
例如:
//匿名結構體類型
struct
{
int a;
char b;
float c;
}x;struct
{
int a;
char b;
float c;
}a[20], *p;
? ? ? ? 但是,這兩個匿名的類型完全一致的結構體是同一個結構體類型嗎?
也就是說:
p = &x;
????????這段代碼合法嗎?
? ? ? ? 經過測試,編譯器會把上面的兩個聲明當成完全不同的兩個類型,所以是?法的。
匿名的結構體類型,如果沒有對結構體類型重命名的話,基本上只能使??次。
?
?
結構體在內存中的對齊
? ? ? ? 現在,我們已經對結構體有了一個基本的認識,那么,結構體的大小是如何計算的呢?
?
? ? ? ? 我們先看一個實例:
計算下列兩個結構體的大小:
//練習1
struct S1
{
char c1;
int i;
char c2;
};
printf("%d\n", sizeof(struct S1));//練習2
struct S2
{
char c1;
char c2;
int i;
};
printf("%d\n", sizeof(struct S2));
? ? ? ? 我們可以先猜測:
????????既然兩個結構體內部的變量類型和數目完全一致,那么我們有理由猜測兩個結構體大小相同,但是,實際情況并不是這樣:
?
? ? ? ? 其實,結構體在內存中的存儲有一套自己的規則:
?首先,引入一個概念:
????????對齊數
????????對齊數=編譯器默認的?個對齊數? ?和 該成員變量長度的較小值
????????默認對齊數 :
VS 中默認的值為 8?
Linux中gcc沒有默認對?數,對?數就是成員??的??
結構體的基本對齊方法是:
????????1.結構體的第?個成員對?到和結構體變量起始位置偏移量為0的地址處
????????2.其他成員變量要對齊到對齊數的整數倍的地址處
結構體大小的計算:
????????1.結構體總??為最?對?數(結構體中每個成員變量都有?個對?數,所有對?數中最?的)的整數倍。
????????2.如果嵌套了結構體的情況,嵌套的結構體成員對?到??的成員中最?對?數的整數倍處,結構體的整體??就是所有最?對?數(含嵌套結構體中成員的對?數)的整數倍。
?
? ? ? ? 這樣一來,我們分析上兩個問題:
?
? ? ? ? 對于s1,先放入char類型,由于char類型是1個字節,小于8(VS默認值),所以直接放在0地址處。
? ? ? ? 值得一提,長度是1的char無論何時都不會浪費空間,它會直接頂著上一個數據的存儲空間放置。
? ? ? ? int型,大小4字節,小于8(VS默認值),所以放在sizeof(int)的整數倍的地址處,也就是4開始,到7結束。
? ? ? ? char 緊跟著int型;
????????由于最大對齊數是4,此時結構體的最小大小大于8,要滿足4的整數倍,那么這個結構體的大小向上取 是12。
? ? ? ? 對于s2?:
? ? ? ? 放入兩個char,一個int后的結構體最小大小正好是8,那么結構體總大小就是8。
?
?
練習:?
int main(int argc, char* argv[])
{struct tagTest1{short a;char d; long b; long c; };struct tagTest2{long b; short c;char d;long a; };struct tagTest3{short c;long b;char d; long a; };struct tagTest1 stT1;struct tagTest2 stT2;struct tagTest3 stT3;printf("%d %d %d", sizeof(stT1), sizeof(stT2), sizeof(stT3));return 0;
? ? ? ? 結果:?
?完~
未經作者同意禁止轉載