目錄
一、是什么
二、三種結構體宣告
①普通的宣告
②自參考
③特殊的宣告——匿名的結構體
三、三種初始化方法
四、三種解參考方法
五、結構體記憶體對齊(重點)
六、記憶體對齊的意義
七、修改默認對齊值
八、獲取偏移值
九、結構體傳參
一、是什么
結構體是一種自定義型別,用來存放多種型別的資料,使我們得以輕松描述諸如人、書本這些較為復雜的物件,
二、三種結構體宣告
①普通的宣告
struct book
{
int date;
float price;
char name[10];
char*ps;
struct content c;
}book1;
typedef struct book
{
int date;
float price;
char name[10];
char*ps;
struct content c;
}book;
1.struct+后面的tab(標簽)組成我們創建的變數名,里面可以放入任何型別的變數,甚至是指標和另一個結構體,圖一中book1是我們在定義時順手定義的一個型別為struct book的變數
2.圖二我們將struct book這個型別名重命名為book,這里并沒有定義一個變數,注意區分,
②自參考
struct book
{
int date;
struct book b;
}book1;
但注意結構體不能“呼叫”自己 ,如上面的例子,我們可以類比遞回函式的形式,圖中無疑是一個“死回圈”,根本無法計算他的大小,當然編譯器的語法也不通過,要想實作結構體的自參考,我們可以傳入一個指標,實作鏈表一個指向下一個的效果,如下圖:
struct Node
{
int data;
struct Node*pn;
};
③特殊的宣告——匿名的結構體
有時候在使用結構體的時候我們直接省去了tag,如下圖,在一定程度上簡化了我們的代碼
struct content
{
char name[10];
char id[20];
int age;
struct
{
int arr[10];
};
}s1,s2,s3;
雖然匿名結構體沒有名字,但編譯器也是堅決把他們看成兩個不同的型別,如下圖,雖然*px指向的型別看似和x一樣,但編譯器堅決認為兩者是不同的,所以px=&x是錯誤的書寫,
struct
{
int price;
}x;
struct
{
int price;
}y,*px;
三、三種初始化方法
//宣告結構體
struct content
{
int page;
char* character;
};
typedef struct book
{
float price;
char name[20];
struct content c;
}b;
typedef struct book1
{
float price;
char* name;
struct content c;
}b1;
int main()
{
//方法一:定義時賦值
b book1 = { 10.0, "c++", {100,"bit"} };
//方法二:先定義后賦值
b1 book2 = {0};
book2.price = 10.0;
book2.name = "c++";
book2.c.page = 100;
book2.c.character = "bit";
//定義時亂序賦值
b1 book3 = {
.name = "c++",
.c.page = 100,
.c.character = "bit",
.price = 10.0
};
}
值得注意的是我們不能用陣列來接收例如“c++”,因為常量字串實際傳入的是首元素的地址,所以我們用指標接收,
四、三種解參考方法
struct book
{
int price;
char name[10];
};
int main()
{
struct book b = { 10, "hello" };
struct book*ps = &b;
//方法一:->
printf("%d\n",ps->price);
//方法二:.
printf("%d\n",b.price);
//方法三:本質等同方法二
printf("%d\n",(*ps).price);
return 0;
}
五、結構體記憶體對齊(重點)
1.第一個成員在結構體變數偏移量為0的地址處,
2. 其他成員變數要對齊到某個數字(對齊數)的整數倍的地址處,
對齊數 = 編譯器默認的一個對齊數 與 該成員大小的較小值,
VS中默認的值為8
Linux中的默認值為4gcc下沒有默認值
3. 結構體總大小為最大對齊數(每個成員變數都有一個對齊數)的整數倍,
4. 如果嵌套了結構體的情況,嵌套的結構體對齊到自己的最大對齊數的整數倍處,結構體的 整體大小就是所有最大對齊數(含嵌套結構體的對齊數)的整數倍,
以下面的例子分析規則一二三,(結果分別為8,12)
struct s1
{
char a;
char b;
int c;
};
struct s2
{
char a;
int b;
char c;
};
int main()
{
struct s1 s1 = { 0 };
struct s2 s2 = { 0 };
printf("%d\n",sizeof(s1));
printf("%d",sizeof(s2));
}
先來分析s2吧
①a作為第一個成員,創建在結構體偏移量為0的地址,即結構體的起始地址,占用記憶體一位元組,

