C语言自定义类型一网打尽(结构体、位段/位域、枚举、联合体)

2023-11-12

前言

C语言自定义类型有:结构体、枚举、联合体
内置类型有:int、char、long、double、short、float等。

结构体-struct

结构体声明

结构体 - 描述一个学生 名字,年龄,电话,性别
定义 下面s1,s3为结构体全局变量。 struct Stu s2 = { "张三",20,"15129521207","男" }; s2就是正常局部变量的创建及初始化。

struct Stu
{
	char name[20];
	short age;
	char tele[12];
	char sex[5];
}s1, s3;

起别名:需要使用关键字typedef。下面就是将结构体Stu起了一个S的别名。之后就可以用别名代替结构体名。S s1;

typedef struct Stu
{
	char name[20];
	short age;
	char tele[12];
	char sex[5];
}S;

匿名结构体

匿名结构体 只能·使用一次。编译器会将下面俩个当成不同结构体。

// 匿名结构体类型
struct
{
	int a;
	float b;
}x;

// 匿名结构体指针
struct
{
	int a;
	float b;
}*p;

初始化

普通结构体题初始化

struct Stu
{
	char c;
	int a;
	double d;
	char arr[20];
};
int main()
{
	struct Stu s = { 'c', 12, 3.6, "林夕" };
	printf("%c %d %f %s\n", s.c, s.a, s.d, s.arr);
}

嵌套结构体类型

struct Tea
{
	double weight;
	short age;
};

struct Stu
{
	char c;
	struct Tea st;
	int a;
	double d;
	char arr[20];
};

int main()
{
	struct Stu s = { 'c',{120.6, 22}, 22, 5.6, "林夕" };
	printf("%c %f %hd %d %f %s\n", s.c, s.st.weight, s.st.age, s.a, s.d, s.arr);
}

结构体变量成员修改

#include <stdio.h>
#include <string.h>
struct Book
{
    char name[20];
    short price;
};
 
int main()
{
    struct Book b1 = { "C++", 51 };
    strcpy(b1.name, "c"); // 结构体变量成员修改
    b1.price = 34; // 结构体变量成员修改
 
    printf("%s %d\n", b1.name, b1.price);
    printf("%u\n", sizeof(b1));//22
    return 0;
}


结构体内存对齐

结构体对齐规则
• 第一个成员在与结构体变量偏移量为0的地址处
• 其他成员变量要对其到某个数字(对齐数)的整数倍的地址处。

对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值
• VS中默认的值为8(gcc无默认对齐数,就按照该成员的大小)
• 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍
• 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。

关于字节对齐:
• 结构体各成员的起始位置相对于结构体变量的起始位置的偏移量,应该为该结构体成员类型所占字节数与pack(n)的n取最小值的倍数
• 结构体变量所占字节数应该是结构体各成员所占字节数的最大值与pack(n)的n取最小值

例子

#include <stdio.h>
 
struct S1
{
    char c1;
    int a;
    char c2;
};
 
struct S2
{
    char c1;
    char c2;
    int a;
};
int main()
{
    struct S1 s1 = { 0 };
    printf("%u\n", sizeof(s1));//12
    struct S2 s2 = { 0 };
    printf("%u\n", sizeof(s2));//8
    return 0;
}


为什么存在内存对齐?
1、平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某个地址处取某些特定类型的数据,否则抛出硬件异常。(比如整型只能在4的倍数取出数据)
2、性能原因:数据结构(尤其是栈)应该尽可能的在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作俩次内存访问;而对齐的内存访问仅需要一次访问

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

在这里插入图片描述
修改默认对齐
#pragma pack(8) 设置默认对齐数为8
#pragma pack() 取消默认对齐数,还原为默认

计算结构体所占字节数

练习1:

struct S1
{
char c1;
int a;
char c2;
};

分析:
最开始是c1字符类型,直接放
a是整型,4字节与8字节相比,4小。所以要从地址4的倍数开始,与前面空三个,
c2是字符型,4字节与8字节相比,1小,所以从地址1的倍数就是放,紧挨着上面放
总大小是最大对齐数的倍数 最大对齐数为4 ,现在已经占了9个字节,所以总的大小就是12个字节。在下面又空三个字节
在这里插入图片描述
练习2:

struct S2
{
	char c1;
	char c2;
	int a;
};

分析:c1占一个字节,c2也占一个字节,a是四个字节,所以先跨过俩个字节,然后占四个字节。
在这里插入图片描述
练习3:

struct S2
{
	char c1;
	char c2;
	double a;
};

分析:首先c1从开始占一个字节
c2 也是一个字节 紧挨上面 也占一个字节
a占8个字节,中间空六个字节,然后又占八个字节,
总大小是最大对齐数, 最大对齐数为:8 刚好,所以就是16字节

