自定义类型--结构体、联合体、枚举类型

**Ladies and gentlemen**,今天,我们将来进行对自定义类型的学习!

在这里插入图片描述

目录

  • 1.结构的特殊声明
  • 2. 结构体内存对齐
    • 2.1 对齐规则
      • 2.1.1
      • 2.1.2
      • 2.1.3
      • 2.1.4
  • 2.2 为什么存在内存对齐?
    • 1. 平台原因 (移植原因):
    • 2. 性能原因:
  • 2.3 修改默认对齐数
  • 3. 结构体传参
  • 4. 结构体实现位段
    • 4.1 什么是位段
    • 4.2 位段的内存分配
    • 4.3 位段的跨平台问题
    • 4.4 位段的应用
    • 4.5 位段使用的注意事项
  • 5. 联合体
    • 5.1 联合体类型的声明
    • 5.2 联合体的特点
    • 5.3 相同成员的结构体和联合体对比
    • 5.4 联合体大小的计算
    • 5.5 联合的一个练习
  • 6. 枚举类型
    • 6.1 枚举类型的声明
    • 6.2 枚举类型的优点
    • 6.3 枚举类型的使用

1.结构的特殊声明

在声明结构的时候,可以不完全的声明。
比如:

//匿名结构体类型
struct
{
	int a;
	char b;
	float c;
}x;
struct
{
	int a;
	char b;
	float c;
}a[20], * p;

上⾯的两个结构在声明的时候省略掉了结构体标签(tag)。
那么问题来了?

//在上⾯代码的基础上,下⾯的代码合法吗?
p = &x;

警告:
编译器会把上面的两个声明当成完全不同的两个类型,所以是非法的。
匿名的结构体类型,如果没有对结构体类型重命名的话,基本上只能使用一次。

2. 结构体内存对齐

我们已经掌握了结构体的基本使⽤了。
现在我们深⼊讨论⼀个问题:计算结构体的大小。
这也是⼀个特别热⻔的考点: 结构体内存对齐

先看代码:

#include<stdio.h>
struct S
{
	char c1;//1
	int i;//4
	char c2;//1
};

int main()
{
	struct S s = { 0 };
	printf("%zd\n", sizeof(s));//size_t
	return 0;
}

结果是什么呢?是不是6呢?
在这里插入图片描述

在这里插入图片描述

是不是很惊喜,为什么呢,这就涉及到了结果体的内存对齐了,让我们往下了解了解他的规则吧!
在这里插入图片描述

2.1 对齐规则

⾸先得掌握结构体的对齐规则:

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

2.1.1

再把上面代码拿来分析分析:

#include<stdio.h>
struct S
{
	char c1;//1
	int i;//4
	char c2;//1
};

int main()
{
	struct S s = { 0 };
	printf("%zd\n", sizeof(s));//size_t
	return 0;
}

由第一个条件我们可以知道,结构体的第一个成员对齐到和结构体变量起始位置偏移量为0的地址处,而字符型变量c1只占一字节,从下图可知,绿色部分就是为c1分配的空间

第二个条件: 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
对齐数 = 编译器默认的⼀个对齐数 与 该成员变量大小的较小值。(小编所用的VS编译器中的默认对齐数为8)(若无默认对齐数,对齐数就是成员自身的大小)

i是int类型变量,占4个字节,而VS编译器默认对齐数为8,取较小值即为4。由条件二可知,我们需要将该变量对齐到对齐数(4)的整数倍地址处(简单来讲,就是偏移量为4的倍数即可),所以在接下来的空间找到偏移量为4的倍数即可,即图中黄色区域部分

c2是字符型变量,只占一个字节,由条件二得,对齐数是1,任何数都是1的倍数,所以再接下去的空间即偏移量为8的位置即可为c2分配空间,即图中蓝色区域部分

到这里,每个成员变量就分配好了空间,但还不够

