当前位置: 首页 > news >正文

C语言-结构体-详解

博客主页:【夜泉_ly】
本文专栏:【C语言】
欢迎点赞👍收藏⭐关注❤️

C语言-结构体-详解

  • 1.前言
  • 2.结构体类型
    • 2.1声明
    • 2.2变量的创建与初始化
    • 2.3访问
    • 2.4匿名结构体类型
  • 3.结构体内存对齐
    • 3.1对齐规则
    • 3.2示例

1.前言

在C语言中,除了整型、浮点型等给定的类型外,还有很多自定义类型,结构体就是其中之一。
结构体十分重要,想要学好数据结构,必须掌握指针、结构体和动态内存管理。
本篇,我将详细介绍C语言中的结构体。

2.结构体类型

结构体作为C语言的一种重要的数据类型,其特点是由一组数据组合而成,且这些数据的类型可以不同。

2.1声明

基本框架:

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

其中,tag为该结构体的名字,member-list为成员列表,variable-list为创建变量的列表。
例如:

struct student
{char name[20];int age;int tele;
}s1,s2;

为了存储一个学生的信息,需要存储姓名、年龄、电话等等内容,这些内容的数据类型显然不同,因此,可以用结构体存储:

struct student

这里定义了一个结构体类型struct student,代表该结构体类型用于存储学生信息。
在这之后:

{char name[20];int age;int tele;
}

这里的namenametele称为结构体的成员,共同组成成员列表。
最后:

s1,s2;

这里使用结构体类型创建了两个变量s1s2,代表学生一和学生二。
当然,这里也可以不创建变量,但需注意, 分号一定不能丢 !

顺带一提,好像只要顺序正确,都能编译成功:
在这里插入图片描述
不过这样的代码过于恶心,这里只是尝试一下,平时可不敢这样写😂。

2.2变量的创建与初始化

创建:

struct Stu
{char name[20];int age;int tele;
}s1;
struct Stu S2;
int main()
{struct Stu S3;return 0;
}

S1、S2为全局变量,S3为局部变量


初始化大致分两种:

  • 按原顺序进行初始化
struct Stu s1 = { "张三", 18, 11223344 };
  • 按指定顺序进行初始化
struct Stu s2 = { .age = 81, .tele = 44332211, .name = "李四" };

2.3访问

直接访问
使用.

struct Stu s1 = { "张三", 18, 11223344 };
printf("Name: %s\nAge: %d\nTele: %d\n", s1.name, s1.age, s1.tele);

运行结果如下:
在这里插入图片描述

通过指针访问
常见于数据结构部分,因为这部分的结构体变量大多是在堆上分配的。

struct Stu *p = malloc(sizeof(struct Stu));
if(!p){perror("malloc");return 1;}
strcpy(p->name, "李四");
p->age = 81;
p->tele = 44332211;printf("Name: %s\n", p->name);
printf("Age: %d\n", p->age);
printf("Tele: %s\n", p->tele);free(p);
p = NULL;

运行结果如下:
在这里插入图片描述

一个小细节:

p->name = "李四";
//strcpy(p->name, "李四");

为什么不能写成这样?
在C语言中,字符串常量(如 “李四”)是不可修改的常量数组。
当试图将一个字符串常量赋值给一个字符数组时,编译器会报错:
在这里插入图片描述
因此,需要先创建一个字符数组,然后将字符串strcpy复制到这个数组中。

2.4匿名结构体类型

特点是没有名字,且只能用一次:

struct
{char name[20];int age;int tele;
}s,*ps;

使用时,只能在结构体后创建变量,如s*ps
且,即便两个匿名结构体的成员列表相同,它们依然是两个不同的类型。

3.结构体内存对齐

先来看看下面这段代码:

struct S1
{char c1;int n;char c2;
};
struct S2
{char c1;char c2;int n;
};
int main()
{printf("%zd\n",sizeof(struct S1));printf("%zd\n",sizeof(struct S2));return 0;
}

运行结果如下:

12
8

如果单纯的将成员列表中各个成员的大小相加,那么S1、S2都应该是6字节。
但是很遗憾,不仅不是6字节,S1、S2的大小甚至都不一样,这是为什么呢?

结构体的大小不是结构体元素单纯相加就行的,因为主流的计算机使用的都是32bit字长的CPU,对这类型的CPU取4个字节的数要比取一个字节要高效,也更方便。所以在结构体中每个成员的首地址都是4的整数倍的话,取数据元素时就会相对更高效,这就是内存对齐的由来。

3.1对齐规则

  1. 第一个成员对齐到偏移量为0的位置。
  2. 之后的成员对齐,对齐到对齐数的整数倍处。
  3. 总大小需是最大对齐数的整数倍。

对齐数

  • 常见类型的对齐数,按默认对齐数和自身长度,较小的那个进行
  • 数组的对齐数,按默认对齐数和自身成员长度,较小的那个进行
  • 结构体的对齐数,按默认对齐数和自身成员最大长度,较小的那个进行

默认对齐数
每个特定平台上的编译器都有自己的默认对齐数,如VS上的是8。
也可以通过预编译指令修改默认对齐数:

#pragma pack(n)

注:这里的n最好取2的倍数

