C系列-自定义类型:结构体

 🌈个人主页: 会编程的果子君
💫个人格言:“成为自己未来的主人~” 

 

结构体类型的声明

前面我们在学习操作符的时候,已经学习了结构体的知识,这里我们稍微复习一下。

结构体回顾

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

结构的声明

struct stg
{member - list;
}variable-list;

假如描述一个学生

struct stu
{char name[20];//名字int age;//年龄char sex[5];//性别char id[50];//学号
};

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

 

#include<stdio.h>
struct stu
{char name[20];int age;char sex[20];char id[20];
};
int main()
{/** 第一种struct stu s = { "张三",20,"男","2023631002" };printf("%d", s.age);printf("%s", s.name);printf("%s", s.sex);printf("%s", s.id);*///第二种struct stu s2 = { .age = 20,.id = "2023631002",.name = "lisi",.sex = "男" };printf("%d", s2.age);printf("%s", s2.name);printf("%s", s2.sex);printf("%s", s2.id);return 0;
}

结构的特殊声明

在声明结构的时候,可以不完全的声明

比如:

 

#include<stdio.h>
struct
{int a;int b;
}x;struct
{char a[20];int b;float c;}a[20],*p;

 上面的结构在声明的时候省略掉了结构体标签,那么问题来了

在上面代码的基础上,下面的代码合法吗?

p=&x;

警告: 

编译器会把上面的两个声明当成完全不同的两个类型,所以是非法的。

匿名的结构体类型,如果没有对结构体类型重命名的话,基本上只能使用一次。

结构的自引用

在结构中包含了一个类型为该结构本身的成员是否可以呢?

比如,定义一个链表的节点

struct Node
{int a;struct Node next;
};

上述代码正确吗?如果正确,那sieof(struct Node)结果是多少

仔细分析,其实是不行的,因为一个结构体中在包含一个同类型的结构体变量,这样的结构体变量的大小就会无穷的大,是不合理的。

正确的自引用方式:

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

在结构体自引用使用的过程中,夹杂了typedef对匿名结构体类型重命名,也容易引入问题,看着下面的代码,可行吗?

typedef struct
{int a;struct Node* p;
}Node;

答案是不行的,因为Node是对前面的匿名结构体类型的重命名产生的,但是在匿名结构体内部提前使用Node类型来创建成员变量,这是不行的。

解决方案如下:定义结构体不要使用匿名结构体了。

typedef struct Node
{int a;struct Node* next;
}Node;

结构体内对齐

我们已经掌握了结构体的基本使用了

现在我们深入探讨一个问题,计算结构体的大小

这也是一个特别热门的考点:结构体内存对齐

对齐规则

首先得掌握结构体的对齐规则:

1.结构体的第一个成员对齐到和结构体变量起始位置偏移量为0的位置

2.其他成员变量要对齐某个数字(对齐数)的整数倍的地址处。

对齐数=编译器默认的一个对齐数与该成员变量大小的较小值

VS中默认的值为8

Linux中gcc没有默认对齐数,对齐数就是成员自身的大小

3.结构体总大小为最大对齐数(结构体中每个成员变量都有一个对齐数,所有对齐数最大的)的整数倍。

4.如果嵌套了结构体的情况,嵌套的结构体成员对齐到自己的成员中最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体中成员的对齐数)的整数倍

#include<stdio.h>
//练习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;
};
int main()
{printf("%d\n", sizeof(struct S1));printf("%d\n", sizeof(struct S2));printf("%d\n", sizeof(struct S3));printf("%d\n", sizeof(struct S4));return 0;
}

 为什么存在内存对齐

大部分的参考资料是这样说的

1.平台原因(移植原因)

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

2.性能原因

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

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

那在设计结构体的时候,我们既要满足对齐,又要节省空间,如何做到让占用的空间小的成员尽可能集中在一起。