由条件3得: 结构体总大小最大对齐数(结构体中每个成员变量都有⼀个对齐数,所有对齐数中最大的)的整数倍
在该代码中,最大对齐数为4,此时9不是最大对齐数4的整数倍,往下扩大,最终结构体总大小为12个字节(浪费了6个字节)

在这里插入图片描述

2.1.2

经过上面的了解,下面这个代码结果又是什么呢?

#include<stdio.h>
struct S2
{
	char c1;
	char c2;
	int i;
};
int main()
{
	struct S2 s2 = { 0 };
	printf("%zd\n", sizeof(s2));//size_t
	return 0;
}

同样的,结构体的第一个成员对齐到和结构体变量起始位置偏移量为0的地址处
第一个成员c1为字符型变量,占一个字节,图中绿色部分即为c1的空间

第二个成员c2为字符型变量,占一个字节,对齐数为1,图中蓝色区域

第三个成员i为整型变量,占4个字节,对齐数为4,需对齐到对齐数的整数倍地址处(相当于偏移量为4的倍数即可),即图中黄色区域

切记,还有最后一步,结构体总大小应为成员变量中最大对齐数的整数倍
由图可知,该结构体占8字节,为4的整数倍,刚好!!
在这里插入图片描述

2.1.3

再来!

#include<stdio.h>
struct S3
{
	double d;//8 8 8
	char c;  //1 8 1
	int i;   //4 8 4
};
int main()
{
	struct S3 s3 = { 0 };

	printf("%zd\n", sizeof(s3));//size_t

	return 0;
}

由规则可知,结构体的第一个成员对齐到和结构体变量起始位置偏移量为0的地址处
第一个成员d为double型变量,占8个字节,从偏移量为0处往下开辟8个字节给d,即图中绿色区域

第二个成员c为字符型变量,占1个字节,对齐数为1,任何数都是1的倍数,即图中蓝色区域

第三个成员i为整型变量,占4个字节,对齐数为4,需对齐到对齐数的整数倍地址处(相当于偏移量为4的倍数即可),即图中黄色区域

最后,由图可知,总占16个字节,最大对齐数为4,刚好为4的倍数
在这里插入图片描述

2.1.4

最后一个规则:

#include<stdio.h>
struct S3
{
	double d;//8 8 8
	char c;  //1 8 1
	int i;   //4 8 4
};

struct S4
{
	char c1;
	struct S3 s3;
	double d;
};
int main()
{
	struct S3 s3 = { 0 };
	struct S4 s4 = { 0 };
	printf("%zd\n", sizeof(s4));//size_t
	return 0;
}

如果嵌套了结构体的情况,嵌套的结构体成员对齐到自己的成员中最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体中成员的对齐数)的整数倍。

还是那些规则:结构体的第一个成员对齐到和结构体变量起始位置偏移量为0的地址处
第一个成员c1是字符型变量,占1个字节,为其开辟一个字节空间,即蓝色区域
第二个成员是嵌套的结构体,按规则4可知,该结构体需对齐到其结构体成员中最大对齐数(8)的整数倍处,往下找,即偏移量为8的位置处,由前面代码结果我们知道,结构体S3大小为16个字节,所以往下为其开辟16个字节的空间,即绿色区域

第三个成员d是double型变量,占8个字节,对齐数为8,需对齐到对齐数的整数倍地址处(相当于偏移量为8的倍数即可),刚好接下来的偏移量为24处即为8的倍数,往下开辟8个字节空间,即图中黄色区域

最后,结构体的整体大小就是所有最大对齐数(含嵌套结构体中成员的对齐数)的整数倍。
由图可知,总共占32个字节,最大对齐数为8,刚好为8的倍数

在这里插入图片描述在这里插入图片描述

2.2 为什么存在内存对齐?

大部分的参考资料都是这样说的:

1. 平台原因 (移植原因):

不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据( 比如地址为8的整数倍),否则抛出硬件异常。

2. 性能原因:

