C语言结构体的相关知识

前言

从0开始记录我的学习历程,我会尽我所能,写出最最大白话的文章,希望能够帮到你,谢谢。


1.结构体类型的概念及定义

1.1、概念:

        结构体是一种构造类型的数据结构, 是一种或多种基本类型或构造类型的数据的集合。
它允许我们组合多个基本数据类型(如整数、浮点数、字符等)来创建一个新的复合数据类型。这个新的数据类型就像一个小包裹,里面装着了我们需要的不同类型的数据。 

1.2、 结构体类型的定义方法 

咱们在使用结构体之前必须先有类型,然后用类型定义数据结构,这个类型相当于一个模具

(1).先定义结构体类型,再去定义结构体变量

struct 结构体类型名{

成员列表(允许我们组合多个基本数据类型(如整数、浮点数、字符等))

};

例1:

struct stu{
int num;
char name[20];
char sex;
};

我们看这个例子。

我们定义了一个名为stu的结构体类型。这个结构体类型包含了三个成员:一个整数num,一个字符数组name,和一个字符sex。这些成员代表了学生的学号、姓名和性别。

struct stu lucy, bob, lilei;

一旦我们定义了这个结构体类型,就可以创建变量来存储实际的学生信息了。在这个例子中,我们创建了三个变量:lucyboblilei。每个变量都是一个结构体类型的实例,它们都有三个成员:numnamesex

在定义结构体类型的时候顺便定义结构体变量,以后还可以定义结构体变量
struct 结构体类型名 {
成员列表 ;
} 结构体变量 1, 变量 2;
struct 结构体类型名 变量 3 ,变量 4

例如看下面这个例子,实际上是和上面的例子是一个意思的。

struct stu{
int num;
char name[20];
char sex;
}lucy;
struct stu bob, lilei;

在定义结构体类型的时候,没有结构体类型名,顺便定义结构体变量,

因为没有类型名,所以以后不能再定义相关类型的数据了。也就是不可以往成员列表里面添加东西了。
struct {
成员列表 ;
} 变量 1 ,变量 2;

 2 结构体变量的定义初始化及使用

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

结构体变量,是个变量,这个变量是若干个数据的集合

注:
(1): 在定义结构体变量之前首先得有结构体类型,然后在定义变量
(2): 在定义结构体变量的时候,可以顺便给结构体变量赋初值,被称为结构体的初始化
(3): 结构体变量初始化的时候,各个成员顺序初始化
struct stu{
int num;
char name[20];
char sex;
};
struct stu boy;
struct stu lucy={101,"lucy",'f'};

我们创建了两个变量来存储实际的学生信息。第一个变量boy它是一个未初始化的结构体变量,它没有任何成员的值被赋定。第二个变量lucy是一个已经初始化的结构体变量。在声明时,我们直接给它赋初值。这里,我们给了学号、姓名和性别的值。

在这个初始化中,我们按照成员的顺序给了值:

  • 101 是 num 成员的值。
  • "lucy" 是 name 成员的值。由于 name 是一个字符数组,我们使用引号括起来来表示字符串。
  • 'f' 是 sex 成员的值。

 1、结构体变量的使用

定义了结构体变量后,要使用变量

(1). 结构体变量成员的引用方法
        结构体变量. 成员名
struct stu{
int num;
char name[20];
int age;
}bob;
int main(int argc, char *argv[])
{
lihua={10,"bob",10};
printf("%d\n",bob.num);
printf("%s\n",bob.name);
printf("%d\n",bob.age);
return 0;
}

 可以看到 通过.操作符,我们可以访问结构体变量的各个成员。上面的代码分别打印出了bob的学号、姓名和年龄。

2、结构体成员多级引用

        首先,我们有一个名为date的结构体,它包含了三个整数字段:yearmonthday。这些字段代表了一个特定的日期。

struct date{int year;int month;int day;
};

        接下来,我们有一个名为stu的结构体,它包含了四个字段:一个整数num、一个字符数组name、一个字符sex和一个子结构体birthday。子结构体birthday是我们之前定义的date结构体。

