C语言有内置类型
如 char , short , int, long,long long, double, float
也支持自定义类型
结构体 枚举 联合体
结构体
关于结构体的声明和变量的创建
数组是相同类型的元素的集合
结构是一些值的集合,这些值称为成员变量。结构的每个成员可以是不同类型的变量。

结构体类型 struct Student
结构体成员变量 name, age, sex,
结构变量 s1,s2,s3,s4,s5,其中s1和s2是结构体全局变量,s3,s4和s5是结构体局部变量
结构体的特殊声明
这里省略了结构体的名字Student,也被称之为匿名结构体类型
这种结构体不能创建结构体局部变量,只能在结构体后面创建全局变量,如s1,s2
结构体的自引用
在结构中包含一个类型为该结构本身的成员是否可以呢?
结构体中包含在自己这个结构体创建的指针,就是结构体的自引用
结构体的定义和初始化

结构体内存对齐
首先得掌握结构体的对齐规则:
1. 第一个成员在与结构体变量偏移量为0的地址处。
2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。 对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。 VS中默认的值为8
3. 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。
4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整 体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
内存对齐的主要目的是为了效率
例题一:
创建两个结构体,结构体的成员变量相同,但是排列顺序不同,问占用内存是否相同
struct S1
{
char c1;
char c2;
int i;
};
struct S2
{
char c1;
int i;
char c2;
};
int main()
{
//结构体的成员变量类型都相同但占用字节大小不同
printf("%d\n", sizeof(struct S1));
printf("%d\n", sizeof(struct S2));
return 0;
}



例题二
对于结构体的嵌套,求结构体占用内存
struct S3
{
double d;
char c;
int i;
};
//结构体的嵌套
struct S4
{
char c1;
struct S3 s3;
double d;
};
int main()
{
////结构体的成员变量类型都相同但占用字节大小不同
//printf("%d\n", sizeof(struct S1));
//printf("%d\n", sizeof(struct S2));
printf("%d\n", sizeof(struct S3));
printf("%d\n", sizeof(struct S4));
return 0;
}
这里最大对齐数是8,那么结构体字节数必须大于等于16且是8的倍数



修改默认对齐数
默认对齐数的修改一般都是2的次方
#pragma pack(1)//设置默认对齐数为1
struct S1
{
char c1;
int i;
char c2;
};
#pragma pack()//取消设置的默认对齐数,还原为默认
struct S2
{
char c1;
int i;
char c2;
};
int main()
{
printf("%zd\n", sizeof(struct S1));
printf("%zd\n", sizeof(struct S2));
return 0;
}
结构体传参
结构体传参一般会使用传址
函数传参的时候,参数是需要压栈,会有时间和空间上的系统开销。
如果传递一个结构体对象的时候,结构体过大,参数压栈的的系统开销比较大,所以会导致性能的降低
struct S
{
char arr[1000];
int num;
};
void print1(struct S s1)
{
printf("传值调用%d %d %d %d\n", s1.arr[0], s1.arr[1], s1.arr[2], s1.num);
}
void print2(const struct S* p)
{
printf("传址调用%d %d %d %d\n", p->arr[0], p->arr[1], p->arr[2], p->num);
}
int main()
{
struct S s1 = { {1,2,3},100 };
print1(s1);//这里传递的是s1变量,函数在打印时会重新开辟空间,设置变量
print2(&s1);//这里是传址调用,不需要开辟空间
return 0;
}

2.位段
位段与结构体相比可以很好的节省空间,但是存在着明显缺陷(难以跨平台)
2.1什么是位段
位段和结构体类似
主要区别有两个
1.位段的成员必须是 int, unsigned int, 或者signed int(在C99之后也有其他类型,但主要是 int和char)
2.位段的成员后面有一个冒号和一个数字
//struct A 是位段,而struct B 是结构体
struct A
{
int _a : 2;//这里的:2表示_a 变量占用2个比特位
int _b : 5;//5个比特位
int _c : 10;//10个比特位
int _d : 30;//30个比特位
};
struct B
{
int _a;
int _b;
int _c;
int _d;
};
int main()
{
printf("位段占用的空间%d\n", sizeof(struct A));
printf("结构体占用的空间%d\n", sizeof(struct B));
return 0;
}

