广告
返回顶部
首页 > 资讯 > 后端开发 > 其他教程 >最新C语言自定义类型详解
  • 718
分享到

最新C语言自定义类型详解

C语言 自定义类型C自定义类型详解 2023-01-13 12:01:18 718人浏览 安东尼
摘要

目录前言结构体结构体的基础知识结构和数组的区别结构体的声明结构体的特殊声明 结构体的自引用我们能不能结构体套结构体呢?那么怎么实现结构体自引用呢?结构体变量的定义和初始化我

前言

随着学习的进一步深入,我们不再满足于做简单的计算题,想着做一些像样的东西,如学生管理系统、酒店入住系统等等......而C语言提供的基础数据类型已经不足以支撑我们的想法,于是便要学习自定义类型,根据自己的需求来创建类型。

结构体

生活当中有很多物品是不能简单的用整型、浮点型、字符型来区分,它们常常是复杂的集合,比如人,一个人拥有年龄,身高、体重、学历......等信息。我们可以用结构体来实现准确描述人这种复杂集合。

结构体的基础知识

结构和数组的区别

结构是一些值的集合,这些值称为成员变量,每个成员变量可以是不同类型。而数组是同类型值得集合

结构体的声明

对于结构体的声明,需要有结构体名、成员列表、变量列表(可省略)。

struct tag
{
	member_list;//成员列表
}variable_list;//变量列表

我们以描述一个学生为例。

struct student
{
	int id;//学号
	char name[20];//姓名
	char sex;//性别
	int age;//年龄
}s1,s2,s3;

结构体的特殊声明 

在声明结构体的时候,我们可以以不完全声明(匿名结构体类型),也就是省略掉结构体的标签。

就像这样:

struct
{
	int a;
	char b;
	float c;
}d;

但这种声明并不安全

例如: 

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

我们令*p=&d 是非法的。因为编译器会将这两个声明当成完全不同的两个类型。

结构体的自引用

我们能不能结构体套结构体呢?

是可以的,比如我们描述一辆车的构造,它的成员变量分别是车门、轮子、发动机、玻璃......等,而这些成员也是复杂的集合,比如车门的内部也是复杂的,这就需要结构体套结构体。我们称为结构体的自引用。

那么怎么实现结构体自引用呢?

先看一段错误示范:

我们结构体变量的创建,是要在结构体变量声明过后,这种写法属于是在这个结构体类型还没有创建完成的时候就使用了该类型的结构体变量。

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

  

正确示例:

将该类型的结构体变量改成该类型的结构体指针就能解决这一问题了~。

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

结构体变量的定义和初始化

我们尝试一下初始化上面的学生结构体变量。

#include<stdio.h>
struct student
{
	int id;//学号
	char name[20];//姓名
	char sex;//性别
	int age;//年龄
}s1, s2, s3;
int main()
{
	s1 = { 1,"aa",'M',18 };
	printf("%d %s %c %d", s1.id, s1.name, s1.sex, s1.age);
}

是不是发现和数组的初始化差不多? 定义一个结构体变量和定义一个整型变量的方式差不多,这里就不列举了。

结构体内存对齐 

我们已经掌握了结构体的基本使用,现在我们进入到下一个问题——结构体的大小怎么计算?

猜想:结构体的大小就是结构体内的成员大小加起来

还是以学生结构体类型为例:

struct student
{
	int id;//学号
	char name[20];//姓名
	char sex;//性别
	int age;//年龄
}s1,s2,s3;

我们按照猜想一计算一下,计算出该结构体类型大小为29个字节,事实果真如此?

用代码验证一下:

#include<stdio.h>
struct student
{
	int id;//学号
	char name[20];//姓名
	char sex;//性别
	int age;//年龄
}s1, s2, s3;
int main()
{
	printf("%d", sizeof(struct student));
}

我们发现,结果是32个字节,说明猜想一错误。

结构体的对齐规则

一、第一个成员在于结构体变量偏移量为0的地址处。