数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。假设⼀个处理器总是从内存中取8个字节,则地址必须是8的倍数。如果我们能保证将所有的double类型的数据的地址都对齐成8的倍数,那么就可以用一个内存操作来读或者写值了。否则,我们可能需要执行两次内存访问,因为对象可能被分放在两个8字节内存块中。

字节对齐是为了提高CPU访问数据的效率。32位字长的计算机,其CPU一次可以读写的数据长度是4个字节,64位计算机则一次可以读取8个字节。对于char型数组,只占据一个字节,小于计算机一次读取的字节数,所以无论其元素放在什么地址,都可以一次读取到

从图中所指位置开始访问,在32位机器上,一次访问4个字节,在对齐情况下,第一次就能把c的值取出,紧接着,再访问4个字节,就把i一次性取了出来

在不对齐的情况下,仍从图中所指位置开始访问,当我们要读取i的数据时,第一次访问只拿到了i的前3个字节,需要再往后读取4个字节,得到i剩余的字节,两次读取内容拼接才能得到i的值

所以在不对齐的情况下,我们可能需要读取两次才能把i的值取到,而对齐的情况下,我们只需读取一次就能把i的值取出
在这里插入图片描述

总体来说:结构体的内存对齐是拿空间来换取时间的做法。

那在设计结构体的时候,我们既要满足对齐,又要节省空间,如何做到?
---->让占用空间小的成员尽量集中在一起

//例如:
struct S1
{
	char c1;
	int i;
	char c2;
};
struct S2
{
	char c1;
	char c2;
	int i;
};

S1 和 S2 类型的成员⼀模⼀样,但是 S1 和 S2 所占空间的大小有了⼀些区别。
上面中的成员变量一致,只是位置发生了变化
其中,S1所占空间大小为12个字节,S2所占空间大小为8个字节
结论:把占用空间较小的成员集中在一起,可以减少空间的浪费

2.3 修改默认对齐数

#pragma 这个预处理指令,可以改变编译器的默认对齐数。

//一般设置默认对齐数都设为2的次方次
#include <stdio.h>
#pragma pack(1)//设置默认对⻬数为1
//设默认对齐数为1后会连着存放
struct S
{
	char c1;
	int i;
	char c2;
};
#pragma pack()//取消设置的对⻬数,还原为默认
int main()
{
	//输出的结果是什么?
	printf("%d\n", sizeof(struct S));
	return 0;
}
//虽然设置默认对齐数为1可以达到减少空间的浪费(这种做法相当于不对齐情况),但也不一定满足我们的所需,需根据实际情况来调整

使用情况:结构体在对齐方式不合适的时候,我们可以自己更改默认对齐数。

3. 结构体传参

struct S
{
	int data[1000];
	int num;
};
struct S s = { {1,2,3,4}, 1000 };
//结构体传参
void print1(struct S s)
{
	printf("%d\n", s.num);
}
//结构体地址传参
void print2(struct S* ps)
{
	printf("%d\n", ps->num);
}
int main()
{
	print1(s); //传结构体
	print2(&s); //传地址
	return 0;
}

上面的 print1 和 print2 函数哪个好些?
答案是:首选print2函数。

原因:
函数传参的时候,参数是需要压栈,会有时间和空间上的系统开销。(需要创建新空间存放结构体中的成员变量,并将这些变量一 一传过去,浪费了空间,也浪费了时间,而用指针,指针变量大小为4/8字节)
如果传递⼀个结构体对象的时候,结构体过大,参数压栈的的系统开销比较大,所以会导致性能的下降。
结论:结构体传参的时候,要传结构体的地址。

4. 结构体实现位段

结构体讲完就得讲讲结构体实现 位段 的能力。

4.1 什么是位段

位段的声明和结构是类似的,有两个不同:

  1. 位段的成员必须是 int、unsigned int 或signed int (char类型本质上也可以是int类型,因为char是根据ACLII码值存储的),在C99中位段成员的类型也可以选择其他类型。
  2. 位段的成员名后边有⼀个冒号和⼀个数字。

比如:

