C语言——结构体自定义类型

news2024/11/29 20:33:45

目录

结构体类型

声明结构体

结构体的特殊声明

创建结构体变量和初始化结构体变量

结构体的自引用

结构体内存对齐

对齐规则

内存对齐存在意义

默认对齐数的修改

结构体传参

结构体实现位段

了解位段是什么

位段的内存分配

位段有跨平台的问题及使用注意事项


C语言中有内置的类型,内置类型如下:

内置类型
char

short

int
long
long long
float
double
long double

这些都是C语言本身支持的现场类型,但是仅仅有内置类型是不够的。

比如,我们要定义一个人的变量,

人:3.14   ——   这种就是不行的

人是一个复杂的对象,有身高、体重、名字等。所以这就要用到一个自定义类型——结构体

C语言中也有自定义类型的。

结构体类型

声明结构体

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

//结构体的声明
struct tag  //tag就是标签名
{
    member-list;  //成员列表:1个或者多个,但是不能没有
}variable_list;  //变量列表

我们知道结构体的声明之后,我们就可以自定义一个学生练习一下:

struct Stu
{
	char name[20]; //名字
	int age;       //年龄
	char sex[5];   //性别
	char id[20];   //学号
};                 //分号不能丢

结构体的特殊声明

在声明结构体的时候,可以不完全声明:匿名结构体类型

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

这两个结构体对比前面的,看出tag省略了

在使用这种形式的时候,要注意两点:

  1. 匿名的结构体类型,如果没有对结构体类型重命名的话,基本上只能使用一次,第二次往后基本不能用了。
  2. 编译器会把上面的两个声明当成完全不同的两个类型,所以下面操纵是非法的。
p = &x;

创建结构体变量和初始化结构体变量

我们定义了这个类型,要怎么创建这种类型的变量并怎么初始化它呢?

局部结构体变量创建和初始化有两种:

一种按照结构体成员的顺序初始化的,如下:

//按照结构体成员的顺序初始化
struct Stu s = { "张三", 20, "男", "20230818001" };

我们想打印验证一下这个结构体变量,要怎么打印呢?

printf("name: %s\n", s.name);
printf("age : %d\n", s.age);
printf("sex : %s\n", s.sex);
printf("id : %s\n", s.id);

                        

注:   ·   和  ->都是用来访问结构体内的变量用的,· 是用来取的这个结构体中的元素,->是取得这个结构体中元素的地址所对应的元素。

例如:
struct Stu
{
	char name[20];//名字
	int age;//年龄
	char sex[5];//性别
	char id[20];//学号
};             

*p=Stu;
Stu.name==(*p).name==p->name。

第二种初始化按照指定顺序初始化的(乱序):

//按照指定的顺序初始化
struct Stu s2 = { .age = 18, .name = "李四", .id = "20230818002", .sex = "女" };
printf("name: %s\n", s2.name);
printf("age : %d\n", s2.age);
printf("sex : %s\n", s2.sex);
printf("id : %s\n", s2.id);

                                

全局变量的定义有两种,初始化方式也跟局部变量相似,下面就只举例全局变量的创建:

struct Stu
{
	char name[20];//名字
	int age;//年龄
	char sex[5];//性别
	char id[20];//学号
}b2,b3; //全局变量           

struct Stu b1;//全局变量

结构体的自引用

先介绍typedef相关知识, typedef为C语言的关键字:作用是为一种数据类型定义一个新名字

typedef可以声明新的类型名来代替已有的类型名,不能增加新的类型。这里的数据类型有前面所说的内置数据类型(int,float等),还有自定义的数据类型(struct等)。

结构体自引用对于数据结构上的链表是非常有用的,

数据结构——其实是数据在内存种的存储和组织的结构,数据结构有多种

  • 线性数据结构:顺序表、链表、栈、队列。
  • 树形数据结构:二叉树
  • 等等……

结构体自引用的正确方式(链表形式):

