本篇主要讲解一些自定义类型(结构体 ,枚举,联合体)
目录
1.结构体
1.1结构体基础
在C语言中,结构体(struct)指的是一种数据结构,是C语言中聚合数据类型(aggregate data type)的一类。结构体可以被声明为变量、指针或数组等,用以实现较复杂的数据结构。结构体同时也是一些元素的集合,这些元素称为结构体的成员(member),且这些成员可以为不同的类型,成员一般用名字访问。
1.2结构体声明
结构体的定义如下所示,struct为结构体关键字,tag为结构体的标志,member-list为结构体成员列表,其必须列出其所有成员;variable-list为此结构体声明的变量
struct tag
{
member-list; }variable-list;
下面举个例子
struct stu{
char *name; //姓名
int num; //学号
int age; //年龄
char group; //所在学习小组
float score; //成绩
};
1.3结构体的特殊声明
这里的特殊声明是指在声明时可以不完全声明
//匿名结构体类型
struct
{
int a;
char b;
float c; }x;
struct
{
int a;
char b;
float c; }a[20], *p;
省略掉了结构体标签(tag)。
1.4结构体的自引用
//代码1
struct Node
{
int data;
struct Node* next;
};
//代码2
//typedef 为一个数据类型起一个新的别名,
typedef struct Node
{
int data;
struct Node* next; }Node;
1.5结构体变量的定义和初始化
定义及初始化介绍
struct Point
{
int x;
int y; }p1; //声明类型的同时定义变量p1
struct Point p2; //定义结构体变量p2
//初始化:定义变量的同时赋初值。
struct Point p3 = {x, y};
举例
struct Stu //类型声明
{
char name[15];//名字
int age; //年龄
};
struct Stu s = {"lisi", 24};//初始化
也可以结构体嵌套初始化
struct Node
{
int data;
struct Point p;
struct Node* next;
}n1 = {10, {4,5}, NULL};
struct Node n2 = {20, {5, 6}, NULL};
1.6结构体内存对齐
我们计算结构体大小就要 学会 结构体内存对齐
结构体的大小不是结构体元素单纯相加就行的,因为我们主流的计算机使用的都是32bit字长的CPU,对这类型的CPU取4个字节的数要比取一个字节要高效,也更方便。所以在结构体中每个成员的首地址都是4的整数倍的话,取数据元素时就会相对更高效,这就是内存对齐的由来。每个特定平台上的编译器都有自己的默认“对齐系数”(也叫对齐模数)。程序员可以通过预编译命令#pragma pack(n),n=1,2,4,8,16来改变这一系数,其中的n就是你要指定的“对齐系数”。
我们首先就要知道结构体的对齐规则
1.
第一个成员在与结构体变量偏移量为
0
的地址处。2.
其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。对齐数
=
编译器默认的一个对齐数 与 该成员大小的
较小值
。VS
中默认的值为
83.
结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。4.
如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
下面练习1 练习2
练习4 —-结构体嵌套问题
’1.7修改默认对齐数
使用#pragma
这个预处理指令,可以修改默认对齐数。
#include <stdio.h>
#pragma pack(8)//设置默认对齐数为8
struct S1
{
char c1;
int i;
char c2;
};
#pragma pack()//取消设置的默认对齐数,还原为默认
#pragma pack(1)//设置默认对齐数为1
struct S2
{
char c1;
int i;
char c2;
};
#pragma pack()//取消设置的默认对齐数,还原为默认
int main()
{
//输出的结果是什么?
printf("%d\n", sizeof(struct S1));
printf("%d\n", sizeof(struct S2));
1.8结构体传参
//结构体地址传参
void print2(struct S* ps) {
printf("%d\n", ps->num);
}
int main()
{
print1(s); //传结构体
print2(&s); //传地址
return 0; }
结构体传参 可以直接传结构体也可以传地址,我们这里传的是地址
因为函数传参时,参数是需要压栈,会有时间和空间上的系统开销,假若传递一个结构体对象的时候,结构体过大,会使压栈的系统开销比较大,所以会导致性能的下降。
2.位段
2.1位段是什么
位段的声明和结构是类似的,有两个不同:1.位段的成员必须是 int、unsigned int 或signed int 。2.位段的成员名后边有一个冒号和一个数字。
struct A {
int _a:2;
int _b:5;
int _c:10;
int _d:30;
};
2.2 位段的内存分配
1.
位段的成员可以是
int unsigned int signed int
或者是
char
(属于整形家族)类型2.
位段的空间上是按照需要以
4
个字节(
int
)或者
1
个字节(
char
)的方式来开辟的。3.
位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使用位段。
2.3 位段的跨平台问题
1. int
位段被当成有符号数还是无符号数是不确定的。2.
位段中最大位的数目不能确定。(
16
位机器最大
16
,
32
位机器最大
32
,写成
27
,在
16
位机器会出问题。3.
位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义。4.
当一个结构包含两个位段,第二个位段成员比较大,无法容纳于第一个位段剩余的位时,是舍弃剩余的位还是利用,这是不确定的。
3. 枚举
枚举(enum)类型是计算机编程语言中的一种数据类型。枚举类型:在实际问题中,有些变量的取值被限定在一个有限的范围内。例如,一个星期内只有七天,一年只有十二个月,一个班每周有六门课程等等。如果把这些量说明为整型,字符型或其它类型显然是不妥当的。
3.1 枚举类型的定义
enum Day//星期
{
Mon,
Tues,
Wed,
Thur,
Fri,
Sat,
Sun
};
enum Sex//性别
{
MALE,
FEMALE,
SECRET
};
{}
中的内容是枚举类型的可能取值,也叫
枚举常量
。这些可能取值都是有值的,默认从
0
开始,一次递增
1
,当然在定义的时候也可以赋初值。
3.2枚举的优点
1.
增加代码的可读性和可维护性2.
和
#define
定义的标识符比较枚举有类型检查,更加严谨。3.
防止了命名污染(封装)4.
便于调试5.
使用方便,一次可以定义多个常量
3.3枚举的使用
enum Color//颜色
{
RED=1,
GREEN=2,
BLUE=4
};
enum Color clr = GREEN;//只能拿枚举常量给枚举变量赋值,才不会出现类型的差异。
clr = 5;
4.联合体
4.1 联合体的定义
//联合类型的声明
union Un
{
char c;
int i;
};
//联合变量的定义
union Un un;
//计算连个变量的大小
printf("%d\n", sizeof(un));
4.2 联合体的特点
union Un
{
int i;
char c;
};
union Un un;
// 下面输出的结果是一样的吗?
printf("%d\n", &(un.i));
printf("%d\n", &(un.c));
//通过运行发现两个是相同的
4.3 联合体大小的计算
联合的大小至少是最大成员的大小。当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍。
union Un1
{
char c[5];
int i;
};
union Un2
{
short c[7];
int i;
};
//大家可以试着输出一下看有什么发现
printf("%d\n", sizeof(union Un1));
printf("%d\n", sizeof(union Un2));
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/91300.html