//位段式的结构
struct S
{
	int _a:2;
	int _b:5;
	int _c:10;
	int _d:30;
};
int main()
{
	printf("%zd\n", sizeof(struct S));
	return 0;
}

在这里插入图片描述

那么,在使用位段式结构的情况下,这个位段占几个字节呢?
上面总和为47个bit位,一个字节8个bit位,那么,6个字节是否就够了?
实则不然,这就涉及到了另一个知识点 ----位段的内存分配

4.2 位段的内存分配

  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;
    printf("%zd\n", sizeof(s));
    return 0;
}
//空间是如何开辟的?

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

int main()
{
  unsigned char puc[4];
  struct tagPIM
  {
    unsigned char ucPim1;
    unsigned char ucData0 : 1;
    unsigned char ucData1 : 2;
    unsigned char ucData2 : 3;
  }*pstPimData;
  pstPimData = (struct tagPIM*)puc;
  memset(puc,0,4);
  pstPimData->ucPim1 = 2; 
  pstPimData->ucData0 = 3;
  pstPimData->ucData1 = 4;
  pstPimData->ucData2 = 5;
  printf("%02x %02x %02x %02x\n",puc[0], puc[1], puc[2], puc[3]);
  return 0;
}

分析如下:
在这里插入图片描述
在这里插入图片描述

4.3 位段的跨平台问题

  1. int 位段被当成有符号数还是无符号数是不确定的。
  2. 位段中最大位的数目不能确定。(16位机器最⼤16,32位机器最⼤32,写成27,在16位机器会出问题。
  3. 位段中的成员在内存中从左向右分配,还是从右向左分配,标准尚未定义。
  4. 当一个结构包含两个位段,第二个位段成员比较大,无法容纳于第一个位段剩余的位时,是舍弃剩余的位还是利用,这是不确定的。
    总结
    跟结构体相⽐,位段可以达到同样的效果,并且可以很好的节省空间,但是有跨平台的问题存在。

4.4 位段的应用

下图是⽹络协议中,IP数据报的格式,我们可以看到其中很多的属性只需要几个bit位就能描述,这里使用位段,能够实现想要的效果,也节省了空间,这样网络传输的数据报大小也会较小一些,对网络的畅通是有帮助的。
在这里插入图片描述

4.5 位段使用的注意事项

位段的几个成员共有同⼀个字节,这样有些成员的起始位置并不是某个字节的起始位置,那么这些位置处是没有地址的。内存中每个字节分配⼀个地址,⼀个字节内部的bit位是没有地址的。所以不能对位段的成员使⽤&操作符,这样就不能使⽤scanf直接给位段的成员输入值,只能是先输入放在⼀个变量中,然后赋值给位段的成员。(尽量在使用位段时使位段成员类型一致)
比如上面提到的:
在这里插入图片描述
在该图中我们不难看出,成员a、b、c、d都不在每个字节的起始地址处,所以我们无法获取其地址为其用scanf来输入值

下面是一个例子:

#include<stdio.h>

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;
}

OK!到这里结构体的补充就暂告一段落!让我们进一步学习另外的自定义类型吧!
在这里插入图片描述

5. 联合体

5.1 联合体类型的声明

像结构体一样,联合体也是由一个或者多个成员构成,这些成员可以不同的类型。
但是编译器只为最大的成员分配足够的内存空间。联合体的特点是所有成员共用同⼀块内存空间。所以联合体也叫:共用体。
给联合体其中一个成员赋值,其他成员的值也跟着变化。

联合体的关键字为union
定义联合体类型变量的一般形式为:

union 共用体名
{
	成员表列;
}变量表列;
//可以将类型声明与变量定义分开

可以对联合体变量初始化,但初始化表中只能有一个常量。如下:

union Data
{
	int i;
	char ch;
	float f;
}a = {1,'a',1.5};	//不能初始化3个成员,他们占用同一段存储单元
union Data a = { 16 };		//正确,对第一个成员初始化
union Data a = { .ch='j'};		//C99允许对指定的一个成员初始化
#include <stdio.h>
//联合类型的声明
union Un
{
	char c;
	int i;
};
int main()
{
	//联合变量的定义
	union Un un = { 0 };
	printf("%d\n", sizeof(un));
	return 0;
}