struct Node
{
	int data;			//数据
	struct Node* next;  //指针——自己里包含一个自己同类型的指针
};

我们用typedef重命名一下:

typedef struct Node
{
	int data;			//数据
	struct Node* next;  //指针——自己里包含一个自己同类型的指针
}Node;    //将struct Node 类型重命名为 Node
//也可也写成
struct Node
{
	int data;			//数据
	struct Node* next;  //指针——自己里包含一个自己同类型的指针
};
typedef struct Node Node;  //typedef在后面重命名

注意,千万不能写成以下这些形式:

struct Node
{
 int data;
 struct Node next;
};
一个结构体中再包含一个同类型的结构体变量,这样结构体变量的大小就会无穷的大。
typedef struct   //匿名结构体类型
{
 int data;
 Node* next;  //这里面还有struct不能省略掉
}Node;

这个Node是对前面的匿名结构体类型的重命名产生的,但在匿名结构体内部提前使用Node类型来创建变量成员变量是不可以的。所以匿名结构体类型是不能实现这种自引用的。

所以,定义结构体尽量不要使用匿名结构体。

结构体内存对齐

这个牵扯到计算结构体的大小,并且还有它特有的对齐规则。

对齐规则

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

例如下列例子,并了解这个规则怎么用:

struct S1
{
 char c1;
 int i;
 char c2;
};
printf("%d\n", sizeof(struct S1));


struct S2
{
 double d;
 char c;
 int i;
};
printf("%d\n", sizeof(struct S2));
//结构体嵌套问题
struct S3
{
 char c1;
 struct S2 s2;
 double d;
};
printf("%d\n", sizeof(struct S3));

第一个:

所以会输出:12.

注:若遇到数组,如char[5] 则就是存了五个char。

第二个:

所以这个输出16.

第三个:

所有会输出:32.

内存对齐存在意义

结构体内存对齐是牺牲空间来换取时间的做法。

1. 平台原因 (移植原因)
2. 性能原因
若又想节省一些空间,那么就让空间小的成员尽量集中在一起。
struct S1
{
    char c1;
    int i;
    char c2;
};  //占12

struct S2
{
    char c1;
    char c2;
    int i;
};  //占8

默认对齐数的修改

我们前面说VS中默认对齐数是8,那么我们可以修改吗?

我们可以用一个预处理指令修改,并且另一个指令可以取消修改,如下:

#pragma pack(1)//设置默认对⻬数为1
#pragma pack()//取消设置的对⻬数,还原为默认

根据自己所需,可以用这个预处理指令修改对齐数。

结构体传参

我们函数传参,可以传整型变量、数组、指针变量等。结构体变量也可以传。

实现具体如下列程序:

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

不过上面两个函数,print2的效率高一些。

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

结构体实现位段

了解位段是什么

位段实现是基于结构体的

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

  1. 位段的成员必须是int、unsigned int 或者signed int,C99中位段成员的类型可以选择其他类型。
  2. 位段成员名后边有一个冒号和一个数字。
    struct A
    {
     int _a:2;
    };

位段的内存分配

  1. 位段的成员可以是int、unsigned int、signed int 或者char类型等
  2. 位段的空间上是按照需要以4个字节(int)或1个字节(char)的方式来开辟的
  3. 位段很多不确定因素,因此不能跨平台的,可移植程序应避免使用位段。

我们看下一段代码:

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

int main()
{
	printf("%d\n", sizeof(struct A));
	return 0;
}

A所占内存是多少?

输出结果:

位段有跨平台的问题及使用注意事项

int位段被当成有符号还是无符号不确定

位段最大位的数目不确定。

位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义。

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

所以位段可以达到结构体同样的效果,并且可以很好的节省空间,但有跨平台问题。

注意事项:位段内存中每个节分配一个地址,一个字节内部的bit位是没有地址的。所以不能对位段成员使用&操作符,不能使用scanf直接输入值,只能先输入放在一个变量中,然后赋值给位段的成员(使用位段结构体里面的类型尽量要一样,否则可控性就会比较差)。

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