二、其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。(ps:对齐数=编译器默认的一个对齐数与改成员大小的较小值,vs编译器的默认对齐数为8)

三、结构体总大小为最大对齐数的整数倍。

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

对结构体对齐规则的解析

id变量便是学生结构体变量的第一个成员,它对齐的位置应该在于该结构体变量的地址偏移量为0处,又因为id是int类型,占4个字节。

看图,红色所占空间就是id所占空间:

 第二个成员name变量每个元素所占大小为1个字节,与默认值对齐数8个字节相比,1<8,所以name要对齐到1的整数倍数处,也就是4的位置,又因为name占20个字节,其所占空间如图中绿色区域所示。

第三个成员sex,类型所占字节数为1,1<8同样是对齐到1的整数倍处,且sex占一个字节,sex所占区域便是黄色位置。

第四个成员为age,为int类型,所占大小为4个字节,4<8,所以age要对齐到4的整数倍出,也就是28处,又因为它本身占4个字节,所以age所占空间便是蓝色的区域。

 31位置到0位置,中间有32二个字节,这就是为什么student结构体变量大小不是29个字节而是32个字节。

为什么存在内存对齐

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

二是性能原因数据结构应该尽可能的在自然边界上对齐,原因在于,对于访问未对齐的内存,需要处理器访问两次内存,而对齐的话访问一次就行了。以空间换取了时间。

修改默认对齐数

内存对齐是空间换时间的办法,那么我们能不能更进一步,在特定情况下又省空间又省时间呢?修改默认对齐数便是一种办法。

修改默认对齐数需要使用#pragma

#pragma pack(2)就是将默认对齐数修改成了2,使结构体的大小进一步精简。我们来看看效果图(依然是一学生结构体变量为例)

第一个成员id不变,依然是占红色区域。

第二个成员name同样不变,因为1个字节仍然小于2个字节,占绿色区域。

第三个成员sex还是没变,原因与第二个相同,占黄色区域。

第四个成员age改变,2<4,age一个对齐至2的整数倍处,占蓝色区域

 由图我们可以发现,这次的学生结构体类型大小只占了30个字节,缩短了两个字节,我们用程序来验证一下。

 验证

和我们所设想的一样。

 结构体传参

直接上代码,print1使用的是传值,print2使用的是传址。那种方法更好呢?

#include<stdio.h>
struct A
{
	int data[1000];
	int num;
};
struct A a = { {1,2,3,4},666 };
void print1(struct A a)
{
	printf("%d\n", a.num);
}
void print2(struct A* a)
{
	printf("%d\n", a->num);
}
int main()
{
	print1(a);
	print2(&a);
	return 0;
}

print2的方法要优于print1,对于print1,我们需要创建一块内存空间来接收传过来的参数,是需要压栈的,则在空间和时间上会有更多的系统开销,如果传递的结构体对象过大,会导致性能下降。但是对于print2的方法来说,不需要而外的开销,使用的是之前的内存,不需要新创建。

所以结构体传参的时候,要传结构体的地址。

位段

上面我们了解了基础的结构体知识,这里我们介绍一下结构体实现位段的能力。

什么是位段?

位段和结构类似,但多了两条限制。

一是位段的成员必须是int、unsigned int 或者是signed int,也可以出现char。

二是成员名字后面要出现冒号和数字。

例如:

struct A
{
	int a : 2;
	int b : 5;
	int c : 10;
	int d : 30;
};

这就是一个位段类型,冒号后面的数字是指该成员占多少个比特位,并且该数字不能超过限制范围,位段空间的开辟是根据类型来开辟的。

位段A的大小是多少?

我们可以看见,位段A占8个字节。

#include<stdio.h>
struct A
{
	int a : 2;
	int b : 5;
	int c : 10;
	int d : 30;
};
int main()
{
	printf("%d", sizeof(struct A));
	return 0;
}

如何算位段的大小呢? 

猜想:是比特位数加起来/8,算出来字节数。

以位段A为例,位数和为47,按照猜想推算,一个占6个字节,但是结果是8,说明猜想错误

