C语言入门篇——自定义数据篇

2023-05-16

目录

1、结构体

1.2、匿名结构体 

1.3、结构体的自引用

1.4、结构体的声明和初始化 

1.5、结构体的内存对齐

1.6、修改默认对齐数

1.7、结构体传参

2、枚举

3、共用体(联合体)


1、结构体

 设计程序时,最重要的步骤之一是选择表示数据的方法。在许多情况下,简单变量甚至是数组还不够。为此,C提供了结构变量提高你表示数据的能力,它能让你创造新的形式。

结构是一些值的集合,这些值称为成员变量。结构的每个成员可以是不同的变量,其中的成员可以是标量、数组、指针、基本数据类型甚至是其他结构体例如学生这个结构体。学生有姓名,性别,成绩,班别等。

struct student
{
    char name[20];
    char sex;
    int class_num;
    float scorse;
}

1.2、匿名结构体 

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

struct
{
	int a;
	char b;
	float c;
}x;
struct
{
	int a;
	char b;
	float c;
}*p;

上面两个匿名结构体在声明的时候省略了结构体标签。当我们声明用一个结构体指针指向一个类型一样的匿名结构体变量时,会发生问题吗?貌似是没问题的吧,但是编译器会把上面两个声明当成完全不同的两个类型,所以是非法的。

1.3、结构体的自引用

上面我们提到结构体的成员变量里面可以有结构体,如果我定义的结构体中包含一个类型为该结构体本身的成员貌似也是可以的吧,初学者容易写出下面的代码:

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

但是我们通过运行我们的程序会得出程序报错的结果:

 正确的自引用的代码应该是:

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

但是我们有的时候会使用typedef这个关键字给自定义数据起一个别名,如果在自引用中使用别名将会导致编译器无法识别结构体中的成员变量,程序将无法成功编译。

1.4、结构体的声明和初始化 

在声明我们自定义的结构体类型后,我们就可以通过像基本数据类型一样定义和初始化结构体变量:

结构体类型 结构体变量名;

结构体类型 结构体变量名 = { 初始化值 };

1.5、结构体的内存对齐

在考试题和面试题中,我们常常考到结构体大小的计算问题,这里包含着一个特别热门的考点:结构体内存对齐。这里介绍几条结构体的对齐规则:

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

下面通过几个代码案例来掌握这几条规则:

//练习1
struct S1
{
	char c1;
	int i;
	char c2;
};

//练习2
struct S2
{
	char c1;
	char c2;
	int i;
};

//练习3
struct S3
{
	double d;
	char c;
	int i;
};

//练习4-结构体嵌套问题
struct S4
{
	char c1;
	struct S3 s3;
	double d;
};

和自己心目中的答案是一样的吗?下面通过图解来讲解一下下面几个案例:

 

 

为什么存在内存对齐呢?

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

1. 平台原因(移植原因): 不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特 定类型的数据,否则抛出硬件异常。

2. 性能原因: 数据结构(尤其是栈)应该尽可能地在自然边界上对齐。 原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。

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

在设计结构体的时候,既要满足对齐,又要节省空间,就要让占用空间小的成员变量尽量集中在一起。这样就对空间和时间都有了保障。

1.6、修改默认对齐数

在vs2022中,我们可以通过#pragma这个预处理指令,可以改变我们的默认对齐数。

#pragma pack(8)//设置默认对齐数为8
struct S1
{
    char c1;
    int i;
    char c2;
};
#pragma pack()//取消设置的默认对齐数,还原为默认

#pragma pack(1)//设置默认对齐数为1
struct S2
{
    char c1;
    int i;
    char c2;
};
#pragma pack()//取消设置的默认对齐数,还原为默认

 发现当我们修改默认对齐数时,两个成员变量类型相同的结构体的大小不一样。当结构体在对齐方式不合适的时候,我们就可以通过#pragma这个预处理指令自己更改默认对齐数。

1.7、结构体传参

当我们使用函数传参的时候,可以传结构体变量或者结构体指针。这两种方法都是可以的,但我们一般优先选结构体指针。

原因很简单,参数是需要压栈的,会有时间和空间上的系统开销。如果传递一个结构体对象的是时候,结构体过大,参数压栈的系统开销比较大,所以会导致性能的下降。所以在变量和指针的选择上优先使用结构体指针。

2、枚举