struct stu{int num;char name[20];char sex;struct date birthday;
};

        现在,我们在主函数中创建了一个名为lilei的变量,它是一个stu类型的变量。我们通过赋值来初始化这个变量的字段。

struct stu lilei={101,"lilei",'m'};
lilei.birthday.year=1986;
lilei.birthday.month=1;
lilei.birthday.day=8;

        在这里,我们通过.birthday.year.birthday.month, 和 .birthday.day来访问和设置子结构体中的日期字段。

最后,我们使用printf函数来打印出变量

printf("%d %s %c\n",lilei.num,lilei.name,lilei.sex);
printf("%d %d %d\n",lilei.birthday.year,lilei.birthday.month,lilei.birthday.day);

这里,我们通过多级结构体成员的引用来访问和打印出各个字段的值。可以看到就是通过点来访问的 嘎嘎简单哈。

3.结构体数组

结构体数组是个数组,由 若干个相同类型的结构体变量构成的集合
1 、结构体数组的定义方法
struct 结构体类型名 数组名 [ 元素个数 ];

结构体数组是一种数据结构,它允许我们存储多个相同类型的结构体变量。这是非常有用的,因为它可以帮助我们组织和管理大量的相关数据。

让我们来看看一个例子,以便更好地理解结构体数组:

首先,我们有一个名为stu的结构体,它包含了三个字段:一个整数num、一个字符数组name和一个字符sex

struct stu{int num;char name[20];char sex;
};

接下来,我们定义了一个名为edu的结构体数组,它包含了三个元素:edu[0]edu[1]edu[2]

struct stu edu[3];

这个数组 edu 可以被视为三个 stu 类型的结构体变量的集合。我们可以通过数组的下标来访问每个元素,并对它们进行操作。

例如,我们可以将值 101 给 edu[0] 数组中的第一个结构体变量的 num 字段赋值:

edu[0].num = 101;

这里,.num 是指 edu[0] 结构体变量中的 num 字段。

同样,我们可以使用 strcpy 函数将字符串 “lucy” 给 edu[1] 数组中的第一个结构体变量的 name 字段赋值:

strcpy(edu[1].name, "lucy");

这里,.name 是指 edu[1] 结构体变量中的 name 字段。

4.结构体指针

结构体指针是一种在C语言中用于存储和操作结构体变量的特殊类型的指针。它允许我们通过指针来访问和修改结构体变量的字段,而无需直接使用变量的名称。

让我们来看看一个例子,以便更好地理解结构体指针:

首先,我们有一个名为stu的结构体,它包含了两个字段:一个整数num和一个字符数组name

struct stu{int num;char name[20];
};

接下来,我们定义了一个名为p的结构体指针变量,它可以存储 stu 类型的结构体变量的地址。

struct stu * p;

这个指针变量 p 可以被视为一个存储结构体变量地址的“指针”,它占用了相同数量的内存空间,用于保存这个地址。

现在,我们创建了一个名为boy的结构体变量,并使用 & 指针运算符将其地址赋值给指针变量 p

struct stu boy;
p = &boy;

这里,&boy 是 boy 结构体变量的地址,我们将其赋值给指针变量 p

接下来,我们可以通过以下方式访问和修改 boy 结构体变量的字段:

  1. 直接访问

    boy.num = 101; // 直接访问并修改 boy 的 num 字段
    
  2. 通过指针访问

    (*p).num = 101; // 使用指针来访问并修改 boy 的 num 字段
    
  3. 使用指针运算符

    p->num = 101; // 使用指针运算符来访问并修改 boy 的 num 字段
    

在这三种方法中,最后两种都是通过指针来访问和修改结构体变量的字段。前提是指针必须先指向一个结构体变量。如果指针没有正确地指向一个结构体变量,那么尝试访问或修改其字段可能会导致程序出错。

结构体指针经常用到的地方:

(1):保存结构体变量的地址

typedef struct stu{
int num;
char name[20];
float score;
}STU;
int main()
{
STU *p,lucy;
p=&lucy;
p->num=101;
strcpy(p->name,"baby");
//p->name="baby";//错误,因为 p->name 相当于 lucy.name 是个字符数组的名字,是个常量
}

