【C语言】自定义类型(结构体、位段、枚举、联合体)与内存对齐
目錄
一、自定義類型種類
1、結構體
2、位段
3、枚舉
4、聯合體(共同體)
二、結構體
1、結構體的聲明
2、結構體變量的定義與初始化
1.定義
2.結構體變量的初始化
3、結構體成員的訪問
1.變量訪問(.)
2.指針訪問(->)
?4、結構體重命名
1.結構體重命名
2.結構體指針重命名
5、結構體傳參
1.值傳參
2.址傳參
6、結構體內存對齊
1.對齊數
2.內存對齊的規則
3.計算結構體大小訓練
4.內存對齊的意義
?三、位段
1、位段的成員要求及聲明
1.位段成員變量
2.位段的聲明
2、位段的內存分配
?四、聯合體
1、聯合體的概念與聲明
1.概念
2.聲明
2、聯合體的內存分配
?3.聯合體的應用——判斷大小端(面試題)
五、枚舉
1、枚舉的聲明
2、枚舉的使用
一、自定義類型種類
1、結構體
2、位段
3、枚舉
4、聯合體(共同體)
二、結構體
1、結構體的聲明
定義一個表示學生信息的結構體如下
struct students {char name[10];int age;char sex[5]; };根據上述代碼可得到結構體的第一種聲明方式
struct 結構體名 {成員變量列表; }; //注意此處有分號除此之外還有匿名結構體,這種結構體只能使用一次,在聲明的時候就定義了變量s,之后不可以再定義變量
struct {成員變量列表; }s;2、結構體變量的定義與初始化
1.定義
1.在聲明的時候定義變量
struct students {char name[10];int age;char sex[5]; }stu1;在聲明的時候創建了結構體變量stu1,stu1此時是一個全局變量
2.在main函數內部定義變量
struct students {char name[10];int age;char sex[5]; }stu1;int main() {struct students stu2; }這種定義方式:struct 結構體名 變量名;
2.結構體變量的初始化
1.在聲明的時候初始化
同2.1.1中的定義方式,在定義時初始化
struct students {char name[10];int age;char sex[5]; }stu1={"張三",15,"男"};定義方式:
struct students {成員變量列表; }變量名={相對應成員列表的數據};eg:定義一個表示坐標的結構體,p點在(2,4),定義變量表示p struct point {int x;int y; }P={2,4};2.在main函數內部初始化
struct students {char name[10];int age;char sex[5]; }stu1={"張三",15,"男"};int main() {struct students stu2={"李四",14,"男"}; }定義方式:struct 結構體名 變量名={與成員列表對應類型的數據};
3、結構體成員的訪問
1.變量訪問(.)
使用.這個操作符來訪問
#include<stdio.h>#pragma warning(disable:4996)struct students {char name[10];int age;char sex[5]; }stu1={"張三",15,"男"};int main() {struct students stu2={"李四",14,"男"};printf("%s %d %s", stu2.name, stu2.age, stu2.sex); }控制臺:
李四 14 男
訪問時:變量名.要訪問的結構體成員列表里的變量名
//給stu1變量賦值年齡如下:stu1.age=90;2.指針訪問(->)
使用前必須先定義一個結構體指針來指向要訪問的結構體變量
struct students stu2={"李四",14,"男"};struct students* p = &stu2;此時訪問時可以通過:指針名->要訪問的成員變量名
#include<stdio.h>struct students {char name[10];int age;char sex[5]; }stu1={"張三",15,"男"};int main() {struct students stu2={"李四",14,"男"};struct students* p = &stu2;printf("%s %d %s", p->name, p->age, p->sex);return 0; }控制臺:
李四 14男?
?使用:指針名->成員變量名
?4、結構體重命名
1.結構體重命名
使用typedef重命名
typedef struct students {char name[10];int age;char sex[5]; }STU;int main() {STU stu2={"李四",14,"男"};//重命名后使用重命名后的名字定義return 0; }2.結構體指針重命名
struct students { }* 重命名的名字;重命名后結構體指針可以這樣使用
typedef struct students {char name[10];int age;char sex[5]; }STU,* stu;int main() {STU stu2={"李四",14,"男"};stu p = &stu2;printf("%s %d %s", p->name, p->age, p->sex);return 0; }控制臺:
李四 14 男
5、結構體傳參
1.值傳參
void print(struct students s) {printf("%s %d %s", s.name, s.age, s.sex); }int main() {struct students stu2={"李四",14,"男"};print(stu2);//將結構體變量直接傳入函數return 0; }這種傳參方式不推薦使用,因為函數的每次調用都會在棧上開辟空間,開辟函數棧幀的時候,而形參又是實參的零時拷貝,會將結構體變量拷貝一份到函數的棧上,如果結構體所占內存過大,對空間的消耗會很大
2.址傳參
void print(struct students* s)//由于傳入的是一個地址,所以要用指針來接收 {printf("%s %d %s", s->name, s->age, s->sex); }int main() {struct students stu2={"李四",14,"男"};print(&stu2);//將該結構體變量的指針傳入return 0; }推薦使用這種傳參方式,傳入地址,節省空間
6、結構體內存對齊
1.對齊數
在介紹內存對齊前必須先了解一個概念,就對齊數。對齊數就是,該編譯器默認對齊數與該變量所占字節的小的較小值,如果沒有默認對對齊數,就是后者,VS2022默認對齊數時8
默認對齊數的修改
#pragma pack(對齊數字)默認對齊數的改回
#pragma pack()2.內存對齊的規則
1.第一個結構體成員放在相對于起始地址偏移量為0的地方存放
2.其他結構體成員要對齊到對齊數的整數倍的位置上
3.結構體總的大小必須等于最大對齊數的整數倍
4.如果有結構體嵌套的情況,嵌套結構體對齊到他最大對齊數的整數倍,結構體整體大小必須對齊到整個結構體(包含嵌套結構體)的最大對齊數的整數倍處
3.計算結構體大小訓練
?1.
struct point {int a;int b;char c; };//計算該結構題大小?根據上述規則一,第一個成員變量a從相對偏移量為0的位置開始存儲他占4個字節,于是存到了0-3,第二個變量b根據規則2,他的對齊數是4,從對齊數的整數倍開存,接下來存儲a之后偏移量來到了4是4的整數倍,開始存儲占用4-7,最后是c他的對齊數是1,偏移量來的了8,于是存進去了c,接著相對偏移量到了9,但是最大對齊數是4,9不是4的整數倍,所以繼續偏移,直到12,是最大偏移量的整數倍,所以這個結構體所占大小是12
2.
struct point {int a;char b;int c; };?a是第一個成員變量,存儲在相對偏移量為0的位置,所以0-3存儲a,接下來是b對齊數是1,于是存放到相對偏移量為4,接著c的對齊數是4,但是相對偏移量是5,不是4的整數倍,直到相對偏移量為8的時候開始存儲8-11,存放c,接下來偏移量是12,是最大對齊數4的倍數,所以這個結構體大小也是12
4.內存對齊的意義
未對齊的數據訪問時需要訪問兩次才能拿到,內存對齊相當于拿空間來換時間?
對于32位的機器,如果沒有對齊,再下圖拿數據時,需要訪問2次才能拿到b的值
?三、位段
1、位段的成員要求及聲明
1.位段成員變量
必須是int unsigned int ,char ,signed int,int這些
2.位段的聲明
struct point {int a : 32;int b : 12;int c : 19; };每個成員變量后跟:數字,后面數其實是所占比特位大小
2、位段的內存分配
位段的跨平臺性差,不同的編譯器對他的內存分配不同,可移植性的代碼應該避免位段的使用,在VS里,他的內存分配如下:
先給他開辟4個字節空間來放第一個成員變量,放入后,接著存放第二個數據,如果在存放第二個數據的時候之前開辟的空間不夠用,就會接著再開辟4個字節,將第二個數據放在新開辟的空間里,以此類推直到存放完
在三.1.2聲明里的代碼所占大小是8個字節,先開辟4個字節空間存放a,接著不夠了再開辟4字節存放b,之后空間足夠放c,所以占8個字節
?四、聯合體
1、聯合體的概念與聲明
1.概念
變量公用一塊空間
2.聲明
union point {int a;char i; };union 聯合體名
{
成員變量;
};
2、聯合體的內存分配
如上述代碼
變量a與i公用一塊空間,他也存在內存對齊
?3.聯合體的應用——判斷大小端(面試題)
大端字節序:在存儲數據時,數字的低位存儲在內存的高地址,數字的高位存儲在低地址
小端字節序:在存儲數據時,數字的低位存儲在內存的低地址,數字的高位存儲在高地址
union point {int a;char i; };int main() {union point s;s.a = 1;s.i = 0;if (s.a==0){printf("小端");}else{printf("大端");}return 0; }先給變量a賦值1,他的大小端存儲方式如下圖,任何給聯合體里面的i賦值為0,由于他們同用一塊空間,所以改變i的值時不同的存儲方式,對a的值變化不同
?給i賦值0后可以看到小端存儲方式把a的值改變成立0,而大端存儲在這里沒有改變,根據這一特點,我們可以通過上述代碼來判斷編譯器的大小端
五、枚舉
1、枚舉的聲明
enum sex {male,female,shemale };2、枚舉的使用
1.我們使用define來處理時,如果是連續的數字時我們可以使用枚舉來增加程序的可讀性
#define red 0 #define yellow 1 #define black 2 #define green 3 enum color {red,yellow,black,green };2.switch語句時也可以使用枚舉增加程序可讀性
enum Function {Eixt, Scoreb, Scorex, Scoreq, Scoresort };do{system("cls");printf("\t\t\t ___________________________________________________________\n");printf("\t\t\t| |\n");printf("\t\t\t| 0.退出 |\n");printf("\t\t\t| 1.操作 |\n");printf("\t\t\t| 2.操作 |\n");printf("\t\t\t| 3.操作 |\n");printf("\t\t\t| 4.操作 |\n");printf("\t\t\t|___________________________________________________________|\n");printf("\t\t請選擇成績操作:");scanf("%d", &n);switch (n){case Scoreb:scoreb(&class);break;case Scorex:scorex(&class);break;case Scoreq:scoreq(&class);break;case Scoresort:scoresort(&class);break;case Eixt:Eixtscore(&class);break;default:_getch();break;}} while (n);總結
以上是生活随笔為你收集整理的【C语言】自定义类型(结构体、位段、枚举、联合体)与内存对齐的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 通讯录管理系统(C语言版)
- 下一篇: 摩托罗拉 V360i的使用心得