可以用枚举类型声明符号名称来表示整型常量。使 用enum关键字,可以创建一个新“类型”并指定它可具有的值(实际上,enum 常量是int类型,因此,只要能使用int类型的地方就可以使用枚举类型)。枚举类型的目的是提高程序的可读性。它的语法与结构的语法相同。

枚举格式:

enum enumerate {枚举符1,枚举符2,...,枚举符N};

enum enumerate {枚举符1=值1,枚举符2=值2,...,枚举符N};

虽然枚举符是int类型,但是枚举变量可以是任意整数类 型,前提是该整数类型可以储存枚举常量。C语言是允许枚举变量使用++运算符。

默认情况下,枚举列表中的常量都被赋予0、1、2等。在枚举声明中,可以为枚举常量指定整数值:

enum spectrum {red, orange=100, yellow};

如果只给一个枚举常量赋值,没有对后面的枚举常量赋值,那么后面的常量会被赋予后续的值。

枚举的优点:

1. 增加代码的可读性和可维护性

2. 和#define定义的标识符比较枚举有类型检查,更加严谨。

3. 防止了命名污染(封装)

4. 便于调试

5. 使用方便,一次可以定义多个常量

枚举常用于switch语句中,充当case标签后面的表达式。

3、共用体(联合体)

共用体(联合)也是一种特殊的自定义类型,这种类型定义的变量也包含一系列的成员,特征是这些成员公用同一块空间。联合的成员是共用同一块内存空间的,这样一个联合变量的大小,至少是最大成员的大小(因为联 合至少得有能力保存最大的那个成员)。,例如:

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

有一道有趣的面试题:判断当前计算机的大小端存储

什么是大端 / 小端?
大端(存储)模式,是指数据的低位保存在内存的高地址中,而数据的高位保存在内存的低地址中
小端(存储)模式,是指数据的低位保存在内存的低地址中,而数据的高位保存在内存的高地址中

为什么会有大小端之分呢?
因为在计算机系统中,我们以字节为存储单元,每个地址单元都对应着一个字节,一个字节为8bit。而在C语言中,不仅仅是一个字节来存储一个数据,除了一个字节的char,还有两个字节的short,四个字节的int等等(看具体编译器)。另外,对于位数大于8位的处理器,例如32位的处理器,由于寄存器的宽度大于一个字节,那么就有如何将多个字节进行排布的问题,于是就出现了大小端的问题。

这里介绍利用联合体求大小端的思路:一个成员是多字节,一个是单字节,给多字节的成员赋一个最低一个字节不为0,其他字节为0 的值,再用第二个成员来判断,如果第二个字节不为0,就是小端,若为0,就是大端。

//联合类型的声明
union Un
{
	int i;
	char c;

};
//联合变量的定义
union Un un;

int main(void)
{
	//printf("%d\n", sizeof(un));
	union Un u;
	u.i = 1;

	if (u.c == 1)
	{
		printf("小端\n");
	}
	else
	{
		printf("大端\n");
	}

	return 0;
}

联合体大小计算规则:

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

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