练习4:

struct S3
{
	char c1;
	char c2;
	double a;
};

struct S4
{
	char c1;
	struct S3 s3;
	double d;
};

分析:首先c1占一个字节,
s3是一个结构体,该结构体内最大对齐数是8,与vs8的字节相比,就是8。所以先空7个字节,然后占用16个字节
d是8个字节,前面刚好是8的倍数24,再占8个字节
总大小是最大对齐数=8,前面共32 刚好是32.

结构体传参

原理:传值用 . 传地址用->
例如下面这张图:
在这里插入图片描述
问题:上面函数那个好一点?
答:首选2,因为传参需要压入栈中,如果传递一个结构体对象的时候,结构体过大,参数压栈的系统开销较大,所以会导致性能的下降。而传入指针是固定的4/8个字节

offsetof结构体偏移量

函数原型:size_t offsetof(strcut Name,memberName);
头文件:#include<stddef.h>
看下面代码即懂。(不要在意头文件的位置)
在这里插入图片描述

位段/位域

位段的声明和结构体是类似的,有俩个不同:
• 位段的成员必须是int、unsigned int 或 signed int。(char也行)
• 位段的成员名后面有一个冒号和一个数字。(int类型数字不能大于4*8 = 32)

位段的内存分配
1、位段的成员可以是int ,unsigned int ,signed int(有符号整形) 或者是 char(属于整形家族)类型
2、位段的空间上是按照需要以4个字节(int)或者一个字节(char)的方式来开辟的。
3、位段涉及很多不确定因素,段位是不跨平台,注重可移植的程序应该避免使用位段。
例子:
在这里插入图片描述
内存结构:
在这里插入图片描述
位段的跨平台问题
1、int位段被当成有符号数还是无符号数是不确定的
2、位段中最大位的数目不能确定。(16位机器最大16,32位机器最大32)
3、位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义。
4、当一个结构体含俩个位段,第二个位段成员比较大,无法容纳于第一个位段剩余的位时,是舍弃剩余的位还是利用,这是不确定的。

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

位段的应用
在这里插入图片描述

枚举

枚举类型的定义

enum Sex
{
	MALE = 2,
	FEMALE = 4,
	SECRET = 8,
};

优点如下:
1、增加代码的可读性和可维护性
2、和#define定义的标识符比较枚举有类型检查,更加严谨
3、防止了命名污染(封装)
4、便与调试(define会在预编译就处理好了。)
5、方便使用,一次可以定义多个常量

枚举的使用
错误
在这里插入图片描述

联合(共用体)

定义:联合也是一种特殊的自定义类型,这种类型定义的变量也包含一系列的成员、特征是这些成员公用同一块空间

在这里插入图片描述

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

面试题:判断当前计算机的大小端存储。
方法一:
在这里插入图片描述

方法二:
在这里插入图片描述
联合大小的计算
• 联合的大小至少是最大成员的大小。
• 当最大成员大小不是最大对齐数的整数倍时,就要对齐到最大对齐数的整数倍
比如:

在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C语言自定义类型一网打尽(结构体、位段/位域、枚举、联合体) 的相关文章