(2):传结构体变量的地址

#include<stdio.h>
#include<string.h>
typedef struct stu{
int num;
char name[20];
float score;
}STU;
void fun(STU *p)
{
p->num=101;
(*p).score=87.6;
strcpy(p->name,"lucy");
}
int main()
{
STU girl;
fun(&girl);
printf("%d %s %f\n",girl.num,girl.name,girl.score);
return 0;
}

(3):   传结构体数组的地址

结构体数组,是由多个相同类型的结构体变量构成的。存放在内存里,
也有起始地址,其实就是第 0 个结构体的地址。
#include<stdio.h>
#include<string.h>
typedef struct stu{
int num;
char name[20];
float score;
}STU;
void fun(STU *p)
{
p[1].num=101;
(*(p+1)).score=88.6;
}
int main()
{
STU edu[3];
fun(edu);
printf("%d %f\n",edu[1].num,edu[1].score);
return 0;
}

注意: 结构体变量的地址编号和结构体第一个成员的地址编号相同,但指针的类型不同

#include <stdio.h>
struct stu{
int num;
char name[20];
int score;
};
int main(int argc, char *argv[])
{
struct stu bob;
printf("%p\n",&bob);
printf("%p\n",&(bob.num));
return 0;
}

注意:结构体数组的地址就是结构体数组中第 0 个元素的地址.

#include <stdio.h>
struct stu{
int num;
char name[20];
int score;
};
int main(int argc, char *argv[])
{
struct stu edu[3];
printf("%p\n",edu);//struct stu *
printf("%p\n",&(edu[0]));//struct stu *
printf("%p\n",&(edu[0].num));//int *
return 0;
}

原本不想把这个代码放上来的 但是我觉得虽然废话多 但是如果光看话的话感觉总是有点离谱。

5、结构体内存分配

内存分配与对齐规则

分配单位
  • 定义:结构体中最大成员变量的长度。
    • 例如,如果结构体中最大成员是int(占4字节),则分配单位为4字节;如果最大成员是double(占8字节),则分配单位为8字节。
成员变量偏移
  • 定义:成员变量的起始地址必须是其自身长度的整数倍。
    • 例如,一个int类型变量(4字节)必须放在一个地址是4的倍数的位置上。
    • 一个short类型变量(2字节)必须放在一个地址是2的倍数的位置上。
结构体总大小
  • 定义:结构体总大小必须是分配单位的整数倍。
    • 如果最终计算出的结构体大小不是分配单位的整数倍,需要填充(padding)到分配单位的整数倍。

内存分配与对齐示例

假设我们有以下结构体:

struct Example {char a;   // 1字节,偏移0short b;  // 2字节,偏移2(1 + 1对齐到2)int c;    // 4字节,偏移4double d; // 8字节,偏移8
};
1. 确定分配单位
  • char:1字节
  • short:2字节
  • int:4字节
  • double:8字节

最大成员变量是double,占8字节,因此分配单位是8字节。

2. 计算每个成员变量的偏移量

按照结构体中成员变量的顺序,并根据它们自身长度的整数倍来计算偏移:

  1. char a
    • 起始地址:0(分配结构体的起始地址)
    • 长度:1字节
    • 偏移量:0(0是1的倍数)
  2. short b
    • 上一个成员a结束地址:1
    • 为满足2字节对齐,b的起始地址:2
    • 长度:2字节
    • 偏移量:2(2是2的倍数)
  3. int c
    • 上一个成员b结束地址:4
    • 为满足4字节对齐,c的起始地址:4
    • 长度:4字节
    • 偏移量:4(4是4的倍数)
  4. double d
    • 上一个成员c结束地址:8
    • 为满足8字节对齐,d的起始地址:8
    • 长度:8字节
    • 偏移量:8(8是8的倍数)
3. 计算结构体总大小
  • 最后一个成员d结束地址:16(8 + 8)
  • 结构体总大小必须是分配单位(8字节)的整数倍。