运行结果为:
在这里插入图片描述

为什么是4呢?

5.2 联合体的特点

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

//代码1
#include <stdio.h>
//联合类型的声明
union Un
{
	char c;
	int i;
};
int main()
{
	//联合变量的定义
	union Un un = { 0 };
	// 下⾯输出的结果是⼀样的吗?
	printf("%p\n", &(un.i));
	printf("%p\n", &(un.c));
	printf("%p\n", &un);
	return 0;
}

运行结果如下:
在这里插入图片描述

//代码2
#include <stdio.h>
//联合类型的声明
union Un
{
	char c;
	int i;
};
int main()
{
	//联合变量的定义
	union Un un = { 0 };
	un.i = 0x11223344;
	un.c = 0x55;
	printf("%x\n", un.i);
	return 0;
}

运行结果为:
在这里插入图片描述
代码1输出的三个地址⼀模⼀样,代码2的输出,我们发现将i的第4个字节的内容修改为55了。我们仔细分析就可以画出,un的内存布局图。
在这里插入图片描述

5.3 相同成员的结构体和联合体对比

我们再对比一下相同成员的结构体和联合体的内存布局情况。
在这里插入图片描述
在这里插入图片描述

5.4 联合体大小的计算

• 联合的大小至少是最大成员的大小。
• 当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍。


#include <stdio.h>
union Un1
{
	char c[5];//相当于放了5个char类型变量,所以对齐数为1
	int i;//自身大小为4,VS默认对齐数为8,较小值为4,所以对齐数为4
};
union Un2
{
	short c[7];
	int i;
};
int main()
{
	//下⾯输出的结果是什么?
	printf("%d\n", sizeof(union Un1));
	printf("%d\n", sizeof(union Un2));
	return 0;
}

上述代码中:
Un1中最大成员的大小为5个字节,系统为他分配了5个字节,Un2中最大成员的大小为4个字节(short类型占2字节,7个就是14个字节),系统为该联合体分配14个字节,结果是不是就是5、14呢?
运行结果如下:
在这里插入图片描述
为什么呢?
这就涉及到了另一条规则:当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍。
Un1中最大对齐数为4,而5不是4的整数倍,最终结果为8
Un2中最大对齐数为4,而14不是4的整数倍,最终结果为16

使用联合体是可以节省空间的,举例:
比如,我们要搞⼀个活动,要上线⼀个礼品兑换单,礼品兑换单中有三种商品:图书、杯子、衬衫。
每⼀种商品都有:库存量、价格、商品类型和商品类型相关的其他信息。
图书:书名、作者、页数
杯子:设计
衬衫:设计、可选颜色、可选尺寸
那我们不耐心思考,直接写出⼀下结构:

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;
};

5.5 联合的一个练习

写⼀个程序,判断当前机器是大端?还是小端?
在前面学习大小端的时候我们已经写过了这个程序,今天就用联合体来写这个代码:

#include<stdio.h>
int check_sys()
{
	union
	{
		int i;
		char c;
	}un;
	un.i = 1;
	return un.c;//返回1是⼩端,返回0是⼤端
}
int main()
{
	int ret = check();
	if (ret == 1)
		printf("小端\n");
	else
		printf("大端\n");
}

#include<stdio.h>
int main()
{
  union
  {
    short k;
    char i[2];
  }*s, a;
  s = &a;
  s->i[0] = 0x39;
  s->i[1] = 0x38;
  printf("%x\n", a.k);
  return 0;
}

这个联合体有两个成员:一个 short 类型的 k 和一个 char 类型的数组 i[2]。
最大成员大小为2字节,符合前述两条规则;