C语言入门篇——自定义数据篇 的相关文章

  • c++调用CSerial 库函数进行串口发送

    毕设做的东西要用到这个 请学弟帮忙收集了下 自己也做个整理 完整实验项目下载 https download csdn net download a897180673 10310065 用到的硬件 1 ch340 土豪金模块 2 arduin
  • 网络---字节序

    字节序 xff1a xff43 xff50 xff55 对内存中数据以字节为单位进行存取的顺序 主机字节序分为 xff1a 大端字节序 xff1a 低地址存高位 小端字节序 xff1a 低地址存低位 地址指内存地址 xff1b 在内存中 x
  • mmdetection ---转onnx模型,Netron可视化网络结构

    详细信息可以看官方文档 xff1a docs en tutorials pytorch2onnx md 这里把命令摘了出来 用法 span class token comment bash span python tools span cl
  • 链路层--->ETH(以太网)协议

    文章目录 ETH xff08 以太网 xff09 协议格式 xff1a ARP协议格式 链路层负责相邻设备之间的数据帧传输 xff0c 典型协议有 xff1a ETHH xff08 以太网协议 xff09 xff0c ARP协议 MTU x
  • BFS练手题目

    文章目录 1 员工的重要性2 腐烂的橘子3 N 叉树的层序遍历4 单词接龙5 最小基因变化6 打开转盘锁 广度优先搜索 xff08 BFS xff09 算法 xff0c 概念就不说啥了 xff0c 常用来求最短路径 xff0c 最少步数等
  • 回溯算法练习题

    回溯是一个常见的算法 xff0c 类似于深搜 广搜 xff0c 会穷举每一个可能 但是会有一个恢复选择的操作 算法核心框架如下 xff1a span class token keyword for span 选择 in 选择列表 xff1a
  • ACM输入输出练习--字符串分割

    ACM输入输出练习 学会即可举一反三 xff0c 主要针对字符串类型分割处理 这里利用getline 和字符串流来分割字符串并格式化输出 xff0c 思路大概如此 span class token macro property span c
  • Spark与hive集成、Hive On Spark 、使用Spark SQL进行数据查询配置流程

    本文主要是介绍在开源hadoop上使用Spark SQL进行数据查询 有关本文的各组件版本如下 xff1a 1 hadoop版本 span class token namespace root 64 hadoop01 span span c
  • 虚拟机网络配置中的几个相关文件

    1 cd etc sysconfig network scripts 目录下的 ifcfg eno 文件 2 more etc hosts 3 more etc hostname 问题记录 Vmware有三种网络连接模式 xff1a 桥接
  • DB2实现判断字符串是否只含数字

    背景 取出客户表中客户姓名字段含数字且只含数字的数据 最开始考虑的是使用正则表达式函数 xff0c 后来发现DB2没有像Oracle一样可以直接使用的正则表达式函数 xff0c 因此考虑使用其他方法 结论 使用DB2的translate函数
  • 华为ELK的几个知识点

    1 ELK是运行在FusionInsight HD平台中的 安装ELK之前必须先安装FusionInsight HD集群 2 ELK依赖FusionInsight HD中的两个组件 xff0c 分别是HDFS和Yarn 3 ELK必须部署在
  • Python 中获取字典的key列表和value列表

    coding utf 8 定义一个字典 dic 61 39 剧情 39 11 39 犯罪 39 10 39 动作 39 8 39 爱情 39 3 39 喜剧 39 2 39 冒险 39 2 39 悬疑 39 2 39 惊悚 39 2 39
  • su oracle 和 su - oracle的区别

    最近整oracle xff0c 发现su oracle过来sqlplus一直报命令不存在 后来发现是因为用su oracle切换的 xff0c 导致还是用的root的环境变量 xff0c 所以才会导致sqlplus命令不存在 xff0c 改
  • 关于Oracle 11g的RAC和Oracle 19c 的RAC在JDBC连接时的一些区别

    19c中新增的 v services可以查询各PDB对应的服务名 xff0c 根据此服务名去写JDBC的连接参数 而非19c中常用的v database视图显示的是CDB的库名 还有 show paramerter service name
  • ORA-31626 ORA-01658 使用impdp遇到的问题

    oracle使用impdp导库时遇到的问题 xff0c span class token punctuation span oracle span class token variable 64 qsrac2 span span class
  • linux安装oracle客户端——SQL*Loader

    背景 在安装Oracle数据库的时候 xff0c 一般是默认安装客户端的 但是有些特殊情况 xff0c 需要在应用服务器上安装客户端 xff0c 用于执行一些特殊操作 xff0c 此时需要安装oracle的客户端 xff0c 如使用sqll

