C语言 --- 自定义类型-结构体、枚举

文章详细介绍了C语言中的结构体,包括声明、匿名结构体的使用、自引用示例以及结构体变量的定义和初始化。还讨论了结构体的内存对齐规则,并给出了节省空间的技巧。此外,文章提到了位段的特性以及枚举类型的使用,强调了枚举相对于#define的优点。
摘要由CSDN通过智能技术生成

一、结构体

1、结构的声明

struct tag//结构体类型名
{
    member-list;//结构体成员
}variable-list;//结构体变量

例如写一个描述学生的结构体

struct Student
{
    char name[10];//姓名
    int age;//年龄
    char ID[10];//学号
};//注意这里的分号不能丢

还有一些特殊的声明方式---匿名结构体,不给tag,如下

struct
{
    int a;
    char b;
}x1;

上诉匿名结构体只能在声明之后立即声明变量,不然无法使用

下面,我们来看看下面的代码是否正确

struct
{
    int a;
    char b;
}x1;//创建一个变量


struct
{
    int a;
    char b;
}*p;//创建了一个结构体指针

int main()
{
    p=&x1;
    return 0;
}

这样做是不被允许的,编译器会认为这是两个独立的结构体,类型不匹配,无法赋值

2、结构体的自引用(自己引用自己)

数据结构的链表就是结构体的自引用实现的

struct Node
{
    int data;
    struct Node*next;
};

那么我们可不可以这样写?

typedef struct Node
{
    int data;
    Node*next;
}Node;

编辑器从上到下依次执行代码,struct Node还没有被重命名之前就已经被使用,故是不行的

3、结构体变量的定义和初始化

首先我们要明白一点,结构体是一种类型,和int,char一样,那么变量的定义也是同样的道理

struct Stu
{
    char name[10];
    int age;
    int num;
}s1;//全局变量----除了这种特殊的方式,其他的都和int、char什么的一样

struct Stu s2;//全局变量

int main()
{
    struct Stu s3;//局部变量
    return 0;
};

那么有了变量我们应该如何初始化呢

struct Stu
{
    char name[10];
    int age;
    int num;
}s1;

int main()
{
    struct Stu s1={"zhangsan",40,1};//该方法顺序要与结构体成员的顺序相同
    struct Stu s2={.age=20,.num=2,.name="lisi"};//该方法可以任意顺序
    return 0;
}

4、结构体内存对齐

我们都知道,基本数据类型都是有大小的,比如int是4字节,char是1字节,float是4字节等等,那么结构体类型的大小是几个字节呢?

下面举个例子来说明一下---结构体是如何计算大小的

struct S
{
    char a;
    int b;
    char c;
};

int main()
{
    printf("%d",sizeof(struct S));
    return 0;
}

很多人会认为该结构体的大小就是1个int的大小加上2个char的大小共6个字节,那就太想当然了

要想计算结构体大小,首先我们要了解结构体的对齐规则:

  1. 第一个成员在与结构体变量偏移量为0的地址处。
  2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。(VS中默认的值为8)
  3. 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。
  4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍

了解完规则,我们就可以试着来解释一下上诉代码结果

 注意:默认对齐数是可以修改的

#pragma pack(1)//默认对齐数设置为1
struct S
{
    char a;
    int b;
    char c;
};
#pragma pack()//恢复默认对齐数

int main()
{
    printf("%d", sizeof(struct S));
    return 0;
}

那么,结构体为什么要对齐

1.考虑到移植性,不是每种硬件设备都能访问任意地址的

2.考虑到性能,处理器可以更快的读取数据

节省空间的小技巧:可以将较小的类型放在一起,比如刚刚的结构体可以写成

struct S
{
    char a;
    char c;
    int b;
};//大小是8字节

提示:结构体的传参尽量传地址,防止结构体太大,占用的空间太多

二、位段

相信很多人都只知道段位,而没有听过位段

其实位段和结构体很相似,有两个不同:
1.位段的成员必须是 int、unsigned int 或signed int 。
2.位段的成员名后边有一个冒号和一个数字。

下面我写一个位段,给你们看看

struct S
{
    int _a:1;
    int _b:3;
    int _c:10;
    int _d:30;
};

数字代表该成员变量占有几个bit位(1个字节是8个bit位)

由于位段的一些内存方面的标准,C语言标准也没有明确的指示,故不同的编译器有不同的分配内存方法,这里就不细讲了,没什么普适性。

三、枚举类型

enum Sex
{
    MALE,//默认为0
    FEMALE,//1
    SERCRET//2
};



enum Week
{
    MON,//0
    TES,//1
    WES,//2
    THUR=5,//5
    FRI,//6
    SAT,//7
    SUN//8
};
int main()
{
    enum Sex s = SERCRET;
    printf("%d\n",MALE);//0
    printf("%d\n",FEMALE);//1
    printf("%d\n",MON);//0
    printf("%d\n",THUR);//5
    printf("%d\n",SUN);//8
    return 0;
}

注意:枚举类型的大小是4个字节

相对于#define枚举的优点:

  • 增加代码的可读性和可维护性
  • 枚举是一种类型,更加的严谨
  • enum更加方便,一次可以定义多个常量
  • 防止了命名污染
  • 方便调试
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值