s = &a; 这行代码将共用体 a 的地址赋给指针 s。
s->i[0] = 0x39; 这行代码将 0x39(十六进制)转换为 char 类型并存储在联合体 a 的 i[0] 位置。在内存中,这通常位于联合体的起始位置。
s->i[1] = 0x38; 这行代码将 0x38(十六进制)转换为 char 类型并存储在联合体 a 的 i[1] 位置。在内存中,这通常位于 i[0] 之后的地址。

k与字符数组i共用2个字节空间,所以改变数组i元素的值的同时也改变了k的值
所以k的高位为0x38,低位为0x39,最终结果为3839
在这里插入图片描述

6. 枚举类型

6.1 枚举类型的声明

枚举顾名思义就是⼀⼀列举。
把可能的取值⼀⼀列举。
比如我们现实生活中:

一周的星期一到星期日是有限的7天,可以⼀⼀列举
性别有:男、女、保密,也可以⼀⼀列举
⽉份有12个月,也可以⼀⼀列举
三原色,也是可以意义列举

这些数据的表示就可以使用枚举了。

enum Day//星期
{
	Mon,
	Tues,
	Wed,
	Thur,
	Fri,
	Sat,
	Sun
};
enum Sex//性别
{
	MALE,
	FEMALE,
	SECRET
};
enum Color//颜⾊
{
	RED,
	GREEN,
	BLUE
};

以上定义的 enum Day , enum Sex , enum Color 都是枚举类型。
{}中的内容是枚举类型的可能取值,也叫 枚举常量
这些可能取值都是有值的,默认从0开始,依次递增1,当然在声明枚举类型的时候也可以赋初值。

enum Color//颜⾊
{
	RED = 2,
	GREEN = 4,
	BLUE = 8
};

6.2 枚举类型的优点

为什么使用枚举?
我们可以使用 #define 定义常量,为什么非要使用枚举?
枚举的优点:

  1. 增加代码的可读性和可维护性
  2. 和#define定义的标识符比较枚举有类型检查,更加严谨。(C++具有类型检查)
  3. 便于调试,预处理阶段会删除 #define 定义的符号
  4. 使用方便,⼀次可以定义多个常量
  5. 枚举常量是遵循作用域规则的,枚举声明在函数内,只能在函数内使用
#define MALE 0
#define FEMALE 1
#define SECRET 2


int main()
{
	int sex = MALE;

	return 0;
}
在预处理的时候,程序中所以MALE都会替换成0,表面上看它还是MALE,实际已经被替换为0了,在前面讲解typedf与defind的区别时讲解过

6.3 枚举类型的使用

void menu()
{
	printf("**************************\n");
	printf("****  1. add    2.sub ****\n");
	printf("****  3. mul    4.div ****\n");
	printf("****  0. exit         ****\n");
	printf("**************************\n");
}

enum Option
{
	EXIT,//0
	ADD,//1
	SUB,//2
	MUL,//3
	DIV//4
};


int main()
{
	int input = 0;
	printf("请选择:>");
	scanf("%d", &input);
	switch (input)
	{
	case ADD:
		break;
	case DIV:
		break;
	case MUL :
		break;
	case SUB:
		break;
	case EXIT:
		break;
	default:
		break;
	}
	return 0;
}
enum Color//颜⾊
{
 RED=1,
 GREEN=2,
 BLUE=4
};
enum Color clr = GREEN;//使⽤枚举常量给枚举变量赋值
enum enum Color clr = 0;//error,会发生错误(c++),如下

