詳解C語言結(jié)構(gòu)體,枚舉,聯(lián)合體的使用
一、匿名結(jié)構(gòu)體
struct { char name[20]; int age; }s1;
匿名結(jié)構(gòu)體對(duì)象s1過了這一行即銷毀。
二、結(jié)構(gòu)體的自引用
1、聲明時(shí)不要自己引用自己
struct Node { int data; struct Node next;//錯(cuò)誤的,嚴(yán)禁自己引用自己 }; struct Node { int data; struct Node* next;//正確的引用方式 };
2、結(jié)構(gòu)體重命名時(shí)不能使用重命名
typedef struct { int data; Node* next;//錯(cuò)誤的,不要再重命名中使用重命名 }Node; typedef struct Node { int data; struct Node* next;//正確的 }Node;
博主在學(xué)數(shù)據(jù)結(jié)構(gòu)的時(shí)候踩過這個(gè)坑,在結(jié)構(gòu)體重命名的時(shí)候成員變量的類型就使用了重命名,導(dǎo)致整個(gè)程序不認(rèn)識(shí)這個(gè)成員變量的類型(但是vs在typedef這里不報(bào)錯(cuò),而是在每個(gè)使用這個(gè)類型的地方報(bào)錯(cuò)?。。。:髞戆堰@個(gè)成員變量的類型修改為重命名之前的類型,整個(gè)程序就可以運(yùn)行了。(如上圖的正確寫法)
三、結(jié)構(gòu)體內(nèi)存對(duì)齊規(guī)則
第一個(gè)成員在與結(jié)構(gòu)體變量偏移量為0的地址處。
后續(xù)成員變量要放到各自的對(duì)齊數(shù)的倍數(shù)上。對(duì)齊數(shù) = 編譯器默認(rèn)對(duì)齊數(shù)與該成員類型大小的較小值。(vs中默認(rèn)對(duì)齊數(shù)是8,gcc沒有默認(rèn)對(duì)齊數(shù))
結(jié)構(gòu)體最終大小為最大對(duì)齊數(shù)的整數(shù)倍。
如果嵌套了結(jié)構(gòu)體的情況,嵌套的結(jié)構(gòu)體對(duì)齊到自己的最大對(duì)齊數(shù)的整數(shù)倍處,結(jié)構(gòu)體的整體大小就是所有最大對(duì)齊數(shù)(含嵌套結(jié)構(gòu)體的對(duì)齊數(shù))的整數(shù)倍。
1、結(jié)構(gòu)體內(nèi)存計(jì)算
struct S1 { char c1; int i; char c2; }; int main() { printf("%d\n", sizeof(struct S1)); return 0; }
char c1在結(jié)構(gòu)體變量的零偏移量處分配內(nèi)存
int i的對(duì)齊數(shù)為4,所以跳過3個(gè)字節(jié),在4的整數(shù)倍地址處分配內(nèi)存
char c2的對(duì)齊數(shù)為1,使用下一個(gè)字節(jié)空間即可
目前已使用9字節(jié)
由于該結(jié)構(gòu)體中所有成員變量中最大的成員類型大小為4字節(jié),所以最大內(nèi)存對(duì)齊數(shù)為4字節(jié),結(jié)構(gòu)體總大小為最大對(duì)齊數(shù)的整數(shù)倍。所以該結(jié)構(gòu)體內(nèi)存為12字節(jié)。
可以使用宏offsetof來觀察結(jié)構(gòu)體成員在內(nèi)存中的偏移量:
2、結(jié)構(gòu)體嵌套
struct S3//16 { double d; char c; int i; }; struct S4//32 { char c1; struct S3 s3; double d; };
char c1在結(jié)構(gòu)體變量的零偏移量處分配內(nèi)存
struct S3 s3按照其最大內(nèi)存對(duì)齊數(shù)(此處為8)進(jìn)行對(duì)齊
double d按照其最大內(nèi)存對(duì)齊數(shù)(8)進(jìn)行對(duì)齊
S4的最大內(nèi)存對(duì)齊數(shù)為8,所以結(jié)構(gòu)體的最終大小為32
3、通過調(diào)整結(jié)構(gòu)體成員順序,壓縮內(nèi)存
通過上述例子可以發(fā)現(xiàn),結(jié)構(gòu)體成員之間有很大的空間浪費(fèi),哪怕是擁有相同結(jié)構(gòu)體成員的兩個(gè)結(jié)構(gòu)體類型,其在內(nèi)存中所占據(jù)的空間也不相同,所以為了空間的節(jié)省,在不影響數(shù)據(jù)結(jié)構(gòu)的情況下,有目的的把字節(jié)占用小的成員變量放在一起,達(dá)到節(jié)省空間的目的。
四、存在內(nèi)存對(duì)齊的原因
1. 平臺(tái)原因(移植原因)
不是所有的硬件平臺(tái)都能訪問任意地址上的任意數(shù)據(jù)的;某些硬件平臺(tái)只能在某些地址處取某些特定類型的數(shù)據(jù),否則拋出硬件異常。
2. 性能原因(空間換時(shí)間)
數(shù)據(jù)結(jié)構(gòu)(尤其是棧)應(yīng)該盡可能地在自然邊界上對(duì)齊。 原因在于,為了訪問未對(duì)齊的內(nèi)存,處理器需要作兩次內(nèi)存訪問;而對(duì)齊的內(nèi)存訪問僅需要一次訪問。
五、修改默認(rèn)對(duì)齊數(shù)
#pragma pack(2)//把默認(rèn)對(duì)齊數(shù)改成2 struct S { char c1; int i; short c2; }; #pragma pack()//恢復(fù)默認(rèn)對(duì)齊數(shù)為8 int main() { printf("%d\n", sizeof(struct S)); return 0; } #pragma pack(num)修改默認(rèn)對(duì)齊數(shù),該結(jié)構(gòu)體的內(nèi)存大小由12字節(jié)降低為8字節(jié)。
默認(rèn)對(duì)齊數(shù)盡量為2的次方。
六、結(jié)構(gòu)體傳參
結(jié)構(gòu)體傳參要傳地址。
傳址調(diào)用優(yōu)于傳值調(diào)用的原因是地址占4/8個(gè)字節(jié)。
但是傳值調(diào)用參數(shù)需要壓棧,當(dāng)結(jié)構(gòu)體過大時(shí),參數(shù)壓棧的系統(tǒng)開銷較大。
七、位段
位段是在結(jié)構(gòu)體中實(shí)現(xiàn)的。
位段的成員可以是 int、unsigned int、signed int或者是char類型
位段的空間增長方式為每次增長4個(gè)字節(jié)(int)或1個(gè)字節(jié)(char)
位段涉及很多不確定因素,位段是不跨平臺(tái)的,注重可移植的程序應(yīng)該避免使用位段。
1、位段在內(nèi)存中的存儲(chǔ)
1.1位段中char類型的存儲(chǔ)方式(vs中舍棄剩余空間)
struct S//占用3個(gè)字節(jié) { char _a : 3; char _b : 4; char _c : 5; char _d : 4; }; int main() { printf("%d\n", sizeof(struct S)); struct S s= { 0 }; s._a = 10;//1010,截?cái)酁?10 s._b = 12;//1100 s._c = 3;//0011 s._d = 4;//0100 return 0; }
通過調(diào)用內(nèi)存發(fā)現(xiàn),&s中存儲(chǔ)的16進(jìn)制數(shù)字為62 03 04,那么可以發(fā)現(xiàn)s在內(nèi)存中的存儲(chǔ)方式如下圖:
在vs環(huán)境中,char成員變量在單個(gè)字節(jié)中是倒著存儲(chǔ)的(有截?cái)嘞劝l(fā)生截?cái)啵?,?dāng)該字節(jié)中剩余的比特位不足以存放下一個(gè)完整的成員變量時(shí),會(huì)將剩余的比特位舍棄。
1.2位段中int類型的存儲(chǔ)方式(vs中利用剩余空間)
struct A//占4個(gè)字節(jié) { int a : 2; int b : 3; int c : 4; }; int main() { struct A s = { 0 }; s.a = 12;//1100,截?cái)酁?0 s.b = 13;//1101,截?cái)酁?01 s.c = 14;//1110 return 0; }
通過調(diào)用內(nèi)存發(fā)現(xiàn),&s中存儲(chǔ)的16進(jìn)制數(shù)字為d4 01 00 00,那么可以發(fā)現(xiàn)s在內(nèi)存中的存儲(chǔ)方式如下圖:
在vs環(huán)境中,int成員變量在單個(gè)字節(jié)中是倒著存儲(chǔ)的(有截?cái)嘞劝l(fā)生截?cái)啵?,?dāng)該字節(jié)中剩余的比特位不足以存放下一個(gè)完整的成員變量時(shí),會(huì)將繼續(xù)存儲(chǔ),存不下的二進(jìn)制位將存放至下一個(gè)字節(jié)的右側(cè)。
注意:位段冒號(hào)后面的數(shù)字只能小于等于類型大?。ɡ鏲har a:9是錯(cuò)誤的)
2、位段的跨平臺(tái)問題
1.int 位段被當(dāng)成有符號(hào)數(shù)還是無符號(hào)數(shù)是不確定的。
2. 位段中最大位的數(shù)目不能確定。(16位機(jī)器最大16,32位機(jī)器最大32,寫成27,在16位機(jī)器會(huì)出問題。
3. 位段中的成員在內(nèi)存中從左向右分配,還是從右向左分配標(biāo)準(zhǔn)尚未定義。
4. 當(dāng)一個(gè)結(jié)構(gòu)包含兩個(gè)位段,第二個(gè)位段成員比較大,無法容納于第一個(gè)位段剩余的位時(shí),是舍棄剩余的位還是利用,這是不確定的。
八、枚舉
1、枚舉的定義
enum color { RED,//枚舉常量 BLUE, YELLOW };
不賦值那么默認(rèn)從0開始,后續(xù)枚舉成員的值遞增1
enum color { RED=1, BLUE, YELLOW };
只需要對(duì)第一個(gè)成員進(jìn)行賦值,后續(xù)枚舉成員的值遞增1
在寫枚舉成員的時(shí)候建議全大寫,博主在寫通訊錄枚舉了exit,使用時(shí)vs提示該命名和exit函數(shù)沖突。
2、枚舉的優(yōu)點(diǎn)
增加代碼的可讀性和可維護(hù)性
枚舉使用時(shí)有類型檢查,#define定義的標(biāo)識(shí)符沒有
防止了命名污染(封裝)
便于調(diào)試(#define定義宏在預(yù)處理時(shí)是直接替換)
使用方便,一次可以定義多個(gè)常量
九、聯(lián)合體(共用體)
聯(lián)合也是一種特殊的自定義類型 這種類型定義的變量也包含一系列的成員,特征是這些成員共用同一塊空間(所以聯(lián)合也叫共用體)。
1、聯(lián)合體大小的計(jì)算
#include <stdio.h> union un { char arr[5]; int a; }u; int main() { printf("%d", sizeof(u));//8 return 0; }
聯(lián)合體的大小至少是最大成員的大小。
最大內(nèi)存對(duì)齊數(shù)的整數(shù)倍要大于等于最大成員的大小。
(這里最大成員是arr,占5個(gè)字節(jié),最大內(nèi)存對(duì)齊數(shù)是4,所以需要為祖國聯(lián)合體開辟8個(gè)字節(jié)空間)
2、使用聯(lián)合體判斷計(jì)算機(jī)的大小端字節(jié)序
#include <stdio.h> union un { int m; char n; }u; int check_sys() { u.m = 1; return u.n; } int main() { int a = check_sys(); if (a == 1) printf("小端存儲(chǔ)\n"); else printf("大端存儲(chǔ)\n"); return 0; }
以上就是詳解C語言結(jié)構(gòu)體,枚舉,聯(lián)合體的使用的詳細(xì)內(nèi)容,更多關(guān)于C語言 結(jié)構(gòu)體 枚舉 聯(lián)合體的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
C語言實(shí)現(xiàn)可增容動(dòng)態(tài)通訊錄詳細(xì)過程
這篇文章主要為大家介紹了C語言實(shí)現(xiàn)簡易通訊錄的完整流程,此通訊錄還可以增容,并且每個(gè)環(huán)節(jié)都有完整代碼,有需要的朋友可以借鑒參考下,希望能夠有所幫助2022-05-05C++類與對(duì)象的重點(diǎn)知識(shí)點(diǎn)詳細(xì)分析
類和對(duì)象是兩種以計(jì)算機(jī)為載體的計(jì)算機(jī)語言的合稱。對(duì)象是對(duì)客觀事物的抽象,類是對(duì)對(duì)象的抽象。類是一種抽象的數(shù)據(jù)類型;變量就是可以變化的量,存儲(chǔ)在內(nèi)存中—個(gè)可以擁有在某個(gè)范圍內(nèi)的可變存儲(chǔ)區(qū)域2023-02-02C++實(shí)現(xiàn)圖書管理系統(tǒng)課程設(shè)計(jì)
這篇文章主要為大家詳細(xì)介紹了C++實(shí)現(xiàn)圖書管理系統(tǒng)課程設(shè)計(jì),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-03-03利用C++的基本算法實(shí)現(xiàn)十個(gè)數(shù)排序
以下是對(duì)利用C++的基本算法實(shí)現(xiàn)十個(gè)數(shù)排序的代碼進(jìn)行了介紹,需要的朋友可以過來參考下,希望對(duì)大家有所幫助2013-10-10cocos2dx實(shí)現(xiàn)刮獎(jiǎng)效果
這篇文章主要為大家詳細(xì)介紹了cocos2dx實(shí)現(xiàn)刮獎(jiǎng)效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-12-12