#include<stdio.h>
struct s1
{char a;char c;int b;
};
struct s2
{char a;int b;char c;
};
int main()
{printf("%d\n", sizeof(struct s1));printf("%d\n", sizeof(struct s2));return 0;
}

s1和s2类型的成员一模一样,但是两者所占的空间大小存在一定的差别。

修改默认对齐数

#pragma这个预处理指令,可以改变编译器的默认对齐数。

#include<stdio.h>
#pragma pack(1)//设置默认对齐数为1
struct s1
{char a;int b;char c;
};#pragma pack()//将默认对齐数返回默认值int main()
{printf("%d", sizeof(struct s1));return 0;
}

 结构体在对齐方式不合适的时候,我们可以自己更改默认对齐数。

结构体传参

 

#include<stdio.h>
struct s1
{char name[20];int age;char sex[10];
};
struct s1 s = { "zhangsan",19,"男" };
void print1(struct s1 s)
{printf("%s\n", s.name);
}
void print2(struct s1* s)
{printf("%s\n", s->name);
}
int main()
{print1(s);print2(&s);return 0;
}

上面的print1和print2函数哪个好些

答案是:首选print2函数

原因:函数传参的时候,参数是需要压栈的,会有时间和空间上的系统 开销

如果传递一个结构体对象的时候,结构体过大,参数压栈的时候系统开销会比较大,所以会造成系统的性能下降。

结论:

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

结构体实现位段

结构体讲完就得讲讲结构体实现位段的能力。

什么是位段

位段的声明和结构是类似的,有两个不同。

1.位段的成员必须是 int unsigned int 或 signed int ,在C99中成员还可以是其他的类型。

2.位段的成员名后面有一个冒号和一个数字。

比如:

struct s1
{int a : 5;int b : 10;int c : 10;};

s1就是一个位段类型

那么位段s1所占内存的大小是多少

 

 上面的一共是25个bit位,三个字节是24个bit,所以需要有4个字节

位段的内存分配

  1. 位段的成员可以是int ,unsigned int ,signed int或者char类型
  2. 位段的空间上是按照需要以4个字节或者1个字节的方式开辟的
  3. 位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使用位段
#include<stdio.h>
struct S
{char a : 3;char b : 4;char c : 5;char d : 4;
};
struct S s = { 0 };
int main()
{s.a = 10;s.b = 12;s.c = 3;s.d = 4;return 0;
}
//空间是如何开辟的?

位段的跨平台问题

  • int 位段被当成有符号数还是无符号数是不确定的
  • 位段中最大位的数目不能确定。(16位机器最大16,32位机器最大是32)
  • 位段的成员在内存中从左向右分配还是从右向左分配是不确定的
  • 当一个结构包含两个位段,第二个位段成员比较大,无法容纳第一个位段剩余的位时,是舍弃剩余的位还是利用,这是不确定的。

总结:

根结构相比,位段可以达到同样的效果,并且可以很好的节省空间。

位段使用的注意事项

位段的几个成员共有同一个字节,这样有些成员的起始位置并不是某个字节的起始位置,那么这些位置处是没有地址的,内存中每个字节分配一个地址,一个字节内部的bit位是没有地址的

所以不能对位段的成员使用&操作符,这样就不能使用scanf直接给位段的成员输入值,而是先输入到一个变量当中,然后赋值给位段的成员。

#include<stdio.h>
struct s1
{int s:30;
};
int a = 0;
int main()
{struct s1 s = { 0 };//scanf("%d", &s);//这是错误的//正确的方法int b = 0;scanf("%d", &b);s.s = b;return 0;
}

 

 

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

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

相关文章

【linux】磁盘相关命令fdisk/lsblk和file

1. fdisk 磁盘分区&#xff0c;查看系统分区。 fdisk 的意思是 固定磁盘(Fixed Disk) 或 格式化磁盘(Format Disk)&#xff0c;它是命令行下允许用户对分区进行查看、创建、调整大小、删除、移动和复制的工具。它支持 MBR、Sun、SGI、BSD 分区表&#xff0c;但是它不支持 GUI…