那是否可以拿整数给枚举变量赋值呢?在C语言中是可以的,C语言中的枚举没有强类型检查,即你可以为枚举成员赋予任何整数值,包括负数;但是在C++是不行的,C++提供了对枚举成员的类型检查
如下:一边是整型,一边是枚举类型(还有一点:C语言中,枚举类型在内存中存储为整数

在这里插入图片描述

OK!万字阐述到此结束!感谢各位观看,欢迎下次再来!
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/476537.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

这个简单的生活方式,为你带来满满的幸福感

在今天文章的开头&#xff0c;我想请你思考一个问题&#xff1a;影响幸福感的最大因素是什么&#xff1f; 不妨先想一想&#xff0c;再往下拉&#xff0c;继续阅读。 可能不少朋友的回答&#xff0c;会是财富、事业、理想、生活环境、社会地位…… 这些因素当然对幸福感都非常重…

Python正则表达式之模式修正符,你get了吗?

​大家好&#xff0c;今天我要和大家分享一个Python编程中的神秘武器——正则表达式模式修正符&#xff01;正则表达式&#xff0c;对于很多编程新手来说&#xff0c;可能是一个头疼的问题。但别担心&#xff0c;模式修正符就像是你手中的魔法棒&#xff0c;让你的正则表达式更…

3.21系统栈、数据结构栈、栈的基本操作、队列、队列的基本操作------------》

栈 先进后出、后进先出 一、系统栈 大小&#xff1a;8MB 1、局部变量 2、未经初始化为随机值 3、代码执行到变量定义时为变量开辟空间 4、当变量的作用域结束时回收空间 5、函数的形参和返回值 6、函数的调用关系、保护现场和恢复现场 7、栈的增长方向&#xff0c;自高…

C语言例:设 int x; 则表达式 (x=4*5,x*5),x+25 的值

代码如下&#xff1a; #include<stdio.h> int main(void) {int x,m;m ((x4*5,x*5),x25);printf("(x4*5,x*5),x25 %d\n",m);//x4*520//x*5100//x2545return 0; } 结果如下&#xff1a;

必学干货!使用Python正则表达式匹配多个字符

1.匹配多个字符 今天我们来聊一聊正则表达式中一个很强大的功能&#xff1a;匹配多个字符&#xff01;正则表达式是一个非常强大的工具&#xff0c;可以帮助我们轻松地处理和匹配字符串。通过使用不同的符号和技巧&#xff0c;我们可以匹配多个字符&#xff0c;从而更加灵活地…

【智能算法】海洋捕食者算法(MPA)原理及实现

目录 1.背景2.算法原理2.1算法思想2.2算法过程 3.结果展示4.参考文献 1.背景 2020年&#xff0c;Afshin Faramarzi 等人受到海洋生物适者生存启发&#xff0c;提出了海洋捕食者算法(Marine Predators Algorithm&#xff0c;MPA)。 2.算法原理 2.1算法思想 MPA根据模拟自然界…

WSL2的安装步骤

WSL2&#xff08;Windows Subsystem for Linux 2&#xff09;是微软公司开发的一项创新性技术&#xff0c;它在Windows操作系统上提供了一个完整的Linux内核&#xff0c;并允许用户在Windows环境中运行Linux发行版。之前想在Windows上使用Linux系统必须先安装VirtualBox或VMWar…

做跨境用哪种代理IP比较好?怎么选到干净的IP?

代理IP对于做跨境的小伙伴来说&#xff0c;都是必不可少的工具&#xff0c;目前出海的玩法已经是多种多样&#xff0c;开店、账号注册、短视频运营、直播带货、网站SEO等等都是跨境人需要涉及到的业务。而国外代理IP的获取渠道非常多&#xff0c;那么做跨境到底应该用哪种代理I…

一、rv1126开发之视频输入和视频编码

RV1126 H264/HEVC编码流程 一、RV1126编码的流程图&#xff1a; 二、每个代码模块详细讲解 2.1. VI模块的创建 VI模块的初始化&#xff1a;关键在于VI_CHN_ATTR_S结构体&#xff0c;这个结构体是VI设置的结构体。这个结构体的成员变量包括&#xff1a;pcVideoNode&#xff0…

【系统架构设计师】计算机系统基础知识 03

系统架构设计师 - 系列文章目录 01 系统工程与信息系统基础 02 软件架构设计 03 计算机系统基础知识 文章目录 系统架构设计师 - 系列文章目录 文章目录 前言 一、计算机系统概述 1.计算机组成 ​编辑2.存储系统 二、操作系统 ★★★★ 1.进程管理 2.存储管理 1.页式存储 …

python --- 练习题3

目录 1、猜数字游戏&#xff08;使用random模块完成&#xff09; &#xff1a;继上期题目&#xff0c;附加 2、用户登录注册案例 3、求50~150之间的质数是那些&#xff1f; 4、打印输出标准水仙花数&#xff0c;输出这些水仙花数 5、验证:任意一个大于9的整数减去它的各位…

[环境配置].ssh文件夹权限修改方法

问题描述&#xff1a; 通过VSCode中的Remote Explorer或者通过CMD等命令行窗口连接远程机器时&#xff0c;会因为提示 "Bad owner or permissions on C:\\Users\\xxx/.ssh/config"而导致失败&#xff0c;最终呈现在VSCode中的效果是&#xff0c;弹窗提示"Could…

群晖Cloud Sync数据同步到百度云、另一台群晖、nextcloud教程

群晖Cloud Sync数据同步到百度云、另一台群晖、nextcloud教程 为了更好的浏览体验&#xff0c;欢迎光顾勤奋的凯尔森同学个人博客http://www.huerpu.cc:7000 一、群晖套件中下载Cloud Sync 二、同步到百度云盘 打开Cloud Sync&#xff0c;点击左上角的号&#xff0c;云供应商…

归并算法详细解析

归并排序 1945年&#xff0c;约翰冯诺依曼&#xff08;John von Neumann&#xff09;发明了归并排序&#xff0c;这是典型的分治算法的应用。归并排序&#xff08;Merge sort&#xff09;是建立在归并操作上的一种有效的排序算法&#xff0c;该算法是采用分治法&#xff08;Di…

[Semi-笔记] 2023_TIP

目录 概要一&#xff1a;Conservative-Progressive Collaborative Learning&#xff08;保守渐进式协作学习&#xff09;挑战&#xff1a;解决&#xff1a; 二&#xff1a;Pseudo Label Determination for Disagreement&#xff08;伪标签分歧判定&#xff09;挑战&#xff1a;…

华工考研复试模板

华工考研复试PPT模板 前言PPT概览PPT章节展示 最后的最后 前言 前段时间&#xff0c;有考研的学弟学妹咨询选导师的相关事项。可能也有的学弟学妹在准备复试相关的PPT&#xff0c; 这里小编我打算这几天DIY一个模板&#xff0c;主要其实还是跟自己之前夏令营的答辩模板和奖学金…

ResNet《Deep Residual Learning for Image Recognition》

ResNet论文学习 引言Deep Residual Learning 深度残差学习Residual Learning 残差学习Identity Mapping by Shortcuts 通过捷径来恒等映射网络结构Plain NetworkResidual Network实现细节 实验总结代码复现Building blockBottleneckResnet 18Resnet 34Resnet 50 引言 深度网络…

23.合并两个有序链表

将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 示例 1&#xff1a; 输入&#xff1a;l1 [1,2,4], l2 [1,3,4] 输出&#xff1a;[1,1,2,3,4,4]示例 2&#xff1a; 输入&#xff1a;l1 [], l2 [] 输出&#xff1a;[]示…

【计算机考研】杭电 vs 浙工大 怎么选?

想求稳上岸的话&#xff0c;其他几所学校也可以考虑&#xff0c;以留在本地工作的角度考虑&#xff0c;这几所学校都能满足你的需求。 如果之后想谋求一份好工作&#xff0c;肯定优先杭电是比较稳的&#xff0c;当然复习的时候也得加把劲。 这个也可以酌情考虑&#xff0c;报…

TikTok小店运营经验分享,美国本土小店怎么做?

作为资深跨境老玩家&#xff0c;虽不说是经验丰富&#xff0c;至少也是摸清了基本的玩法思路。TikTok作为近来的跨境新蓝海&#xff0c;他的玩法其实并不难&#xff0c;作为第一批试错玩家&#xff0c;今天也诚心给大家分享一些美国本土小店运营经验&#xff0c;感兴趣的话就看…