3.2示例

下面我将给出多个结构体,并解释它们的大小是如何得到的。


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

先将c1存入偏移量为0的位置,占一字节:
在这里插入图片描述
再存c2,计算得对齐数为1,因此存入偏移量为1的位置,占一字节:
在这里插入图片描述
再存n,计算得对齐数为4,因此存入偏移量为4的位置,占四字节:
在这里插入图片描述
最后,最大对齐数为4,因此大小为8


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

先将c1存入偏移量为0的位置,占一字节:
在这里插入图片描述
再存n,计算得对齐数为4,因此存入偏移量为4的位置,占四字节:
在这里插入图片描述
再存c2,计算得对齐数为1,因此存入偏移量为8的位置,占一字节:
在这里插入图片描述
最后,最大对齐数为4,因此大小为12


struct S3
{short n;char c;int i;
};

先将n存入偏移量为0的位置,占两字节:
在这里插入图片描述

再存c,计算得对齐数为1,因此存入偏移量为2的位置,占一字节:
在这里插入图片描述

再存i,计算得对齐数为4,因此存入偏移量为4的位置,占四字节:

在这里插入图片描述

最后,最大对齐数为4,因此大小为8


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

先将c存入偏移量为0的位置,占一字节:
在这里插入图片描述
再存S3,计算得对齐数为4,因此存入偏移量为4的位置,占八字节:
在这里插入图片描述
再存d,计算得对齐数为8,因此存入偏移量为16的位置,占八字节:
在这里插入图片描述
最后,最大对齐数为8,因此大小为24


希望本篇文章对你有所帮助!并激发你进一步探索C语言的兴趣!

本人仅是个C语言初学者,如果你有任何疑问或建议,欢迎随时留言讨论!让我们一起学习,共同进步!

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 【数据结构与算法 | 灵神题单 | 自顶向下DFS篇】力扣1022,623
  • windows11+ubuntu20.04.6双系统安装
  • 数据结构-2.顺序表
  • Delta Function的简单介绍
  • 关于less的基本使用
  • Java设计模式—面向对象设计原则(一) ----->开闭原则OCP(完整详解,附有代码+案例)
  • re题(27)BUUFCTF-[MRCTF2020]Transform
  • C++速通LeetCode简单第18题-杨辉三角(全网唯一递归法)
  • 如何快速解决程序中的BUG
  • 商淘云九周年 分账系统助力企业合规发展
  • 深度学习数据集交通类常见图像分类、目标检测、分割图像数据集(深度学习数据集 - 交通类解决方案)
  • PHP环境搭建详细教程
  • 中秋献礼!2024年中科院一区极光优化算法+分解对比!VMD-PLO-Transformer-LSTM多变量时间序列光伏功率预测
  • 2021 年 6 月青少年软编等考 C 语言二级真题解析
  • QT Mode/View之View
  • [case10]使用RSQL实现端到端的动态查询
  • ➹使用webpack配置多页面应用(MPA)
  • 03Go 类型总结
  • CNN 在图像分割中的简史:从 R-CNN 到 Mask R-CNN
  • Koa2 之文件上传下载
  • Linux后台研发超实用命令总结
  • MySQL QA
  • Mysql5.6主从复制
  • node.js
  • React-flux杂记
  • TiDB 源码阅读系列文章(十)Chunk 和执行框架简介
  • vue2.0项目引入element-ui
  • 记一次和乔布斯合作最难忘的经历
  • 聊聊flink的BlobWriter
  • 聊聊spring cloud的LoadBalancerAutoConfiguration
  • 使用 @font-face
  • 适配iPhoneX、iPhoneXs、iPhoneXs Max、iPhoneXr 屏幕尺寸及安全区域
  • 我是如何设计 Upload 上传组件的
  • 移动互联网+智能运营体系搭建=你家有金矿啊!
  • 优化 Vue 项目编译文件大小
  • 原创:新手布局福音!微信小程序使用flex的一些基础样式属性(一)
  • 阿里云IoT边缘计算助力企业零改造实现远程运维 ...
  • 基于django的视频点播网站开发-step3-注册登录功能 ...
  • ​软考-高级-系统架构设计师教程(清华第2版)【第15章 面向服务架构设计理论与实践(P527~554)-思维导图】​
  • ​虚拟化系列介绍(十)
  • ###C语言程序设计-----C语言学习(3)#
  • #13 yum、编译安装与sed命令的使用
  • #laravel 通过手动安装依赖PHPExcel#
  • (python)数据结构---字典
  • (vue)el-cascader级联选择器按勾选的顺序传值,摆脱层级约束
  • (过滤器)Filter和(监听器)listener
  • (蓝桥杯每日一题)love
  • (四)js前端开发中设计模式之工厂方法模式
  • (四)鸿鹄云架构一服务注册中心
  • (译) 函数式 JS #1:简介
  • (转)Android中使用ormlite实现持久化(一)--HelloOrmLite
  • (最全解法)输入一个整数,输出该数二进制表示中1的个数。
  • **登录+JWT+异常处理+拦截器+ThreadLocal-开发思想与代码实现**
  • .cfg\.dat\.mak(持续补充)
  • .NET 4.0中的泛型协变和反变