⭐️ C语言进阶 ⭐️ 自定义类型:结构体(位段),枚举,联合(二)

简介: 深入掌握结构体,枚举,联合的使用和特点,以及学会明白位段


位段


什么是位段

位段的声明和结构是类似的


  • 有两个不同:
  1. 位段的成员必须是 int、unsigned int 或signed int
  2. 位段的成员名后边有一个冒号和一个数字


  • 示例:
struct A
{
 int _a:2;
 int _b:5;
 int _c:10;
 int _d:30;
};


位段的内存分配

说明:

  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;
};
struct S s = {0};
s.a = 10;
s.b = 12;
s.c = 3;
s.d = 4;


  • 对于上述空间是如何开辟的:


image.png


注:在vs平台下,其他平台不确定(可以自己进行测试)


  1. 首先对于char类型会开辟一个字节空间(8bite)
  2. 在这一个空间里先从高地址开始使用(从右边开始)
  3. 当存入的数据大于相应的空间(冒号后的数字表示属于该变量的空间大小,单位为bite)可以表示的数据大小时,会发生截断(从右边开始)
  4. 对于一个字节里还剩的空间,如果后面的位段空间能够放入时,将存入这一个字节里还剩的空间里
  5. 不够时将另开辟一个对应类型的空间来存放


位段的跨平台问题

