C语言----自定义类型:联合和枚举

2024-09-23 20:39:11 浏览数 (2)

1.联合体

联合体的特点

像结构体一样,联合体也是一个或者多个成员构成的,这些成员可以是不同的类型

联合体的关键字:union

结构体的关键字:struct

枚举的关键字:enum

但是编译器只为最⼤的成员分配⾜够的内存空间。联合体的特点是所有成员共⽤同⼀块内存空间。所

以联合体也叫:共⽤体。

代码语言:javascript复制
//struct S
//{
//    char c;
//    int i;
//
//};
//union Un
//{
//    char c;
//    int i;
//};
//int main()
//{
//    printf("%zdn", sizeof(struct S));//8
//    printf("%zdn", sizeof(union Un));//4
//
//    return 0;
//}
/*
但是编译器只为最⼤的成员分配⾜够的内存空间。
联合体的特点是所有成员共⽤同⼀块内存空间。所以联合体也叫:共⽤体。

联合体的特点是所有成员共⽤同⼀块内存空间,这样一个联合变量的大小,至少是最大成员的大小
(因为联合至少的有能力保存最大的那个成员)

那么为什么这里是4呢?

*/


//union Un
//{
//    char c;
//    int i;
//};
//int main()
//{
//    union Un un = { 0 };
//    printf("%zdn", sizeof(union Un));
//    printf("%pn", &un);//007EFDD4
把un里面的c和i的地址都打印出来
//    printf("%pn", &(un.c));//007EFDD4
//    printf("%pn", &(un.i));//007EFDD4
//    return 0;
//}
/*
取出的地址都是相同的
第一个字节是c,所有的4个字节都是i
所以我们发现i和c公用这4个字节的空间
所以联合体也叫共用体

我们可以发现,当我们用i的时候我们就不能用c
用c的时候就不能用i
因为改i的时候,c也改了

所以联合体成员在使用的时候,一次只能用一个成员,不能同时一起用

同一个时间只能用一个成员




对于结构体的话,c和i有各自的空间,但是对于联合体来说,成员共用空间
*/

union Un
{
    char c;
    int i;
};
int main()
{ 
    union Un un = { 0 };
    un.i = 0x11223344;
    un.c = 0x55;
    //经过调试我们不难发现随着c的改变,i也被改变了
    return 0;
}

联合体的特点是所有成员共⽤同⼀块内存空间。所以联合体也叫:共⽤体。

联合体大小的计算

联合的大小至少是最大成员的大小。

当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍

代码语言:javascript复制
//union Un
//{
//    char arr[5];//对齐数是1
//    //这个数组放在这里,跟放5个char类型是一样的
//
//    int i;//对齐数是4
//};
//int main()
//{
//    printf("%dn", sizeof(union Un));//8
//    return 0;
//}
/*
计算出的是8,所以我们得知联合体的大小不一定是最大成员的大小
联合体的大小至少是最大成员大大小


这个联合体最大对齐数是4
那么联合体的总大小一定要是4的倍数


这个联合体最大成员的大小是这个数组,大小是5,就是相当于5个char类型

但是5不是4的倍数,所以后面还要浪费3个字节,对齐8,所以最终的大小是8个字节


所以联合体也是存在内存空间的对齐的
*/


//练习计算联合体大小
union Un
{
    short arr[7];//对齐数是1
    //这个数组放在这里,跟放5个char类型是一样的

    int i;//对齐数是4
};
int main()
{
    printf("%dn", sizeof(union Un));//16
    return 0;
}


/*
因为shourt类型是2个字节,那么7个short就是14个字节了

short arr的对齐数是按照2来算的

i的对齐数是4,那么最大对齐数是4
那么联合体的大小必须是4的倍数


虽然说联合体很节省空间,但是也不是那么很绝对的节省空间
*/

联合体的运用

代码语言:javascript复制
/*
图书:库存量、价格、商品类型、书名、作者、⻚数
杯⼦:商品类型、价格、库存量设计、
衬衫:设计、可选颜⾊、可选尺⼨、库存量、价格、商品类型
*/

//struct gift_list
//{
//    //公共属性
//    int stock_number;//库存量
//    double price; //定价
//    int item_type;//商品类型
//
//    char title[20];//书名
//    char author[20];//作者
//    int num_pages;//⻚数
//
//    char design[30];//设计
//    int colors;//颜⾊
//    int sizes;//尺⼨
//};
//
/*
上述的结构其实设计的很简单,⽤起来也⽅便,但是结构的设计中包含了所有礼品的各种属性,这样
使得结构体的⼤⼩就会偏⼤,⽐较浪费内存。因为对于礼品兑换单中的商品来说,只有部分属性信息
是常⽤的。⽐如:
商品是图书,就不需要design、colors、sizes。
所以我们就可以把公共属性单独写出来,剩余属于各种商品本⾝的属性使⽤联合体起来,这样就可以
介绍所需的内存空间,⼀定程度上节省了内存。
*/


struct gift_list
{
    int stock_number;//库存量
    double price; //定价
    int item_type;//商品类型

    union {
        struct
        {
            char title[20];//书名
            char author[20];//作者
            int num_pages;//⻚数
        }book;
        struct
        {
            char design[30];//设计
        }mug;
        struct
        {
            char design[30];//设计
            int colors;//颜⾊
            int sizes;//尺⼨
        }shirt;
    }item;
};

//将我们每次就只用一个的东西拎出来放到联合体里面