2.2位段的内存分配(在VS编译器中)
1. 位段的成员可以是 int unsigned int signed int 或者是 char (属于整形家族)类型
2. 位段的空间上是按照需要以4个字节( int )或者1个字节( char )的方式来开辟的。
3. 位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使用位段
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;
}

3.枚举
也就是枚举类型
枚举就是把可能的取值列举出来
enum day
{
//里面是可能的取值,也叫枚举常量
Mon,//枚举常量
Tues,
Wed,
Thur,
Fri,
Sat,
Sun
};
int main()
{
//枚举常量的值第一个值默认从0开始依次递增,常量不可修改
printf("%d ", Mon);
printf("%d ", Tues);
printf("%d ", Wed);
printf("%d ", Thur);
printf("%d ", Fri);
printf("%d ", Sat);
printf("%d ", Sun);
return 0;
}

enum day
{
//里面是可能的取值,也叫枚举常量
Mon=100,//枚举常量在main函数中不可修改,但是在设定是可以改变
Tues,
Wed,
Thur=200,
Fri,
Sat,
Sun
};
int main()
{
//枚举常量的值第一个值默认从0开始依次递增,常量不可修改
printf("%d ", Mon);
printf("%d ", Tues);
printf("%d ", Wed);
printf("%d ", Thur);
printf("%d ", Fri);
printf("%d ", Sat);
printf("%d ", Sun);
return 0;
}
枚举的使用
枚举可以用来定义一组数据,与#define类似,但是比它跟安全
enum Color
{
RED=10,
GREEN,
BLUE
};
int main()
{
enum Color cla = GREEN;//enum Color是枚举类型,用来定义枚举变量,
//然后把枚举常量red的值赋给这个变量
//只能拿枚举常量给枚举变量赋值,才不会出现类型的差异
printf("%d\n", cla);
return 0;
}
4.联合
联合类型的定义
联合也是一种特殊的自定义类型
这种类型定义的变量也包含一系列的成员,特征是这些成员公用同一块空间(所以联合也叫共用体)
union Un
{
int a;
char ch;
};
int main()
{
union Un un;
printf("%p\n", &un);//联合体变量un中的成员a和成员ch共用同一块空间,
printf("%p\n", &un.a); //所以成员变量一般不能同时使用
printf("%p\n", &un.ch);
printf("%d\n", sizeof(un));
return 0;
}

联合体大小的计算
联合的大小至少是最大成员的大小。
当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍。
union Un1
{
char c[5];//5个字节,默认对齐数是8,数组相当于5个char类型变量对齐数按1来算
int i;//4 个字节 对齐数按4来算,所以最大对齐数是4,要大于等于5且是4的倍数,所以占8个字节
};
union Un2
{
short c[7];//每个元素两个字节,对齐数是2
int i;//四个字节,对齐数是4,要大于等于14且是4的倍数,所以是16
};
int main()
{
printf("%d\n", sizeof(union Un1));
printf("%d\n", sizeof(union Un2));
return 0;
}

联合体的使用
要求:1.联合体的成员不在同一时间使用
可以用来判断编译器是大端存储还是小端存储
//int fun1()
//{
// int a = 1;
// return *(char*)&a;
//}
int fun1()
{
union un
{
int a;
char i;
}u;
u.a = 1;//利用占用同一块空间,
return u.i;
}
int main()
{
int rst = fun1();
if (rst == 1)
{
printf("小端\n");
}
else
{
printf("大端\n");
}
return 0;
}



被折叠的 条评论
为什么被折叠?