存在问题:

  1. int 位段被当成有符号数还是无符号数是不确定的
  2. 位段中最大位的数目不能确定(例如:16位机器最大16,32位机器最大32,写成27,在16位机器会出问题
  3. 位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义
  4. 当一个结构包含两个位段,第二个位段成员比较大,无法容纳于第一个位段剩余的位时,是舍弃剩余的位还是利用,这是不确定的


结论:

跟结构相比,位段可以达到同样的效果,但是可以很好的节省空间,但是有跨平台的问题存在


枚举enum


定义:

枚举型是一个集合(可以一一列举的类似元素),其元素(枚举成员)是一些命名的整型常量(元素之间用逗号隔开)


枚举类型的定义

示例:


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


注意:

  1. 以上定义的 enum Day , enum Sex , enum Color 都是枚举类型
  2. {}中的内容是枚举类型的可能取值,也叫枚举常量
  3. 第一个枚举成员的默认值为整型的0,后续枚举成员的值在前一个成员上加1(可以人为设定枚举成员的值,从而自定义某个范围内的整数)


示例:


enum Color//颜色
{
 RED=1,
 GREEN=2,
 BLUE=4
};


枚举的优点

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


  • 示例:程序中为某些整数定义一个别名

预处理指令#define:


#define MON  1
#define TUE  2
#define WED  3
#define THU  4
#define FRI  5
#define SAT  6
#define SUN  7


枚举类型能完成同样的工作(更加简洁便捷):


enum DAY
{
      MON=1, TUE, WED, THU, FRI, SAT, SUN
};


总结枚举的优点:

  1. 增加代码的可读性和可维护性
  2. 和#define定义的标识符比较枚举有类型检查,更加严谨
  3. 防止了命名污染(封装)
  4. 便于调试
  5. 使用方便,一次可以定义多个常量


enum 与 #define 的区别


作用的时期:

#define是在预处理阶段直接进行替换,并且不进行类型检查,

枚举则是在程序运行之后才起作用


储存位置:

#define定义的类型存储在代码段


枚举常量存储在数据段的静态存储区里


赋值类型:

#define可以赋值多种类型数据

枚举变量的大小只能为整型数据(例如:0、1、2…)(enum当我们不主动对它进行赋值时,第一个枚举成员的默认值为整型的0,后续枚举成员的值在前一个成员上加1,#define则不会)


使用定义:

#define宏一次只能定义一个

枚举可以一次定义大量相关的常量


调试:

一般在编译器里,可以调试枚举常量,但是不能调试宏常量


定义类型:

枚举量具有类型,宏没有类型;枚举常量属于常量,宏定义不是常量


联合union

定义:

联合也称为共用体,很明显意思是多个变量共用一个空间,所以不能同一时间使用多个变量


联合类型的定义

示例:


//联合类型的声明
union Un
{
 char c;
 int i;
};
//联合变量的定义
union Un un;


联合的特点

  • 联合变量的大小:

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


  • 示例:


//在上述代码的基础上 计算联合变量的大小
printf("%d\n", sizeof(un));
//输出结果:4


  • 联合成员的空间使用:

任何成员变量都是从低地址开始使用


  • 示例:


union Un
{
 int i;
 char c;
};
union Un un;
// 下面输出的结果是一样的吗?
printf("%p\n", &(un.i));
printf("%p\n", &(un.c));
//相同 都是从联合变量的低地址开始使用
//下面输出的结果是什么?
un.i = 0x11223344;
un.c = 0x55;
printf("%x\n", un.i);
//0x11223355


union和大小端


  • 经典面试题:

判断当前计算机的大小端存储


#include<stdio.h>
union var{
        char c[4];
        int i;
};
int main(){
        union var data;
        data.c[0] = 0x04;//因为是char类型,值对应ascii
        data.c[1] = 0x03;//16进制便于直接与内存中的值对比
        data.c[2] = 0x02;
        data.c[3] = 0x01;
//数组先使用低地址再使用高地址,内存内容依次为:04,03,02,11(共四字节)
//而把四个字节作为一个整体,对于小端来说:低地址放在低权位
//读取出来则是:0x01020304
//反之则是大端存储模式
        printf("%x\n",data.i);//共用空间
}


联合大小的计算

  • 规则:
  1. 联合的大小至少是最大成员的大小
  2. 当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍


  • 示例:


union Un1
{
 char c[5];
 int i;
};
union Un2
{
 short c[7];
 int i;
};
//下面输出的结果是什么?
printf("%d\n", sizeof(union Un1));
//输出结果:8
printf("%d\n", sizeof(union Un2));
//输出结果:16


相关文章
|
25天前
|
存储 C语言
如何在 C 语言中实现结构体的深拷贝
在C语言中实现结构体的深拷贝,需要手动分配内存并逐个复制成员变量,确保新结构体与原结构体完全独立,避免浅拷贝导致的数据共享问题。具体方法包括使用 `malloc` 分配内存和 `memcpy` 或手动赋值。
33 10
|
25天前
|
安全 编译器 Linux
【c语言】轻松拿捏自定义类型
本文介绍了C语言中的三种自定义类型:结构体、联合体和枚举类型。结构体可以包含多个不同类型的成员,支持自引用和内存对齐。联合体的所有成员共享同一块内存,适用于判断机器的大小端。枚举类型用于列举固定值,增加代码的可读性和安全性。文中详细讲解了每种类型的声明、特点和使用方法,并提供了示例代码。
24 3
|
25天前
|
存储 大数据 编译器
C语言:结构体对齐规则
C语言中,结构体对齐规则是指编译器为了提高数据访问效率,会根据成员变量的类型对结构体中的成员进行内存对齐。通常遵循编译器默认的对齐方式或使用特定的对齐指令来优化结构体布局,以减少内存浪费并提升性能。
|
29天前
|
编译器 C语言
共用体和结构体在 C 语言中的优先级是怎样的
在C语言中,共用体(union)和结构体(struct)的优先级相同,它们都是用户自定义的数据类型,用于组合不同类型的数据。但是,共用体中的所有成员共享同一段内存,而结构体中的成员各自占用独立的内存空间。
|
29天前
|
存储 C语言
C语言:结构体与共用体的区别
C语言中,结构体(struct)和共用体(union)都用于组合不同类型的数据,但使用方式不同。结构体为每个成员分配独立的内存空间,而共用体的所有成员共享同一段内存,节省空间但需谨慎使用。
|
1月前
|
编译器 C语言 C++
C语言结构体
C语言结构体
26 5
|
1月前
|
C语言 C++
C语言 之 内存函数
C语言 之 内存函数
34 3
|
9天前
|
C语言
c语言调用的函数的声明
被调用的函数的声明: 一个函数调用另一个函数需具备的条件: 首先被调用的函数必须是已经存在的函数,即头文件中存在或已经定义过; 如果使用库函数,一般应该在本文件开头用#include命令将调用有关库函数时在所需要用到的信息“包含”到本文件中。.h文件是头文件所用的后缀。 如果使用用户自己定义的函数,而且该函数与使用它的函数在同一个文件中,一般还应该在主调函数中对被调用的函数做声明。 如果被调用的函数定义出现在主调函数之前可以不必声明。 如果已在所有函数定义之前,在函数的外部已做了函数声明,则在各个主调函数中不必多所调用的函数在做声明
26 6
|
29天前
|
存储 缓存 C语言
【c语言】简单的算术操作符、输入输出函数
本文介绍了C语言中的算术操作符、赋值操作符、单目操作符以及输入输出函数 `printf` 和 `scanf` 的基本用法。算术操作符包括加、减、乘、除和求余,其中除法和求余运算有特殊规则。赋值操作符用于给变量赋值,并支持复合赋值。单目操作符包括自增自减、正负号和强制类型转换。输入输出函数 `printf` 和 `scanf` 用于格式化输入和输出,支持多种占位符和格式控制。通过示例代码详细解释了这些操作符和函数的使用方法。
35 10
|
23天前
|
存储 算法 程序员
C语言:库函数
C语言的库函数是预定义的函数,用于执行常见的编程任务,如输入输出、字符串处理、数学运算等。使用库函数可以简化编程工作,提高开发效率。C标准库提供了丰富的函数,满足各种需求。