②b作為第二個成員要考慮偏移量的問題,b的型別int 的大小4<8,所以對齊數為4,對齊在最后未被利用的空間里找一塊地址,其偏移量為對齊數的整數倍,

③c作為第三個成員,c的型別為1,偏移量8當然是1的倍數啦,所以c隨即在b的后方開辟空間,但由于規則三,現在的總大小為9,并不是最大對齊數(每個成員的對齊數之間)4的倍數,所以又在后面浪費了3個位元組使得總位元組為12,

同樣的道理分析s1,我們發現是s1的大小是8位元組,同樣的內容卻占用了不同大的空間?我們由此得出以下結論:
為了減小整體所占空間,應該使占用空間小的成員集中在一起
再分析一個內嵌結構體的例子吧!
struct s1
{
char a;
char b;
int c;
};
struct s2
{
char a;
char b;
struct s1 c;
};
int main()
{
struct s1 s1 = { 0 };
struct s2 s2 = { 0 };
printf("%d\n",sizeof(s1));
printf("%d",sizeof(s2));
}
相同的分析不再贅述,分析一下內嵌結構體,內嵌結構體的最大對齊數是4,整個結構體的最大對齊數也是4,由此我們畫出以下的記憶體示意圖,

內嵌結構體第一個成員進行記憶體對齊(對齊數為內嵌結構體內對齊數的最大值),之后的照123規則對齊,
六、記憶體對齊的意義
由于官方沒有給出明確的規定,我們來學習以下兩個比較有說服力的理由
1. 平臺原因(移植原因): 不是所有的硬體平臺都能訪問任意地址上的任意資料的;某些硬體平臺只能在某些地址處取某些特定型別的資料,否則拋出硬體例外,
2. 性能原因: 資料結構(尤其是堆疊)應該盡可能地在自然邊界上對齊, 原因在于,為了訪問未對齊的記憶體,處理器需要作兩次記憶體訪問;而對齊的記憶體訪問僅需要一次訪問,
想必原因一通俗易懂,我們來解釋一下原因二,
我們知道我們的電腦為32/64根地址線,換算成位元組就是4/8位元組,以4個位元組進行說明,記憶體每四個位元組四個位元組進行訪問時,若結構體的記憶體對齊為四個位元組,則記憶體可以順暢的一次性訪問,若沒有對齊,為了完整訪問,需要兩次,這就降低了效率

總的來說,就是用空間來換取效率
七、修改默認對齊值
我們可以采用預處理指令pragma來修改,如以下的案例:列印結果先后為5 8,
#pragma pack(1)//將默認對齊數改為1;
struct book1
{
char c;
int a;
};
#pragma pack()//將默認對齊數改為默認值(8)
struct book2
{
char c;
int a;
};
int main()
{
struct book1 b1 = { 0 };
struct book2 b2 = { 0 };
printf("%d\n",sizeof(b1));
printf("%d\n", sizeof(b2));
}
八、獲取偏移值
我們可以采用宏“offsetof”實作,頭檔案為<stddef.h>,
#pragma pack(1)//將默認對齊數改為1;
struct book1
{
char c;
int a;
};
#pragma pack()//將默認對齊數改為默認值(8)
int main()
{
struct book1 b1 = { 0 };
printf("%d\n",offsetof(struct book1,c));
printf("%d\n",offsetof(struct book1,a));
}
我們也可以用offsetof來驗證修改默認對齊值的情況,
九、結構體傳參
結構體傳參有傳址操作和傳參操作之分,但當我們在選擇時,如果需要修改結構體,必須選擇傳址操作;若不用修改,也應該盡可能選擇傳址操作,
原因一:傳址才可以改變結構體的內容,
原因二:傳參需要壓堆疊,會帶來記憶體和效率上的損耗,若結構體過大,會導致程式性能下降
若用傳址的方法,但不想結構體被改變,可以采用const修飾,
以下用一個簡單的函式分別演示傳址和傳參操作,
struct book
{
int page;
float price;
};
void print1(struct book b1)
{
printf("%d\n",b1.page);
}
void print2(const struct book*ps)
{
printf("%d\n", ps->page);
}
int main()
{
struct book b1 = {100,15.0};
print1(b1);
struct book*ps = &b1;
print2(ps);
return 0;
}
很厲害嘛,堅持看到這里,希望對你有所幫助!
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/300284.html
標籤:其他
上一篇:(DFS)深度優先搜索演算法詳解
下一篇:破后而立-linux基礎知識點