最终大小为16字节(已经是8的整数倍,无需额外填充)。

成员类型偏移量
achar0
bshort2
cint4
ddouble8

结构体总大小为16字节,满足对齐规则。

就是这样存的。

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

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

相关文章

第11章 规划过程组(二)(11.8排列活动顺序)

第11章 规划过程组&#xff08;二&#xff09;11.8排列活动顺序&#xff0c;在第三版教材第390~391页&#xff1b; 文字图片音频方式 第一个知识点&#xff1a;主要工具与技术&#xff08;重要知识点&#xff09; 1、箭线图法(ADM) &#xff08;双代号网络图或活动箭线图&am…

windows USB 设备驱动开发- 不同模型下的控制传输

在不同的模型下&#xff0c;USB控制传输会有不同的特点&#xff0c;但是任何控制传输的目标都始终是默认端点。 接收者是设备的实体&#xff0c;其信息&#xff08;描述符、状态等&#xff09;是主机感兴趣的。请求可进一步分为&#xff1a;配置请求、功能请求和状态请求。 发…

leetcode力扣_双指针问题

141. 环形链表 思路&#xff1a;判断链表中是否有环是经典的算法问题之一。常见的解决方案有多种&#xff0c;其中最经典、有效的一种方法是使用 快慢指针&#xff08;Floyd’s Cycle-Finding Algorithm&#xff09;。 初始化两个指针&#xff1a;一个快指针&#xff08;fast&…

uni-app 使用Pinia进行全局状态管理并持久化数据

1.引言 最近在学习移动端的开发&#xff0c;使用uni-app前端应用框架&#xff0c;通过学习B站的视频以及找了一个开发模板&#xff0c;终于是有了一些心得体会。 B站视频1&#xff1a;Day1-01-uni-app小兔鲜儿导学视频_哔哩哔哩_bilibili B站视频2&#xff1a;01-课程和uni的…

[FFmpeg] windows下安装带gpu加速的ffmpeg

1.显卡能力排查 目前只有 NIVIDIA 支持 ffmpeg 的 gpu加速(AMD貌似也陆续开始支持)。 在下述网站中查找自己的显卡能够支持的编解码格式。https://developer.nvidia.com/video-encode-and-decode-gpu-support-matrix-newhttps://developer.nvidia.com/video-encode-and-decod…

Vue88-Vuex中的mapActions、mapMutations

一、mapMutations的调用 此时结果不对&#xff0c;因为&#xff1a;若是点击事件不传值&#xff0c;默认传的是event&#xff01;&#xff0c;所以&#xff0c;修改如下&#xff1a; 解决方式1&#xff1a; 解决方式2&#xff1a; 不推荐&#xff0c;写法麻烦&#xff01; 1-…

【Unity数据交互】二进制私

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 秩沅 原创 &#x1f468;‍&#x1f4bb; 专栏交流&#x1f9e7;&…

电脑硬盘分区的基本步骤(2个实用的硬盘分区方法)

在现代计算机中&#xff0c;硬盘分区是非常重要的一步。无论是新硬盘的初始化&#xff0c;还是重新组织现有硬盘&#xff0c;分区都是必不可少的操作。本文将详细介绍电脑硬盘分区的基本步骤&#xff0c;帮助您更好地管理和利用硬盘空间。 文章开始&#xff0c;我们先简单说一…

【C++】 解决 C++ 语言报错:Invalid Conversion from ‘const char*’ to ‘char*’

文章目录 引言 在 C 编程中&#xff0c;类型转换错误&#xff08;Invalid Conversion&#xff09;是常见的编译错误之一。特别是当程序试图将一个常量字符指针&#xff08;const char*&#xff09;转换为非常量字符指针&#xff08;char*&#xff09;时&#xff0c;会导致编译…

Vmware环境下ESXi主机 配置上行链路、虚拟交换机、端口组、VMkernel网卡

一、适用场景 1、使用专业服务器跑多种不同的业务&#xff0c;每种业务可能所需运行的server环境不同&#xff0c;有的需要Linux server CentOS7/8、kali、unbuntu……有的需要windows server2008、2003、2016、2019、2022…… 2、本例采用的是VMware ESXi6.7 update 3版本&am…