位段的内存分配

对于位段A,先开辟4个字节(因为成分成员类型是int),将a,b,c的位数相加,得到17位,再将d的位数加上去,发现位数超过了开辟的空间,所以需要新开一个4个字节的空间(按照类型来开辟空间),这时能够将位段A所有成员放进开辟的内存之中,所以是8个字节。

位段的缺点

位段不适用于需要跨平台的程序。

一是int位段被当成有符号数还是无符号数并没有明文规定。

二是位段中最大为的数目不确定(16位的机器是16,32位的机器是32)。

三是位段中成员在内存中从左向右分配,还是从右向左分配没有明文规定。

四是当一个结构包含两个位段,第二个位段成员比较大,无法容纳与第一个位段剩余位的时候,是舍弃剩余的位还是利用,这是不确定的。

位段的意义

就是节约内存,省空间,在缓解网络拥挤的时候有作用。

枚举

枚举类型的定义

直接看代码吧。类型+标签+可能的情况。

enum sex
{
	MALE,
	FEMALE
};

 枚举的优点

一是增加代码的可读性和可维护性。

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

三是防止了命名污染(封装)。

四是便于调试。

五是方便使用,一次可以定义多个变量,提高编程效率。

六是可以将数字换成符号(例如switch语句中,将case后面的数字换成便于理解的字符) 

联合(共用体)

联合也是一种被特殊的自定义类型。

这种类型定义的变量也包含一系列的成员,特征是这些成员共用一块空间(所以联合也叫共用体)

联合类型的定义

#include<stdio.h>
//联合体的声明
uNIOn un
{
	char c;
	int i;
};
//联合体的定义
union un u;
int main()
{
	//计算联合体的大小
	printf("%d", sizeof(u));
	return 0;
}

联合体的特点 

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

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

到此这篇关于C语言 自定义类型的文章就介绍到这了,更多相关C语言 自定义类型内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: 最新C语言自定义类型详解