Docker 数据管理、容器互联、网络与资源控制

一、docker数据管理 管理 Docker 容器中数据主要有两种方式&#xff1a;数据卷(Data volumes)和数据卷容器(Datavolumes containers)。 1、数据卷 数据卷是一个供容器使用的特殊目录&#xff0c;位于容器中。可将宿主机的目录挂载到数据卷上&#xff0c;对数据卷的修改操作立…

Prometheus 容器化部署

实验部署 工作中是基于kube-api的自动发现 1、创建账户绑定集群 kubectl create serviceaccount monitor -n monitor-sa #创建账户 kubectl create clusterrolebinding monitor-clusterrolebinding -n monitor-sa --clusterrolecluster-admin --serviceaccountmonitor-sa:…

FileViewer纯前端预览项目Vue2 demo

FileViewer 项目Vue2 demo 本demo基于vue-clijsvue2.x构建&#xff0c;如果您需要vue3版本的demo&#xff0c;请前往main分支。 适用于Vue2 Webpack&#xff0c;本集成方法要求最低Webpack版本为5&#xff0c;也就是Vue Cli Service 5.0.0以上&#xff0c;当然&#xff0c;if…

13.前端--CSS-盒子模型

1.盒子模型的组成 CSS 盒子模型本质上是一个盒子&#xff0c;封装周围的 HTML 元素&#xff0c;它包括&#xff1a;边框、外边距、内边距、和 实际内容 2.边框&#xff08;border&#xff09; 2.1 边框的使用 1、border设置元素的边框。边框有三部分组成:边框宽度(粗细) 边框…

国网四川宜宾供电公司:基于“RPA+AI”融合技术的电网设备隐患缺陷智能化识别应用

推荐单位&#xff1a;国网四川省电力公司宜宾供电公司 本文作者&#xff1a;杨鑫、唐龙、钟睿、李小航、孙雪冬 摘 要&#xff1a;为推进电力企业生产业务数字化转型&#xff0c;提高基层班组数字化运维水平。本文通过一线班组对变电站视频巡视、设备故障判断应用场景需求分析…

C++-内存管理(1)

1. C/C内存分布 首先我们需要知道&#xff0c;在C中的内存分为5个区。 1. 栈 又叫堆栈 -- 非静态局部变量 / 函数参数 / 返回值等等&#xff0c;栈是向下增长的。 2. 内存映射段 是高效的 I/O 映射方式&#xff0c;用于装载一个共享的动态内存库。用户可使用系统接口 创建…

【2023地理设计组一等奖】基于GIS的桥梁隧道三维建模与可视化

作品介绍 1 设计背景和意义 随着我国基础建设规模不断扩大和深入,构建桥梁可视化管理模型,全面推动智慧桥梁,已成为现代隧道桥梁建设行业的发展趋势。传统的桥梁建模工作需要复杂的算法设计并需要熟练编程实践技能,实现周期长。开发自主知识版权的桥梁建模软件系统或专用插…

在线mockjson

在线mockjson体验地址 在调一个问题的时候&#xff0c;但是问题的数据可能并不能随着想到的场景就变化&#xff0c;譬如说又个数组长度的情况&#xff0c;可能默认的情况下是返回4个元素&#xff0c;但是想要返回为空的时候&#xff0c;如果联系服务给改一下&#xff0c;那么流…

防火墙知识普及详解,使用TOR Router把TOR作为默认网关,增加隐私/匿名性

防火墙知识普及详解,使用TOR Router把TOR作为默认网关,增加隐私/匿名性。 #################### 免责声明:工具本身并无好坏,希望大家以遵守《网络安全法》相关法律为前提来使用该工具,支持研究学习,切勿用于非法犯罪活动,对于恶意使用该工具造成的损失,和本人及开发者…

Abp 从空白WebApplication开始

