自定義型別:結構體,列舉,聯合 【二】
- 本節重點
- 結構體
- 結構體記憶體對齊
- 修改默認對齊數
- 結構體傳參
- 結構體實作位段(位段的填充&可移植性)
- 列舉
- 列舉型別的定義及使用
- 列舉的優點
本節重點
結構體
結構體記憶體對齊
引例
#include<stdio.h>
struct S
{
char c;
int i;
char c2;
};
int main()
{
struct S s = { 0 };
printf("%d\n", sizeof(s));
return 0;
}

分析

要點:
- 第一個成員在與結構體變數偏移量為0的地址處,
- 其他成員變數要對齊到某個數字(對齊數)的整數倍的地址處,對齊數 = 編譯器默認的一個對齊數 與 該成員大小的較小值,
- 結構體總大小為最大對齊數(每個成員變數都有一個對齊數)的整數倍,
- 如果嵌套了結構體的情況,嵌套的結構體對齊到自己的最大對齊數的整數倍處,結構體的整體大小就是所有最大對齊數(含嵌套結構體的對齊數)的整數倍,
注:VS默認對齊數為8;linux無對齊數(即該成員的位元組大小為對齊數)
為什么存在記憶體對齊?!
- 平臺原因(移植原因): 不是所有的硬體平臺都能訪問任意地址上的任意資料的;某些硬體平臺只能在某些地址處取某些特定型別的資料,否則拋出硬體例外,
- 性能原因: 資料結構(尤其是堆疊)應該盡可能地在自然邊界上對齊, 原因在于,為了訪問未對齊的記憶體,處理器需要作兩次記憶體訪問;而對齊的記憶體訪問僅需要一次訪問,
注:結構體的記憶體對齊是拿空間來換取時間的做法,
結論:既要滿足對齊,又要節省空間——讓占用空間小的成員盡量集中在一起,
例子


修改默認對齊數
#include<stdio.h>
//默認對齊數是8
//把默認對齊數改為2
#pragma pack(2)
struct S
{
char c1;
int i;
char c2;
};
#pragma pack()
int main()
{
struct S s = { 0 };
printf("%d\n", sizeof(s));
return 0;
}

分析

結構體傳參
struct S
{
int data[1000];
int num;
};
int main()
{
struct S s = { {1,2,3,4},1000 };
//結構體傳參
printf("%d\n", s.num);
//結構體地址傳參
struct S* ps = &s;
printf("%d\n", ps->num);
return 0;
}
首選地址傳參
原因:
函式傳參的時候,引數是需要壓堆疊,會有時間和空間上的系統開銷,
如果傳遞一個結構體物件的時候,結構體過大,引數壓堆疊的的系統開銷比較大,所以會導致性能的下降,
結論: 結構體傳參的時候,要傳結構體的地址
結構體實作位段(位段的填充&可移植性)
什么是位段
1.位段的成員必須是 int、unsigned int 或signed int 或char,
2.位段的成員名后邊有一個冒號和一個數字,
3.位段的宣告和結構是類似的,
struct A
{
int _a : 2;
int _b : 5;
int _c : 10;
int _d : 30;
};
int main()
{
printf("%d\n", sizeof(struct A));
return 0;
}

注::冒號后+數字是占多少個位元位,
分析
struct A
{
//4個位元組-32bit
int _a : 2;//成員占2個bit位
int _b : 5;//成員占5個bit位
int _c : 10;//成員占10個bit位
//4個位元組-32bit
int _d : 30;//成員占30個bit位
};
int main()
{
printf("%d\n", sizeof(struct A));//4+4=8
return 0;
}
位段的記憶體分配
- 位段的成員可以是 int unsigned int signed int 或者是 char (屬于整形家族)型別
- 位段的空間上是按照需要以4個位元組( int )或者1個位元組( char )的方式來開辟的,
- 位段涉及很多不確定因素,位段是不跨平臺的,注重可移植的程式應該避免使用位段,
struct S
{
char a : 3;
char b : 4;
char c : 5;
char d : 4;
};
int main()
{
struct S s = { 0 };
s.a = 10;
s.b = 12;
s.c = 3;
s.d = 4;
return 0;
}
分析


該分析只適用于VS
位段的跨平臺問題
- int 位段被當成有符號數還是無符號數是不確定的,
- 位段中最大位的數目不能確定,(16位機器最大16,32位機器最大32,寫成27,在16位機器會出問題,
- 位段中的成員在記憶體中從左向右分配,還是從右向左分配標準尚未定義,
- 當一個結構包含兩個位段,第二個位段成員比較大,無法容納于第一個位段剩余的位時,是舍棄剩余的位時,還是利用,這是不確定的,
總結:
跟結構相比,位段可以達到同樣的效果,但是可以很好的節省空間,但是有跨平臺的問題存在,
列舉
列舉型別的定義及使用
列舉就是一一列舉
比如:
一周的星期一到星期日是有限的7天,可以一一列舉,
月份有12個月,可以一一列舉,
例子:
enum Color
{
RED,
GREEN,
BLUE
};
int main()
{
enum Color c = BLUE;
return 0;
}
要點:
{}中的內容是列舉型別的可能取值,也叫 列舉常量 ,
這些可能取值都是有值的,默認從0開始,一次遞增1,當然在定義的時候也可以賦初值,


列舉的優點
- 增加代碼的可讀性和可維護性
- 和#define定義的識別符號比較列舉有型別檢查,更加嚴謹,
- 防止了命名污染(封裝)
- 便于除錯
- 使用方便,一次可以定義多個常量
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/287487.html
標籤:其他
上一篇:成像代碼讀入部分
下一篇:對庫函式函式的一些總結