本文链接: https://www.lsjlt.com/news/177636.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • 最新C语言自定义类型详解
    目录前言结构体结构体的基础知识结构和数组的区别结构体的声明结构体的特殊声明 结构体的自引用我们能不能结构体套结构体呢?那么怎么实现结构体自引用呢?结构体变量的定义和初始化我...
    99+
    2023-01-13
    C语言 自定义类型 C自定义类型详解
  • C语言:自定义类型详解
    目录一、结构体1.结构体变量的定义及初始化2.结构体内存对齐3.为什么要内存对齐呢?二、位段1.什么是位段2.位段的内存分配三、枚举1.枚举的定义2.枚举的优点四、联合(共用体)1....
    99+
    2022-11-12
  • C语言中自定义类型详解
    目录结构大小offsetof结构体对齐规则存在原因总结结构大小 我们先随便给出一个结构体,为了计算他的大小,我给出完整的打印方案: typedef struct num { cha...
    99+
    2022-11-13
  • C语言自定义类型全解析
    目录前言结构体类型结构体的声明结构体变量的定义与初始化结构体的自引用结构体的访问结构体的传参传结构体传地址结构体的内存对齐(强烈建议观看)位段位段的声明位段的内存管理位段的跨平台性 ...
    99+
    2022-11-13
  • C语言自定义类型全面系统理解
    目录一、结构体1.结构体的声明局部结构体变量全局结构体变量2.特殊声明3.结构体的自引用4.结构体变量的初始化5.结构体内存对齐 6.修改默认对齐数7.结构体传参传址调用原因:二、位...
    99+
    2022-11-13
  • 怎么在C语言中自定义类型
    本篇文章为大家展示了怎么在C语言中自定义类型,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。一、初始结构体结构是一些值的集合,这些值称为成员变量。结构的每个成员可以是不同类型的变量。下面举一个例子:s...
    99+
    2023-06-08
  • C语言编程C++自定义个性化类型
    目录自定义类型结构体声明一个结构体类型特殊声明结构体自引用结构体变量的定义和初始化结构体内存对齐结构体传参位段枚举联合(共用体)联合类型的定义联合类型的声明联合的特点判断当前机器的大...
    99+
    2022-11-12
  • C语言中的自定义类型是什么
    C语言中的自定义类型是什么,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。结构大小我们先随便给出一个结构体,为了计算他的大小,我给出完整的打印方案:typedef st...
    99+
    2023-06-28
  • c语言如何自定义结构体类型
    在C语言中,可以使用`typedef`关键字来自定义结构体类型。`typedef`关键字用于为已存在的类型定义一个新的名称。以下是定...
    99+
    2023-09-04
    c语言
  • c语言string类型如何定义
    在C语言中,可以使用字符数组来表示字符串,并通过数组的最后一个元素设置为'\0'来表示字符串的结束。可以使用以下两种方式定义字符串:...
    99+
    2023-10-28
    c语言
  • C语言深入了解自定义数据类型的使用
    目录一、自定义数据类型(上)二、自定义数据类型(中)​​​​​三、自定义数据类型(下)​​​​​一、自定义数据类型(上) 类型命名关键字 (typedef) C语言中可以对类型赋予新...
    99+
    2022-11-13
  • C语言自定义数据类型的结构体、枚举和联合详解
    结构体基础知识 首先结构体的出现是因为我们使用C语言的基本类型无法满足我们的需求,比如我们要描述一本书,就需要书名,作者,价格,出版社等等一系列的属性,无疑C语言的基本数据类型无法解...
    99+
    2022-11-12
  • C语言自定义类型详解(结构体、枚举、联合体和位段)
    目录前言一、结构体1、结构体类型的声明2、结构体的自引用3、结构体变量的定义和初始化4、结构体内存对齐5、结构体传参二、位段1、位段的定义 2、位段的内存分配3、位段的应用...
    99+
    2022-11-12
  • C语言关于自定义数据类型之枚举和联合体详解
    目录前言枚举枚举类型的定义枚举类型的优点枚举类型的使用枚举中需要注意的点联合体联合体类型的定义联合体的特点联合体的使用联合体存在内存对齐结语前言 在C语言的自定义数据类型中,除了我们...
    99+
    2022-11-12
  • C语言枚举类型如何定义
    C语言枚举类型可以使用关键字`enum`来定义。定义枚举类型的语法格式如下:```cenum 枚举类型名 {枚举常量1,枚举常量2,...
    99+
    2023-10-10
    C语言
  • c语言中byte类型如何定义
    在C语言中,没有直接定义byte类型的关键字,但可以使用unsigned char类型来表示一个字节大小的数据。具体定义方式如下: ...
    99+
    2023-10-24
    c语言
  • C语言常量类型怎么定义
    这篇文章主要介绍“C语言常量类型怎么定义”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“C语言常量类型怎么定义”文章能帮助大家解决问题。常量的定义常量就是一个不变的量(比如圆周率,性别,身份证号码)C...
    99+
    2023-06-29
  • C语言中的自定义类型之结构体与枚举和联合详解
    目录1.结构体1.1结构的基础知识1.2结构的声明1.3特殊的声明1.4结构的自引用1.5结构体变量的定义和初始化1.6结构体内存对齐1.7修改默认对齐数1.8结构体传参2.位段2....
    99+
    2022-11-13
  • Go语言中的自定义类型你了解吗
    目录1. 什么是自定义类型2. 如何定义自定义类型3. 自定义类型的方法4. 自定义类型的嵌入和组合5. 自定义类型的值接收者和指针接收者6. 自定义类型的类型方法7. 总结注:本篇...
    99+
    2023-05-18
    Go自定义类型使用 Go自定义类型原理 Go自定义类型
  • C++自定义数据类型方法详情
    目录1、typedef声明2、枚举类型enum1、typedef声明 typedef用于给已有的类型一个新的名字,这个新的名字可以是自己定义的, 其语法为: typedef 现有类型...
    99+
    2022-11-13
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作