目录
-
结构体类型创建:
结构体:
- 结构体是一个数据类型定义:
结构体原型:
struct tag {
member-list;
}variable-list;
例如:
struct stu {
char name[20]; //名字
int age ;//年龄
char sex ; //性别
}; //必须有分号
- struct 不能省略,如果省略,必须要 typedef 的方式进行类型重定义;
匿名结构体类型:
- 代码如下:
struct { //结构体没有名字.
int a;
char b;
} X; x是变量
//创建了一个匿名结构体,匿名结构体的变量名是x;
struct {
int a;
char b;
}a[20],*p;
//上面两个结构体声明时省略了结构体标签tag;
结构体的自引用:
- 一个结构体里面不能包含结构体本身;
- 正确的自引用方式:
strcut node{
int date;
struct node* nest;
};
-
结构体变量的定义和初始化:
变量定义:
- 变量定义代码示例:
strcut point{
int x;
int y;
int z;
}p1; //声明类型的同时定义了变量p1;
struct point p2; //定义结构体变量p2;
结构体初始化:
- 定义变量的同时赋初值;
struct point p3={x,y};
struct stu {
char name[20];
int age;
};
struct stu s={"jingyu",20}; //初始化
//结构体嵌套初始化
struct node{
int date;
struct point p;
struct node* nest;
}n1={10,{4,5},NULL};
struct node n2={30,{6,8},NULL};//结构体嵌套初始化
-
结构体内存对齐:
代码示例:
-
#include<stdio.h> #include<stdlib.h> struct test { int a; int b; int c; int d; }; //结构体包含了四个整型变量成员 int main() { struct test t; printf("&t=%p\n", &t); printf("&t.a=%p\n", &t.a); printf("&t,b=%p\n", &t.b); printf("&t.c=%p\n", &t.c); printf("&t.d=%p\n", &t.d); printf("sizeof(t)=%d\n",sizeof(t)); system("pause"); return 0; }
得到结果:
&t=0098FB88
&t.a=0098FB88
&t,b=0098FB8C
&t.c=0098FB90
&t.d=0098FB94
sizeof(t)=16
请按任意键继续. . .
- 如果代码改动: int a; 变成char a;
- j结果不变:
- 代码示例二:
-
#include<stdio.h> #include<stdlib.h> struct s1 { char a; int b; char c; }; struct s2 { char a; char b; int c; }; struct s3 { double a; char b; int c; }; struct s4 { char a; struct s3 b; double c; }; int main() { printf("sizeof(struct s1)=%d\n", sizeof(struct s1)); printf("sizeof(struct s2)=%d\n", sizeof(struct s2)); printf("sizeof(struct s3)=%d\n", sizeof(struct s3)); printf("sizeof(struct s4)=%d\n", sizeof(struct s4)); system("pause"); return 0; }
得到结果:
sizeof(struct s1)=12
sizeof(struct s2)=8
sizeof(struct s3)=16
sizeof(struct s4)=32
请按任意键继续. . .
结构体对齐规则:
-
结构体第一个成员位置 在结构体变量偏移量为0的地址处;
-
其他成员变量对齐到 对齐数的整数倍的地址;
-
对齐数 = 编译器默认的一个对齐数 与该成员大小比较的较小值; vs 中默认对齐数8; linux 默认是4;
-
结构体总大小是结构体对齐数的整数倍;
-
嵌套结构体: 嵌套结构体对齐自己的最大对齐数的整数倍,结构体的大小是最大对齐数的整数倍;结构体的整体大小就是所有最大对齐数(含嵌套结构体对齐数)的整数倍;
附加题:
struct s5 {
char a;
float b;
short c;
double d;
int e;
char f;
float g;
int h;
};
得到结果:
sizeof(struct s5)=40
请按任意键继续. . .
内存对其原因:
- 平台原因:不是所有硬件平台都能访问任意地址上的任意数据; 某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常;
- 性能原因: 数据结构(尤其是栈)应该尽可能地在自然边界上对齐;原因在于,为了访问未对齐的内存,处理器需要做两次内存访问,而对齐的内存访问仅需要一次访问;
- 设计结构体时,既要满足对齐,又要节省空间,让空间小的成员变量尽量集中在一起:
- 如下代码:
-
#include<stdio.h> #include<stdlib.h> struct s1 { char a; int b; char c; }; struct s2 { char a; char b; int c; }; int main() { printf("sizeof(s1)=%d\n", sizeof(struct s1)); printf("sizeof(s2)=%d\n", sizeof(struct s2)); system("pause"); }
结果:
-
sizeof(s1)=12
sizeof(s2)=8
请按任意键继续. . . -
我们可以看到,s1和s2类型成员一模一样,但是所占空间大小有区别;
修改默认对齐数:
- #pragma 这个预处理指令可以用来使用改变对齐数;
-
#include<stdio.h> #include<stdlib.h> #pragma pack(8) //设置默认对齐数为8 struct s1 { char a; int b; char c; }; #pragma pack() //取消设置的默认对齐数,还原默认 #pragma pack(1) //设置默认对齐数为1 struct s2 { char a; char b; int c; }; #pragma pack() //取消设置的默认对齐数,还原默认 int main() { printf("sizeof(s1)=%d\n", sizeof(struct s1)); printf("sizeof(s2)=%d\n", sizeof(struct s2)); system("pause"); }
得到结果:
-
sizeof(s1)=12
sizeof(s2)=6
请按任意键继续. . .
结构体传参:
- 传参两种方式;
-
#include<stdio.h> #include<stdlib.h> #pragma pack(8) //设置默认对齐数为8 struct S { int date[1000]; int num; }; struct S s = { {1,2,3,4},1000 }; //结构体传参 void printf1(struct S s) { printf("%d\n", s.num); } //结构体地址传参 void printf2(struct S *ps) { printf("%d\n", ps->num); } int main() { printf1(s); //传结构体 printf2(&s); //传地址 system("pause"); return 0; }
首选printf2函数;
-
函数传参的时候,参数需要压栈,会有时间和空间的开销;
-
如果传递一个结构体对象的时候,结构体过大,参数压栈的系统开销比较大,所以导致性能下降;
-
结论就是: 结构体传参的时候,要传结构体的地址;
- 计算结构体中某变量对于首地址的偏移:
-
struct test { char a; int b; }; int main() { struct test t; int ret =(char*)&t.b - (char*)&t; printf("%d\n", ret); system("pause"); return 0; }
-
位段,位段计算机大小:
什么是位段?
- 位段的声明和结构体类似;
- 位段的声明必须有一个冒号和数字;
- 位段定义和大小:
#include<stdio.h> #include<stdlib.h> struct A { int _a:2; int _b : 5; int _c : 10; int _d : 20; }; //A就是一个位段类型 int main() { printf("sizeof(struct A)=%d\n", sizeof(struct A));//位段大小 system("pause"); return 0; }
结果:
sizeof(struct A)=8
请按任意键继续. . .
位段的内存分配:
- 位段的成员可以是 int ,unsigned int,signed int或者 char 类型;
- 位段空间按照需要以4个字节(int)或者1个字节(char)的方式来开辟;
- 位段不跨平台;
- 位段跨平台时: int位段被当成有符号数还是无符号数不确定; 位段中最大位的数目不能确定; 位段中成员在内存从左到右还是从右到左分派尚未定义;
- 和结构体相比,位段可以很好的节省空间,但是不可以跨平台:
-
枚举+联合。
枚举类型定义:
- 星期,性别,颜色都可以采用枚举的方式定义:
-
enum Day { //星期枚举 Mon; Tues; Wed; Thur; Fri; Sat; Sun; }; enum color{ red =1; green =2; blue =3; }; enum color clr = green;//利用枚举常量给枚举变量赋值,才不会有类型差异;
- 这里的enum Day 是枚举类型; { }中的内容是枚举类型的可能取值,也叫枚举常量;
枚举的优点:
- 增加代码的可读性和可维护性;
- 和#define 定义的标识符比较,枚举有类型检查,国家严谨;
- 防止了命名污染;
- 便于调试;
- 使用方便.可以一次定义多个常量;
联合(共同体):
联合类型的定义:
- 联合也是一种自定义类型,这种类型定义变量包含了一系列的成员,特征是这些成员公用同一块空间;(所以联合也叫 共同体);
- 联合类型声明:
//联合类型的声明
union Un {
char c;
int i;
};
//联合变量的定义
int main() {
// 联合变量的定义
union Un un;
printf("sizeof(un)=%d\n", sizeof(un));
system("pause");
return 0;
}
sizeof(un)=4
请按任意键继续. . .
联合的特点:
- 联合的成员共同用一块内存空间;
- 一个联合变量的大小,至少是最大成员的大小;
- 这里看一个示例程序:
-
#include<stdio.h> #include<stdlib.h> //联合类型的声明 union Un { char c; int i; }; union Un1 { char c[1]; int i; }; union Un2 { short c[4]; int i; }; union Un3 { short c[7]; int i; }; union Un4 { short c[12]; int i; }; int main() { // 联合变量的定义 union Un un; printf(" &un.c=%d\n", &un.c); printf(" &un.i=%d\n", &un.i); un.c = 0x11223344; un.i = 0x55; printf(" &un.c=%d\n", &un.c); printf(" &un.i=%d\n", &un.i); printf("sizeof(union Un1)=%d\n", sizeof(union Un1)); printf("sizeof(union Un2)=%d\n", sizeof(union Un2)); printf("sizeof(union Un3)=%d\n", sizeof(union Un3)); printf("sizeof(union Un4)=%d\n", sizeof(union Un4)); system("pause"); return 0; }
运行结果:
-
&un.c=11533460
&un.i=11533460
&un.c=11533460
&un.i=11533460
sizeof(union Un1)=4
sizeof(union Un2)=8
sizeof(union Un3)=16
sizeof(union Un4)=24
请按任意键继续. . .
晚安,
2018年的5月8日,北京奥运会火炬成功登顶珠穆朗玛峰!!!!!