制作不易,求各位大佬三连qwq,若有不足的地方,请大佬们多多指点!

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

电脑插上网线之后仍然没网络怎么办?

前言 有小伙伴在使用Windows系统的时候,经常会遇到电脑没网络,但又不知道具体怎么调整才好。 本篇内容适合插网线和使用Wi-Fi的小伙伴,文章本质上是重置电脑的网络设置。 注意事项:网络重置操作会让已连接过的wifi密码丢失&…

51单片机学习笔记7 串转并操作方法

51单片机学习笔记7 串转并操作方法 一、串转并操作简介二、74HC595介绍1. **功能**:2. **引脚**:3. **工作原理**:4. 开发板原理图(1)8*8 LED点阵:(2)74HC595 串转并: 三…

【PyTorch][chapter 22][李宏毅深度学习][ WGAN]【实战三】

前言: 本篇主要讲两个WGAN的两个例子: 1 高斯混合模型 WGAN实现 2 MNIST 手写数字识别 -WGAN 实现 WGAN 训练起来蛮麻烦的,如果要获得好的效果很多超参数需要手动设置 1: 噪声的维度 2: 学习率 3: 生成器,鉴别器…

Bert的一些理解

Bert的一些理解 Masked Language Model (MLM)Next Sentence Prediction (NSP)总结 参考链接1 参考链接2 BERT 模型的训练数据集通常是以预训练任务的形式来构建的,其中包括两个主要任务:Masked Language Model (MLM) 和 Next Sentence Prediction (NSP)。…

使用Vscode连接云进行前端开发

使用Vscode连接云进行前端开发 1、ssh连接腾讯云 本人使用的是腾讯云。 然后vscode,用最新版,插件选择remote ssh,或者remote xxx下载过来。 然后点击远程资源管理器,选择SSH通道 然后输入命令如下。 ssh rootip然后输入密码 腾讯云应该…

Linux环境下使用Eclipse Paho C 实现(MQTT Client)异步订阅Message

目录 概述 1 认识Eclipse Paho C 1.1 paho.mqtt.c简介 1.2 下载和安装paho.mqtt.c 1.3 一些重要的函数 2 异步订阅消息实现 2.1 编写异步订阅消息功能 2.1.1 初始化MQTT参数 2.1.2 初始化函数 2.1.3 订阅消息的回调函数 2.1.4 取消订阅Topic 2.2 编译代码和测试 3…

Lvs+keepalived+nginx搭建高可用负载均衡集群

环境配置 master主机192.168.199.149,虚拟IP192.168.199.148 back备机192.168.199.150 真实服务器1 192.168.199.155 真实服务器2 192.168.199.156 关闭防火墙和selinux master配置(149) 添加虚拟IP ip addr add 192.168.199.148/24 …

web前端之多种方式实现switch滑块功能、动态设置css变量、after伪元素、选择器、has伪类