力扣习题--找不同

目录 前言 题目和解析 1、找不同 2、 思路和解析 总结 前言 本系列的所有习题均来自于力扣网站LeetBook - 力扣&#xff08;LeetCode&#xff09;全球极客挚爱的技术成长平台 题目和解析 1、找不同 给定两个字符串 s 和 t &#xff0c;它们只包含小写字母。 字符串 t…

Java Maven中自动代码检查插件详细介绍

文章目录 Checkstyle主要特点使用场景配置与使用checkstyle.xmlsuppressions.xml 验证打包时验证执行命令验证 Spotless配置文件内容Java配置部分POM 配置部分Markdown 配置部分Up to Date Checking执行部分 验证打包时验证在插件中执行命令验证 Checkstyle Spotless 结合chec…

redis学习(005 java客户端 RedisTemplate学习)

黑马程序员Redis入门到实战教程&#xff0c;深度透析redis底层原理redis分布式锁企业解决方案黑马点评实战项目 总时长 42:48:00 共175P 此文章包含第16p-第p23的内容 文章目录 java客户端jedisSpringDataRedis项目实现hash哈希操作 java客户端 jedis 测试 ps:如果连接不上&…

vs2019 无法打开项目文件

vs2019 无法打开项目文件&#xff0c;无法找到 .NET SDK。请检查确保已安装此项且 global.json 中指定的版本(如有)与所安装的版本相匹配 原因&#xff1a;缺少组件 解决方案&#xff1a;选择需要的组件进行安装完成

步进电机改伺服电机

步进电机&#xff1a; 42&#xff1a;轴径5mm 57&#xff1a;轴径8mm 86&#xff1a;轴径14mm 【86CME120闭环】// 12牛米 伺服电机&#xff1a; 40&#xff1a; 60&#xff1a; 80&#xff1a; 86&#xff1a; ECMA——C 1 0910 R S 4.25A 轴径…

评价ChatGPT与强人工智能的未来

在人工智能领域&#xff0c;ChatGPT的出现无疑是一个里程碑事件。它不仅展示了自然语言处理技术的巨大进步&#xff0c;也引发了人们对于强人工智能&#xff08;AGI&#xff09;的无限遐想。本文将从多个角度评价ChatGPT&#xff0c;并探讨强人工智能距离我们还有多远。 ChatGP…

无人机运营合格证及无人机驾驶员合格证(AOPA)技术详解

无人机运营合格证及无人机驾驶员合格证&#xff08;AOPA&#xff09;技术详解如下&#xff1a; 一、无人机运营合格证 无人机运营合格证是无人机运营企业或个人必须获得的证书&#xff0c;以确保无人机在运营过程中符合相关法规和标准。对于无人机运营合格证的具体要求和申请…

无人机人员搜救

人员搜救-水域救援 水域搜救&#xff1a;快速水面搜查 物资抛投&#xff1a;救生物资抛投 绳索牵引&#xff1a;牵引救援绳索 领航船艇&#xff1a;水面侦察领航 人员搜救 昼夜搜救&#xff0c;精准定位 水域搜救 经纬 M300 RTK 搭载禅思 H20T 能够满足全天候作业需求&a…

新手教学系列——Git Stash踩坑

在之前的文章《如何彻底避免Git代码相互覆盖问题》中,我曾介绍过通过规范分支合并和使用git stash来避免代码覆盖问题。今天,我要深入探讨一下git stash的使用,并分享一些使用过程中遇到的坑,希望能帮你避免类似问题。 脚本mg.sh简介 为了更好地管理代码合并,我编写了一…

linux之管道重定向

管道与重定向 一、重定向 将原输出结果存储到其他位置的过程 标准输入、标准正确输出、标准错误输出 ​ 进程在运行的过程中根据需要会打开多个文件&#xff0c;每打开一个文件会有一个数字标识。这个标识叫文件描述符。 进程使用文件描述符来管理打开的文件&#xff08;FD--…