随机推荐

  • 数组名和指针变量的区别

    数组名和指针变量的区别 今天讲一下数组名和指针变量的区别 只说结果 不过分析 刚刚看到C语言中 有一个sizeof 数组名 和sizeof 指针变量 平时没有注意这两者的区别 在这里记下笔记 不喜勿喷 sizeof 数组名 是计算该数组所占
  • 南方日报:前有杜比,后有DTS,DVD专利费纷争何时休?

    那些所谓 收费太贵 只是一小部分厂商的借口 事实上使用我们产品的客户很多 但大多数没有合理付费 迪提斯 公司全球总裁兼首席执行官 庄柯奇 如是回答本报记者的发问 作为拥有全球最著名数码音频技术之一 几乎与杜比齐名的数码技术公司的掌门人 庄柯
  • python 十进制转十六进制 蓝桥

    试题 基础练习 十进制转十六进制 问题描述 十六进制数是在程序设计时经常要使用到的一种整数的表示方式 它有0 1 2 3 4 5 6 7 8 9 A B C D E F共16个符号 分别表示十进制数的0至15 十六进制的计数方法是满16进1
  • JDBC连接MySQL数据库步骤

    JDBC全称 Java DataBase Connectivity 是Java数据库连接 是一种用于执行SQL语句的Java API 可以为多种关系数据库提供统一访问 例如 MySQL Oracle SQLServer等数据库 它由一组用J
  • Shell命令行选项与参数用法详解

    在bash中 可以用以下三种方式来处理命令行参数 直接处理 使用 1 2 n进行解析 适合小脚本 getopts 单个字符选项的情况 如 n 10 f file txt等选项 能处理绝大多数的情况 getopt 可以处理单个字符选项 也可以
  • mysql系列之一_MySQL学习系列之一---MySQL简介

    大家好 我是小詹小詹 是一名IT工程师 工作内容主要是ERP软件的二次开发和实施 以及数据库的运维 在这里可以分享一些数据库的基础操作以及工作过程中碰到实际问题的解决方案 整个系列会按照数据库内容分为三大块 分别是MySQL SQL ser
  • 电子学会 全国青少年软件编程等级考试标准 十级标准及相关资料

    本级会涉及但不限于随机算法 近似算法 神经网络算法 深度学习算法 强化学习算法 以及算法复杂性分析等更高级或新兴的算法设计及分析方法 随机算法 路径规划算法C 实现 二 A 路径规划算法C 实现 二 A weixin 44504228的博客
  • java sshd实现连接ssh操作

    文章目录 说明 分享 记录 导包 代码 总结 说明 本博客每周五更新一次 日常使用ssh连接工具是jsch实现 但该库从2018年开始停止更新 项目开发中使用免密登录功能时 因为ssh加密算法版本过低失败 最后不得不使用账号密码连接 那次后
  • 面试官问我MySQL索引失效怎么排查?懵逼了。。。

    前 言 上一期 我们讲解了sql优化的一般流程 不管是优化join语句 where语句 聚合函数还是排序操作 核心在于利用索引来优化sql语句 但是 大家以为我们为字段创建了索引之后 索引就一定会生效吗 当然不是的 因为索引可能会失效 那索
  • TensorFlow框架做深度学习之命令行参数操作

    想必在linux系统上做开发的小伙伴们对命令行这种东西是熟悉的不能再熟悉了 linux系统时时刻刻都要通过命令行实现各种操作 但是作为资深的windows用户 我是不太习惯用cmd命令控制台去操作应用的 回到今天的主题 为了使我们用Tens
  • Xamarin.Android DatePickerFragment 日期控件

    MainActivity 代码 public class MainActivity Activity TextView dateDisplay Button dateSelectButton protected override void
  • 服务器显示ipv4有两个ip地址,服务器显示ipv4有两个ip地址

    服务器显示ipv4有两个ip地址 内容精选 换一换 查询子网 您可以在API Explorer中直接运行调试该接口 GET v1 project id subnets subnet id 参数说明请参见表1 参数说明名称是否必选说明proj
  • CLI 发行uni-app到微信小程序,如何不打开微信开发者工具去进行小程序发布?

    前言 小程 赶紧把这个UI这里改一下 改完赶紧推个版本 等着测试 哎小程 这个需求客户需要这样改一下 你赶紧改一下推个版本客户等着看 上面这段话相信做开发的同僚或多或少每天都在听 可是小程序的版本发布流程并不像华为云 阿里云等其他云服务器
  • cuda warp内原语——vote instructions&warp instructions

    warp内原语 shfl sync shfl up sync shfl down sync and shfl xor sync 可在warp级别交换线程的变量 写在开头 shfl shfl up shfl down and shfl xor
  • Excel VLOOKUP 初学者教程:通过示例学习

    目录 前言 一 VLOOKUP的用法 二 应用VLOOKUP的步骤 三 VLOOKUP用于近似匹配 四 在同一个表里放置不同的VLOOKUP函数 结论 前言 Vlookup V 代表 垂直 是 excel 中的内置函数 允许在 excel
  • vue监听浏览器窗口的变化,随着窗口变化调整里面table的宽高

    1 在data中设置 1 tableHeight 500 2 screenHeight window innerHeight 2 在mounted中设置 mounted const that this window onresize gt
  • 路由追踪tracert命令

    路由追踪tracert命令Tracert 跟踪路由 是路由跟踪实用程序 用于确定 IP 数据报访问目标所采取的路径 Tracert 命令用 IP 生存时间 TTL 字段和 ICMP 错误消息来确定从一个主机到网络上其他主机的路由 这点和pi
  • springboot整合redis使用介绍

    文章目录 系列文章目录 Springboot集成Netty Springboot集成Rabbitmq Springboot集成Retry springboot集成websocket Springboot集成Redis springboot整
  • Flink_02_算子(个人总结)

    声明 1 本文为我的个人复习总结 并非那种从零基础开始普及知识 内容详细全面 言辞官方的文章 2 由于是个人总结 所以用最精简的话语来写文章 3 若有错误不当之处 请指出 Flink程序的组成结构 Source 数据输入 Transform
  • C语言自定义类型一网打尽(结构体、位段/位域、枚举、联合体)

    前言 C语言自定义类型有 结构体 枚举 联合体 内置类型有 int char long double short float等 结构体 struct 结构体声明 结构体 描述一个学生 名字 年龄 电话 性别 定义 下面s1 s3为结构体全局