//我们只用开辟一块空间,就能将所有东西存进去
//每次只取一样东西
//我们这个union没有写名字,写成匿名,因为这些成员我们每次用的时候只用一次

联合体的练习

代码语言:javascript复制
union Un
{
    char c;//第一个字节
    int i;
};
int main()
{
    union Un un = {0};
    un.i = 1;
    if (un.c == 1)
    {
        printf("小端n");
    }
    else
    {
        printf("大端n");
    }
    return 0;
}

2.枚举类型

枚举类型的声明

枚举顾名思义就是一一列举的意思

就是将可能的值一一列举出来

我们可以声明枚举类型

枚举的关键字是enum

代码语言:javascript复制
//struct A
//{
//    int _a : 2;
//    int _b : 5;
//    int _c : 10;
//    int _d : 30;
//};
//int main()
//{
//    struct A sa = { 0 };
//    //scanf("%d", &sa._b);//这是错误的
//
//    //正确的⽰范
//    int b = 0;
//    scanf("%d", &b);
//    sa._b = b;//直接进行赋值
//    return 0;
//}

enum Day//星期
{
    Mon,
    Tues,
    Wed,
    Thur,
    Fri,
    Sat,
    Sun
};

enum Sex//姓名
{
    //该枚举类型的三种取值
    //都是常量,被称为枚举常量
    MALE=2,
    FEMALE=4,//我们这里是给常量一个初始值,到后面就无法进行更改了
    SECRET=8
};

int main()
{
    //我们给枚举变量赋值的都是它的可能取值
    /*enum Sex sex1 = MALE;
    enum Sex sex2 = FEMALE;*/
    printf("%dn", MALE);//0
    printf("%dn", FEMALE);//1
    printf("%dn", SECRET);//2
    /*
    打印出来的值是0 1 2
    因为枚举常量的值默认是从0开始的,一次递增往下走,涨1
    */
    //如果我们希望这个值是我们期望的,我们可以在枚举类型中进行更改

    //假如我们仅仅只改变了第一个值为2
    //那么剩下两个值就是3 4


    //如果第1个值不赋值,第二个值赋值为8,那么打印出来的就是0 8 9
    //从我们设置的值进行递增,第一个值不设置的话默认就是0

    return 0;
}

枚举类型的优点

那么我们为什么使用枚举呢?

为什么使⽤枚举?

我们可以使⽤ #define 定义常量,为什么⾮要使⽤枚举?

枚举的优点:

  1. 增加代码的可读性和可维护性
  2. 和#define定义的标识符⽐较枚举有类型检查,更加严谨。
  3. 便于调试,预处理阶段会删除 #define 定义的符号
  4. 使⽤⽅便,⼀次可以定义多个常量
  5. 枚举常量是遵循作⽤域规则的,枚举声明在函数内,只能在函数内使⽤
代码语言:javascript复制
enum Sex//姓名
{

    MALE=2,
    FEMALE=4,
    SECRET=8
};

int main()
{
    enum Sex sex1 = MALE;//因为MALE的类型是enum Sex类型的,所以这么进行赋值是对的
    //enum Sex sex1 = 3;这么赋值就是错的,因为3的类型是整型,但是赋值的前面的枚举类型的
    //因为类型是不一样的,所以我们不能进行赋值
    return 0;
}

define定义的话是全局的定义的

枚举类型的使用

代码语言:javascript复制
enum Color//颜⾊
 {
   RED=1,
   GREEN=2,
   BLUE=4
 };

 enum Color clr = GREEN;//使⽤枚举常量给枚举变量赋值
代码语言:javascript复制
//写一个计算器---完成整数的加法、减法、乘法

enum Option
{
    EXIT,//默认的值是0
    ADD=1,//值表达的是1
    SUB,
    MULL,
    DIV

};
int Add(int x, int y)
{
    return x   y;
}
int Sub(int x, int y)
{
    return x - y;
}
int Mull(int x, int y)
{
    return x * y;
}
int Div(int x, int y)
{
    return x / y;
}
void menu()//菜单
{
    printf("**********************************n");
    printf("**********1.  add   2.  sub*******n");
    printf("**********3.  mull  4.  div*******n");
    printf("********* 0.    exit    **********n");
    printf("**********************************n");
}
int main()
{
    int input = 0,ret=0;
    int x, y;
    do
    {
        menu();
        printf("请选择一个算法");
        scanf("%d", &input);
        switch (input)
        {
        case ADD://这么写的话,ADD的值还是表达的1
            printf("请输入两个数");
            scanf("%d %d", &x, &y);
            ret=Add(x,y);
            printf("%dn", ret);
            break;
        case SUB://对于这种我们想写什么就写什么case ADD都是可以的:
            //我们是可以不用安排这个顺序的
            printf("请输入两个数");
            scanf("%d %d", &x, &y);
            ret = Sub(x, y);
            printf("%dn", ret);
            break;
        case MULL: 
            printf("请输入两个数");
            scanf("%d %d", &x, &y);
            ret= Mull(x, y);
            printf("%dn", ret);

            break;
        case DIV:
            printf("请输入两个数");
            scanf("%d %d", &x, &y);
            ret = Div(x, y);
            printf("%dn", ret);

            break;
        case 0:
            printf("退出n");
            break;
        default:
            printf("选择错误,重新选择n");
            break;
        }
    } while (input);
    return 0;
}

0 人点赞