網站首頁 編程語言 正文
結構體
結構體內存對齊問題:
當我們在計算結構體的大小時,我們便需要清楚的知道結構體內存對齊是什么。
存在內存對齊的原因可細分為兩個:
平臺原因:
不是所有的硬件平臺都能方位任意地址上的任意數據;某些硬件平臺只能在某些地址處取某些特定類型的數據,否則會拋出硬件異常。
性能原因:
首先內存對齊可以提高程序的性能,當訪問未對其的內存空間時,有時候處理器需要進行兩次訪問,而當訪問對齊的內存時,只需要一次就夠了。這同時也被叫做 用空間換取時間。?
結構體的對齊規則:
1.第一個成員在與結構體變量偏移量為0的地址處。
2.其他成員變量要對齊到某各數字(對齊數)的整數倍的地址處。
對齊數=編譯器默認的一個對齊數 與 該成員大小的較小值。(VS中默認的值為8)
3.結構體總大小為最大對齊數(每個成員變量都有一個對齊數)的整數倍。
4.如果嵌套了結構體的情況,嵌套的結構體對齊到自己的最大對齊數的整數倍處,結構體的整體大小就是所有最大對齊數(含嵌套結構體的對齊數)的整數倍。
舉例1:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main()
{
struct s1
{
char c1; // 1字節
int i; // 4字節
char c2; // 1字節
};
printf("%d\n", sizeof(struct s1));
}
輸出結果為:
解釋如下:
我們易知內存會為結構體開辟一塊空間來給結構體存儲數據,從而我們可以用下圖的方式將該空間給表示出來:
舉例2:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main()
{
struct s2
{
int i; // 4字節
char c1;// 1字節
char c2;// 1字節
};
printf("%d\n", sizeof(struct s2));
}
輸出結果為:
解釋如下:
舉例3:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
struct s3
{
double d; // 8字節
char c; // 1字節
int i; // 4字節
};
struct s4
{
char c1; // 1字節
struct s3; // 16字節
double d; // 8字節
};
int main()
{
printf("%d\n", sizeof(struct s4));
return 0;
}
輸出結果為:
解釋如下:
結論總結:
當我們想內存對齊的同時也想節省空間時,可以將空間小的變量集中在一起!!
offsetof-宏
用途:計算結構體成員相對于起始位置的偏移量的
注意:使用該函數時,應該引用頭文件 #include <stddef.h>
舉例:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stddef.h>
struct s1
{
char c1;
int i;
char c2;
};
int main()
{
printf("%u\n", offsetof(struct s1,c1));
printf("%u\n", offsetof(struct s1, i));
printf("%u\n", offsetof(struct s1, c2));
}
輸出結果為:
位段
位段的成員類型必須為: int、unsigned int、signed int
位段的空間是按照需要以4個字節(int)或者1個字節(char )的方式來開辟的
位段的成員名后邊有一個冒號和一個數字!
舉例1:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
struct s5
{ // 位段所代表的意思
int _a : 2; // _a 占 2個bit位
int _b : 5; // _b 占 5個bit位
int _c : 10;// _c 占 10個bit位
int _d : 30;// _d 占 30個bit位
};
int main()
{
printf("%d\n", sizeof(s5));
return 0;
}
輸出結果為:( 原本的字節大小為 16 字節 =16*8=128 bit? 現在的字節大小為 8字節且只占 2+5+10+30 = 47bite)
那為啥不是占用7字節呢?7字節有 7*8=56bite 也夠使用啊?
我們便需要根據位段的規定來解釋,當為(int)類型時內存空間每次都是以4字節的大小來開辟空間的,當為(char)類型時內存空間每次都是以1字節的大小來開辟空間的,所給例子為int類型,當所定的第一個4字節空間不夠用時,便會再開辟一塊大小為4字節的空間來供其存儲,從而輸出結果為8字節。
結論:??
我們可以根據所定義的整型數字大小,利用位段來給其分配相適應大小的空間,從而有效的幫我們進行內存空間的節省。
舉例2:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
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編譯器)內存中是如何存儲的,便可以打開監控來進行調試
如圖所示:
?(結構體變量s 剛開始初始化了 3字節)
?(結構體變量s 經過賦值之后存儲數值的變化)
解釋如下圖:
位段的跨平臺問題:
1. int 位段被當成有符號數還是無符號數是不確定的。
2. 位段中最大位的數目不能確定。(16位機器最大16,32位機器最大32),寫成27,在16位機器會出問題。
3.位段中的成員在內存中從左向右分配,還是從右向左分配標準尚未定義。
4. 當一個結構包含兩個位段,第二個位段成員比較大,無法容納于第一個位段剩余的位時,是舍棄剩余的位還是利用,這是不確定的。
結論:
位段涉及很多不確定因素,位段是不垮平臺的,注重可移植的程序應該避免使用位段。
枚舉
枚舉類型是某類數據可能取值的集合
例子:一周內星期的取值為7天,可以一一列舉出來
定義方式及使用方式:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
enum day
{
Mon,
Tues,
Wed,
Thur,
Fri,
Sat,
Sun
};
int main()
{
enum day s1 = Mon;
enum day s2 = Sat;
return 0;
}
舉例1:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
enum color
{
blue,
green,
yellow
};
int main()
{
printf("%d\n", blue);
printf("%d\n", green);
printf("%d\n", yellow);
return 0;
}
輸出結果為:?(由結果知枚舉常量會被自動從0開始一次往下賦值)
拓展:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
enum color
{
blue=4,
green=7,
yellow
};
int main()
{
printf("%d\n", blue);
printf("%d\n", green);
printf("%d\n", yellow);
return 0;
}
輸出結果為:(由此可知枚舉常量我們可以自定義賦值,未賦值常量為其上一常量的值+1)
#define 也可以用來定義常量,那用枚舉來定義常量的優點為:
1.增加代碼的可讀性和可維護性
2. 和#define定義的標識符比較枚舉有類型檢查,更加嚴謹
3.防止了命名污染(封裝)
4. 便于調試
5. 使用方便,一次可以定義多個常量
聯合體(共用體)
特點:
各成員共享一段內存空間,一個 聯合變量的長度等于各成員中最長的長度。
舉例1:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
union Un
{
char c;
int i;
};
int main()
{
union Un u1;
printf("%d\n", sizeof(u1));
printf("%p\n", &u1);
printf("%p\n", &u1.c);
printf("%p\n", &u1.i);
}
輸出結果為:?
如圖所示:?
應用:(用來判斷編譯器是大端存儲還是小端存儲)
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int chek_sys()
{
union Un //創建一個聯合體 char c 和 int i 共用同一塊存儲空間
{
char c;
int i;
}u;
u.i = 1; // 這里給i賦值為1
//若為小端存儲時內存中所存儲:01 00 00 00(16進制) 為大端存儲: 00 00 00 01(16進制)
return u.c; //這里直接返回 char c 與 int i 所共用空間的值
//返回1字節大小的值 即 int i 以16進制方式所存儲的前兩位數字 ,若值為1則為小端 若值為0則為大端
}
int main()
{
if (1 == chek_sys())
{
printf("小端\n");
}
else
{
printf("大端\n");
}
return 0;
}
輸出結果:
舉例2:(計算聯合體的大小)
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
union Un
{
char arr[5];//5字節
int i; //4字節
};
int main()
{
printf("%d\n", sizeof(union Un));
return 0;
}
輸出結果為:(我們從結果得知聯合體也存在內存對齊)?
解釋:
char類型數組先占用5字節,其對齊數為1字節(char),int i占用4字節與char類型數組公用同一塊空間,其對齊數為4字節(int),該聯合體所占5字節,但存在內存對齊,需為4字節的倍數,從而要浪費3字節空間使其為8字節。
以上便是關于結構體和聯合體的全部內容 !
如有錯誤或者能改進的地方 請各大佬指出 我會及時改正!!?
原文鏈接:https://blog.csdn.net/weixin_63888301/article/details/124309746
相關推薦
- 2024-07-18 Spring Security之認證信息的處理
- 2022-07-15 利用apache?ftpserver搭建ftp服務器的方法步驟_Linux
- 2023-03-27 python中的正則表達式,貪婪匹配與非貪婪匹配方式_python
- 2022-08-16 C#在MEF框架中實現延遲加載部件_C#教程
- 2023-10-17 element ui的from表單,不斷修改版
- 2022-06-01 python?嵌套型partials的使用_python
- 2022-06-26 python中class類與方法的用法實例詳解_python
- 2022-12-10 OpenMP深入剖析reduction子句教程_C 語言
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支