MENU 效果图htmlcsshtmlcssJS 效果图 htmlcss html <div class"s"><input type"checkbox" id"si" class"si"><label for"si" class"sl"></label> </div>style * {margin: 0;pad…

IO多路复用、域套接字

思维导图 面试题TCP通信中的三次握手和四次&#xff1a; 客户端像向服务器端发送连接请求 服务器应答连接请求 客户端与服务器简历连接 客户端向服务器发送断开请求 服务器应答断开请求 服务器请求关闭连接 客户端发送确认应答 并行和并发的区别&#xff1a; 并行&#xff1a…

使用ChatGPT高效完成简历制作[中篇3]-有爱AI实战教程(十)

演示站点&#xff1a; https://ai.uaai.cn 对话模块 官方论坛&#xff1a; www.jingyuai.com 京娱AI 一、导读&#xff1a; 在使用 ChatGPT 时&#xff0c;当你给的指令越精确&#xff0c;它的回答会越到位&#xff0c;举例来说&#xff0c;假如你要请它帮忙写文案&#xff0c;…

【Linux】线程封装 | 线程互斥 | 基于阻塞队列的生产消费者模型

文章目录 一、线程封装二、Linux线程互斥进程线程间的互斥相关背景概念互斥量mutex为什么上面的抢票代码可能无法获得正确结果&#xff1f;&#xff08;票数为负&#xff09;互斥量的接口1. 初始化互斥量方法一&#xff1a;静态分配方法二&#xff1a;动态分配: 2. 销毁互斥量3…

性能优化(CPU优化技术)-NEON指令详解

原文来自ARM SIMD 指令集&#xff1a;NEON 简介 &#x1f3ac;个人简介&#xff1a;一个全栈工程师的升级之路&#xff01; &#x1f4cb;个人专栏&#xff1a;高性能&#xff08;HPC&#xff09;开发基础教程 &#x1f380;CSDN主页 发狂的小花 &#x1f304;人生秘诀&#xf…

如何让自己上百度百科?个人百科词条创建

百度百科&#xff0c;作为我国最大的中文百科全书&#xff0c;其影响力和权威性不言而喻。能够登上百度百科&#xff0c;意味着个人的知名度、成就和社会影响力得到了广泛认可。那么&#xff0c;如何才能让自己上百度百科呢&#xff1f;接下来伯乐网络传媒就来给大家讲解一下。…

HarmonyOS NEXT应用开发之Navigation实现多设备适配案例

介绍 在应用开发时&#xff0c;一个应用需要适配多终端的设备&#xff0c;使用Navigation的mode属性来实现一套代码&#xff0c;多终端适配。 效果图预览 使用说明 将程序运行在折叠屏手机或者平板上观看适配效果。 实现思路 本例涉及的关键特性和实现方案如下&#xff1a…

[AIGC] 在Spring Boot中指定请求体格式

在使用Spring Boot开发Web应用的时候&#xff0c;我们经常会遇到需要接收并处理HTTP请求的情况。一个HTTP请求通常包括一个请求行、若干请求头和一个请求体。请求体在POST和PUT请求中特别重要&#xff0c;因为它通常用于向服务器传递数据。 文章目录 创建并使用一个Java Bean指…

分布式搜索引擎elasticsearch专栏三

1.数据聚合 聚合&#xff08;aggregations&#xff09;可以让我们极其方便的实现对数据的统计、分析、运算。例如&#xff1a; 什么品牌的手机最受欢迎&#xff1f; 这些手机的平均价格、最高价格、最低价格&#xff1f; 这些手机每月的销售情况如何&#xff1f; 实现这些…

3.19作业

1、思维导图 2、模拟面试题 1&#xff09;TCP通信中的三次握手和四次挥手 答&#xff1a;三次握手 客户端向服务器发送连接请求 服务器向客户端回复应答并向客户端发送连接请求 客户端回复服务端&#xff0c;并建立联系 四次挥手 进程a向进程b发送断开连接请求…

vue axios 缓存 接口请求实现缓存加载

文章写的多了&#xff0c;开头就不知道怎么写了&#xff0c;硬挤一些句子总觉的卖弄。其实更多的想留下各位看官&#xff0c;多多的点赞&#xff0c;多多的关注&#xff0c;多的收藏。为将来的博客化动作做好前期数据粉丝基础。哦哦哦&#xff0c;我在想啥呢。。这大下午的。。…

软件工程-第11章 内容总结

如果不想读这本书&#xff0c;直接看这一章即可。 11.1 关于软件过程范型 11.2 关于软件设计方法

java算法第28天 | 93.复原IP地址 78.子集 90.子集II

93.复原IP地址 思路&#xff1a; 这里startIndex为插入‘.’的位置&#xff0c;使用回溯法遍历所有插入的位置&#xff0c;直接在原始字符串上操作。要注意的是开闭区间的规定&#xff08;这里我规定的是左闭右闭区间&#xff09;。还要明确什么时候能return。 class Solution…