开发环境&#xff1a;VS2022、.NET6 1、创建项目&#xff1a;BasicAspNetCoreApplication 2、NuGet添加&#xff1a;Volo.Abp.AspNetCore.Mvc和Volo.Abp.Autofac&#xff0c;如下图所示&#xff1a; 3、开始写代码&#xff0c;目录如下图所示&#xff1a; 3.1、添加启动模块Ap…

使用make_grid多批次显示网格图像(使用CIFAR数据集介绍)

背景介绍 在机器学习的训练数据集中&#xff0c;我们经常使用多批次的训练来实现更好的训练效果&#xff0c;具体到cv领域&#xff0c;我们的训练数据集通常是[B,C,W,H]格式&#xff0c;其中&#xff0c;B是每个训练批次的大小&#xff0c;C是图片的通道数&#xff0c;如果是1…

Python网络拓扑库之mininet使用详解

概要 网络工程师、研究人员和开发人员需要进行各种网络实验和测试&#xff0c;以评估网络应用和协议的性能&#xff0c;以及解决网络问题。Python Mininet是一个功能强大的工具&#xff0c;它允许用户创建、配置和仿真复杂的网络拓扑&#xff0c;以满足各种实际应用场景。本文…

Java 面向对象进阶 01(黑马)

static案例代码&#xff1a; 代码&#xff1a; public class Student {private String gender;private String name;private int age;public static String teacherName ;public Student() {}public Student(String gender, String name, int age) {this.gender gender;this.…

[晓理紫]每日论文分享(有中文摘要,源码或项目地址)--大模型、扩散模型、视觉语言导航

专属领域论文订阅 VX 关注{晓理紫}&#xff0c;每日更新论文&#xff0c;如感兴趣&#xff0c;请转发给有需要的同学&#xff0c;谢谢支持 如果你感觉对你有所帮助&#xff0c;请关注我&#xff0c;每日准时为你推送最新论文。 为了答谢各位网友的支持&#xff0c;从今日起免费…

费一凡:土木博士的自我救赎之道 | 提升之路系列(五)

导读 为了发挥清华大学多学科优势&#xff0c;搭建跨学科交叉融合平台&#xff0c;创新跨学科交叉培养模式&#xff0c;培养具有大数据思维和应用创新的“π”型人才&#xff0c;由清华大学研究生院、清华大学大数据研究中心及相关院系共同设计组织的“清华大学大数据能力提升项…

Linux true/false区分

bash的数值代表和其它代表相反&#xff1a;0表示true&#xff1b;非0代表false。 #!/bin/sh PIDFILE"pid"# truenginx进程运行 falsenginx进程未运行 checkRunning(){# -f true表示普通文件if [ -f "$PIDFILE" ]; then# -z 字符串长度为0trueif [ -z &qu…

时序数据库 Tdengine 执行命令能够查看执行的sql语句

curl是 访问6041端口&#xff0c;在windows系统里没有linux里的curl命令&#xff0c;需要用别的工具实现。我在cmd里是访问6030端口 第一步 在安装是时序数据库的服务器上也就是数据库服务端 进入命令窗口 执行 taos 第二步 执行 show queries\G;

jsjiami.v6加解密教学

1. 优点 a. 安全性提升 JavaScript 加密可以有效保护源代码&#xff0c;减少恶意用户的攻击风险。 b. 代码混淆 通过混淆技术&#xff0c;可以使代码变得难以阅读和理解&#xff0c;增加破解的难度。 c. 知识产权保护 对于商业项目&#xff0c;JavaScript 加密有助于保护…

Abp 创建一个WPF的项目

开发环境&#xff1a;VS2022、.NET6 1、创建项目&#xff1a;MyWpfApp&#xff0c;这里不再废话了。 2、NuGet添加&#xff1a; 2.1、Volo.Abp.Autofac 2.2、Serilog.Sinks.File 2.3、Serilog.Sinks.Async 2.4、Serilog.Extensions.Logging 2.5、Serilog.Extensions.Hos…