随机推荐

  • 如何获取oracle的dmp文件中的表空间名称或Schema

    场景 在给定的dmp下 xff0c 使用impdp导入时 xff0c 报了一个错 xff0c 大致就是说schema在dmp中不存在 xff08 使用impdp导入时指定了schemas 61 XXX XXX XXX xff09 当时懒得去
  • 搭建Hadoop最少需要几个节点

    可以按服务所需的最小节点数进行规划 zookeeper服务 zookeeper服务最少需要3个节点 xff0c 且扩展时需为奇数个才行 HDFS HDFS中的NameNode需要2个节点 xff0c 主备配置 因此hadoop最小需要3个节
  • “远程“操作oracle数据泵impdp、expdp导入导出

    关键词 xff1a NFS 数据泵 impdp expdp oracle客户端 本文解决的主要问题 靠考如下场景 xff0c 你作为一个DBA xff0c 管理者测试环境的Oracle集群 正常情况下测试环境恢复生产数据都是由DBA来做 x
  • 数仓拉链表的缺点

    在选定拉链表时由于对于哪些表适合做拉链表没有一个统一的规范的认识 xff0c 因此出现了以下情况 xff1a 一个表是做的全量拉链表 xff0c 但是没有注意该表数据不是每天都有供数 即 xff0c 可能某一天源系统供给了该表 xff0c
  • oracle监听、启动等命令

    记录一些常用的查看状态和重启数据库的命令 监听 单机版一般为lsnrctl xff0c 集群一般为crsctl lsnrctl Listener Control 在数据库单机环境下使用lsnrctl命令 lsnrctl status 查看状
  • 多进程的python实现

    span class token keyword import span os span class token keyword import span time span class token comment os fork 负责创建一
  • Python三目运算符(三元运算符)用法详解

    我们从一个具体的例子切入本节内容 假设现在有两个数字 xff0c 我们希望获得其中较大的一个 xff0c 那么可以使用 if else 语句 xff0c 例如 xff1a if a gt b max 61 a else max 61 b 但
  • du -sh 和ls -lh的区别

    du sh显示的是文件占用的大小 ls lh显示的文件的实际大小 这里系统层面涉及一个Block Size的概念 xff0c 具体不深究 简而言之 xff0c 假如一个Block是4K xff0c 如果文件A的大小是1K xff0c 那么用
  • docker镜像创建、删除等相关操作

    一 docker镜像的形式 可以为一个tar包 xff0c 如 centos tar 此处为一个现成的镜像 使用方法为 1 加载镜像 span class token punctuation span root 64 hadoop01 sp
  • shell中的数组、循环等基本用法和注意事项

    shell中数组的表示 方法 xff1a array name 61 ele1 ele2 ele3 elen 举例 xff1a span class token punctuation span root 64 hadoop01 span
  • 离线安装rpm包

    离线安装rpm包 安装 repotrack 工具下载依赖包其他常用命令 安装 repotrack 工具 找一台在线的机器 xff08 虚拟机 xff09 xff0c 配置好yum源 span class token punctuation
  • 更改yum源

    Error Failed to download metadata for repo appstream Cannot prepare internal mirrorlist No URLs in mirrorlist 参考连接
  • 在docker中使用sqlplus

    1 找个带sqlplus的镜像 从docker hub上下载https hub docker com r sflyr sqlplus docker pull sflyr sqlplus 2 在k8s中运行 由于该镜像启动后没有运行的程序 x
  • C++常用库函数

    C 43 43 常用库函数 1 常用数学函数 头文件 include lt math gt 或者 include lt math h gt 函数原型 功能 返回值 int abs int x 求整数x 的绝对值 绝对值 double aco
  • 基于GEC6818的触摸屏

    1 输入子系统 连接操作系统的输入设备 xff0c 可不止一种 xff0c 也许是一个标准PS 2键盘 xff0c 也许是一个USB鼠标 xff0c 或者是一块触摸屏 xff0c 甚至是一个游戏机摇杆 xff0c Linux在处理这些纷繁各
  • c语言实现udp广播和组播

    目录 1 UDP广播通信 2 UDP组播通信 1 UDP广播通信 单播 xff1a 数据包发送方式只有一个接受方 广播 xff1a 同时发给局域网中的所有主机 只有用户数据报套接字 xff08 使用UDP协议 xff09 才能广播 以192
  • Odoo10 中常见的 Widget 整理

    Widget是什么 是odoo中字段的显示形式 Odoo内置的widget widget 61 34 mail thread 34 xff1a 消息标签 widget 61 34 html 34 xff1a html相关标签 widget
  • C语言入门篇——介绍篇

    目录 1 什么是C语言 1 C语言的优点 3 语言标准 4 使用C语言的步骤 5 第一个C语言程序 6 关键字 1 什么是C语言 1972年 xff0c 贝尔实验室的丹尼斯 里奇和肯 汤普逊在开发UNIX操作系统时设计了C语言 xff0c
  • 力扣刷题——双数之和

    很多人去力扣刷题都是数组的第一题 xff0c 也就是双数之和 xff0c 相信这也是很多人劝退题目 xff0c 甚至对自己学过的知识产生了怀疑 xff0c 这真的是我学完C语言 xff0c Java xff0c Python或C 43 43
  • C语言入门篇——自定义数据篇

    目录 1 结构体 1 2 匿名结构体 1 3 结构体的自引用 1 4 结构体的声明和初始化 1 5 结构体的内存对齐 1 6 修改默认对齐数 1 7 结构体传参 2 枚举 3 共用体 xff08 联合体 xff09 1 结构体 设计程序时