深入了解c语言中的结构体

news2024/11/28 20:42:18

介绍:

在C语言中,结构体是一种用户自定义的数据类型,它允许我们将不同类型的数据组合在一起,形成一个更为复杂的数据结构。结构体可以用来表示现实世界中的实体,如人员、学生、图书等。本篇博客将介绍结构体的基本概念、定义和使用方法。


结构体的基本概念:

什么是结构体:

结构体是一种由不同类型的数据组成的数据类型,它允许我们将多个相关的数据字段组合在一起,形成一个逻辑上的整体。

为什么需要结构体:

结构体使得我们可以更加灵活地组织数据,将相关的数据字段打包在一起,方便进行管理和操作。


 结构体的声明和定义:

结构体的声明:

在c语言中,结构体的声明使用struct关键字来定义,其一般形式如下:

struct
{
    数据类型 成员1;
    数据类型 成员2;
    // ... 更多成员
} 变量名;

例如, 定义一个表示学生信息的结构体可以如下所示:

struct Student 
{
    int id;//学号
    char name[20];//姓名
    float score;//分数
};

结构体的定义 :

在声明结构体后,可以通过该结构体的名称来定义结构体变量,其一般形式如下:

struct 结构体名 变量名;

例如,定义一个学生的结构体变量可以如下设置:

struct Student stu1;

结构体的成员访问: 

可以使用成员访问运算符  来访问结构体变量的成员,例如:

stu1.id = 1001;
strcpy(stu1.name, "Alice");
stu1.score = 95.5;

在上面的示例中,我们分别给stu1的成员idnamescore赋值。

结构体的匿名声明:

在一些情况下,也可以直接在声明结构体变量的同时定义结构体,称为匿名声明,其形式如下:

struct 
{
    数据类型 成员1;
    数据类型 成员2;
    // ... 更多成员
} 变量名;

例如:

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

但我建议还是少使用匿名声明,避免访问时遇到超过两个匿名的结构体,发生错误。


结构体的应用: 

结构体的初始化:

成员列表初始化是一种简洁的初始化结构体变量的方法,它允许我们在声明结构体变量的同时给成员赋初值。具体方法是在声明结构体变量时使用花括号{ }括起来的成员初始化列表。

// 定义一个包含姓名和年龄的结构体
struct Person 
{
    char name[20];
    int age;
};

int main() 
{
    // 使用成员列表初始化结构体变量
    struct Person person1 = {"Alice", 25};
    // 可以只初始化部分成员
    struct Person person2 = {.age = 30};

    return 0;
}

使用memset函数进行初始化:

另一种初始化结构体变量的方法是使用memset函数,memset函数可以将一块内存空间设置为指定的值。当结构体变量中的所有成员都是基本数据类型时,可以使用memset函数将结构体变量的内存空间全部设置为0,达到初始化的效果。

#include <string.h>

// 定义一个包含姓名和年龄的结构体
struct Person 
{
    char name[20];
    int age;
};

int main() 
{
    // 使用memset函数初始化结构体变量
    struct Person person;
    memset(&person, 0, sizeof(person)); // 将person结构体变量的内存空间全部设置为0

    return 0;
}

需要注意的是,使用memset函数进行初始化时,结构体变量的所有成员都会被设置为0。如果结构体中包含指针等复杂类型的成员,使用memset函数进行初始化可能会导致意想不到的问题,因此在这种情况下最好使用成员列表初始化或者逐个赋值的方式进行初始化。

结构体的初始化方法可以根据具体的情况选择,成员列表初始化适用于简单的结构体变量初始化,而memset函数则可以用于对结构体变量进行快速的清零操作。


结构体的嵌套:

结构体的嵌套是指在一个结构体中包含另一个结构体作为其成员。这种嵌套的结构体可以帮助我们更好地组织和管理复杂的数据结构,使得代码更加清晰和易于维护。接下来我们将讨论结构体中嵌套结构体的用法,以及如何访问嵌套结构体的成员。

例如:

// 定义一个表示日期的结构体
struct Date 
{
    int day;
    int month;
    int year;
};

// 定义一个表示学生的结构体,其中嵌套了日期结构体
struct Student 
{
    int id;
    char name[50];
    struct Date dob; // 嵌套了日期结构体
};

在上面的例子中,结构体Student中嵌套了结构体Date作为其成员。这样一来,Student结构体就包含了学生的ID、姓名以及出生日期等信息。

访问嵌套结构体成员:

要访问嵌套结构体的成员,可以使用成员访问运算符 来访问。例如:

struct Student s1;
s1.id = 1001;
strcpy(s1.name, "Alice");
s1.dob.day = 15; // 访问嵌套结构体的成员
s1.dob.month = 6;
s1.dob.year = 2000;

在上面的例子中,我们首先创建了一个Student类型的结构体s1,然后通过.运算符访问了嵌套在其中的Date结构体的成员,分别给出生日期的daymonthyear赋值。


结构体的指针:

当我们需要在程序中操作结构体变量时,使用指向结构体的指针是一种非常有效的方法。通过结构体指针,我们可以直接访问和修改结构体的成员,而无需对整个结构体进行复制或传递。以下是关于结构体指针的介绍:


结构体指针的定义和初始化:

在C语言中,我们可以使用以下语法定义一个指向结构体的指针:

struct Person 
{
    char name[50];
    int age;
};

struct Person person1; // 定义一个结构体变量
struct Person *ptrPerson; // 定义一个指向结构体的指针

ptrPerson = &person1; // 将指针指向结构体变量

通过指针访问结构体成员:

一旦我们有了指向结构体的指针,就可以使用箭头运算符(->)来访问结构体的成员:

ptrPerson->age = 25; // 通过指针修改结构体成员的值
printf("Name: %s, Age: %d", ptrPerson->name, ptrPerson->age); // 通过指针访问结构体成员的值

结构体指针在函数参数传递和动态内存分配等方面有着广泛的应用。通过传递结构体指针,我们可以避免在函数调用中复制整个结构体,提高程序的效率。同时,结构体指针也可以用于动态分配内存,允许我们在运行时动态创建和管理结构体对象。


结构体的高级应用:

结构体的位域:

结构体位域是C语言中一种特殊的结构体成员类型,它允许我们在一个结构体成员中存储多个字段,每个字段只占用指定数量的位。结构体位域可以用于优化内存空间的利用,尤其是在需要存储大量布尔类型数据或者需要节省内存空间时非常有用。

结构体位域的定义:

结构体位域的定义方式与普通结构体成员略有不同,需要在成员名后面加上冒号和位域长度。例如:

struct BitFieldStruct 
{
    unsigned int flag1 : 1; // 1位的位域
    unsigned int flag2 : 3; // 3位的位域
    // 其他成员...
};

结构体位域的使用方法:

定义了结构体位域后,可以像普通结构体成员一样使用:

struct BitFieldStruct b;
b.flag1 = 1;
b.flag2 = 3;

优化内存空间的使用:

结构体位域可以帮助节省内存空间,特别是在需要存储大量布尔类型数据时。例如,在需要存储开关状态的数据结构中,可以使用结构体位域来节省内存空间:

struct Flags 
{
    unsigned int flag1 : 1; // 1位,用于表示开关状态
    unsigned int flag2 : 1; // 1位,用于表示开关状态
    unsigned int flag3 : 1; // 1位,用于表示开关状态
    // 更多开关状态...
};

 在这个例子中,使用了3个1位的位域来表示3个开关状态,这样可以将这3个开关状态压缩到一个整数的存储空间中,从而节省内存。

注意事项:

  • 结构体位域的长度不能超过成员类型的长度,否则会产生未定义的行为。
  • 结构体位域的布局和大小取决于编译器的实现,可能在不同编译器上有不同的表现,因此在跨平台开发时需要特别注意。

结构体的对齐:

结构体对齐的原理是为了满足平台对数据类型访问的要求,例如某些平台要求访问特定数据类型的内存地址必须是特定值的倍数。这样做可以提高内存访问的效率,但有时也会导致内存空间的浪费。

在C语言中,可以使用#pragma pack指令来控制结构体的对齐方式。#pragma pack指令告诉编译器以指定的字节对齐方式对结构体进行打包。

#include <stdio.h>

// 取消默认的对齐方式
#pragma pack(1)

// 定义一个结构体
struct MyStruct 
{
    char a;
    int b;
    char c;
};

int main() 
{
    // 输出结构体的大小
    printf("Size of MyStruct: %lu\n", sizeof(struct MyStruct));
    return 0;
}

在上述示例中,取消默认的对齐方式后,结构体MyStruct的大小是6字节。这是由于char类型通常为1字节,int类型通常为4字节,在取消对齐后,它们按照实际大小依次排列,因此结构体的大小为1(char) + 4(int) + 1(char) = 6 字节。

注意,pack()里面只能填入2的次方倍,如1,2,4,8,16 

希望该文章能帮助你理解结构体。 

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

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

相关文章

Token 和 N-Gram、Bag-of-Words 模型释义

ChatGPT&#xff08;GPT-3.5&#xff09;和其他大型语言模型&#xff08;Pi、Claude、Bard 等&#xff09;凭何火爆全球&#xff1f;这些语言模型的运作原理是什么&#xff1f;为什么它们在所训练的任务上表现如此出色&#xff1f; 虽然没有人可以给出完整的答案&#xff0c;但…

使用node实现链接数据库并对数据库进行增删改查的后端接口

环境 node npm 编辑器 vscode 项目配置 新建目录 用vscode打开 终端输入 npm init -y npm install mysql npm install express 代码 安装好之后的代码页面 新建 在根目录新建api.js文件 const express require(express); const db require(./db/index); const app…

国产智能运维操作系统新选择-浪潮KeyarchOS

1.背景 在CentOS停更&#xff0c;国有企业纷纷摒弃原有的开发与运维工具&#xff0c;全面拥抱国产。我司也顺应号召&#xff0c;更换原有CentOS系统。 在新系统选型上&#xff0c;我司有以下要求&#xff1a; 国产、快速更新迭代、社区活跃&#xff1b;拥有一定知名度&#x…

数据库Delete的多种用法

数据库的Delete操作是用来删除数据库中的数据记录的&#xff0c;它是数据库操作中的一种重要操作&#xff0c;能够帮助用户删除不需要的数据&#xff0c;以便保持数据库的整洁和高效。在使用Delete操作时&#xff0c;需要注意确保操作的准确性和安全性&#xff0c;以免误删重要…

【Mysql】聚合函数和分组(查找)

聚合函数 函数说明COUNT([DISTINCT] expr)返回查询到的数据的 数量SUM([DISTINCT] expr)返回查询到的数据的 总和&#xff0c;不是数字没有意义AVG([DISTINCT] expr)返回查询到的数据的 平均值&#xff0c;不是数字没有意义MAX([DISTINCT] expr)返回查询到的数据的 最大值&…

最新发布SPAB模块,YOLOv5改进之SPAB

目录 一、原理 二、代码 三、应用到YOLOv5 一、原理 单幅图像超分辨率(SISR)是低分辨率计算机视觉中的一项重要任务,旨在从低分辨率图像中重建高分辨率图像。传统的注意机制虽然显著提高了S

批量获取 Btsow 网站搜索到的磁力链接

BTSOW 是个不错的种子或磁力链接搜索网站&#xff0c;本脚本可以按需要批量抓取 磁力链接&#xff08;老司机必备&#xff09; 功能: 1.获取最新地址&#xff08;网站经常失联&#xff09; 2.最多50页内容&#xff08;网站限制&#xff09; 3.结果生成CSV文件&#xff08;可用e…

全网最新最牛的Appium自动化:Appium常用操作之TouchAction操作

TouchAction操作 Appium的辅助类&#xff0c;主要针对手势操作&#xff0c;比如滑动、长按、拖动等。其原理是将一系列的动作放在一个链条中&#xff0c;然后将该链条传递给服务器。服务器接受到该链条后&#xff0c;解析各个动作&#xff0c;逐个执行。 TouchAction类支持的动…

Verilog基础:编译指令`timescale

相关阅读 Verilog基础https://blog.csdn.net/weixin_45791458/category_12263729.html?spm1001.2014.3001.5482 timescale编译指令用于指定指令后模块的时间单位和时间精度。时间单位是时间值的度量单位&#xff0c;例如延迟值和仿真时间&#xff1b;而仿真精度决定了最小可分…

《SpringBoot项目实战》第六篇—接口防抖(防重复提交)的一些方式

系列文章导航 第一篇—接口参数的一些弯弯绕绕 第二篇—接口用户上下文的设计与实现 第三篇—留下用户调用接口的痕迹 第四篇—接口的权限控制 第五篇—接口发生异常如何统一处理 第六篇—接口防抖(防重复提交)的一些方式 本文参考项目源码地址&#xff1a;summo-springboot-i…

15.Servlet [一篇通]

文章目录 1.Servlet 是什么2.第一个 Servlet 程序2.1创建项目2.2引入依赖2.3创建目录2.4编写代码2.5打包程序2.6部署程序2.7验证程序 3.更方便的部署方式3.1安装 Smart Tomcat 插件3.2配置 Smart Tomcat 插件 4.访问出错怎么办?4.1出现 4044.2出现 4054.3出现 5004.4出现 &quo…

前端面试高频考点—事件循环Event loop

目录 事件循环 执行步骤 概念讲解 主线程 微任务(micro task) 宏任务(macro task) Event Loop经典例题 这段代码的执行结果是什么&#xff1f; 正确答案&#xff1a; 具体流程&#xff1a; 事件循环 主线程从"任务队列"中读取执行事件&#xff0c;这个过程…

WeiPHP 微信开发平台 SQL注入漏洞复现

0x01 产品简介 weiphp 是一个开源,高效,简洁的微信开发平台,基于 oneThink 内容管理框架实现。 0x02 漏洞概述 weiphp 微信开发平台 _send_by_group、 wp_where、 get_package_template等接口处存在 SQL 注入漏洞,攻击者利用此漏洞可获取数据库中的信息(例如,管理员后台…

【Docker】部署若依项目——保姆级教程亲测

目录 1.部署后端 步骤1&#xff1a;修改mysql地址 步骤2&#xff1a;修改redis地址 步骤3&#xff1a;修改后端端口 步骤4&#xff1a;将项目打包成Jar包 ​编辑 步骤5&#xff1a;编写Dockerfile 步骤6&#xff1a;制作镜像 启动容器 步骤7&#xff1a;访问 2.前端打…

国产接口测试工具APIpost

说实话&#xff0c;了解APIpost是因为&#xff0c;我的所有接口相关的文章下&#xff0c;都有该APIpost水军的评论&#xff0c;无非就是APIpost是中文版的postman&#xff0c;有多么多么好用&#xff0c;虽然咱也还不是什么啥网红&#xff0c;但是不知会一声就乱在评论区打广告…

论文阅读-Null-text Inversion for Editing Real Images using Guided Diffusion Models

一、论文信息 作者团队&#xff1a; 论文链接&#xff1a;https://arxiv.org/pdf/2211.09794.pdf 代码链接&#xff1a;https://github.com/google/prompt-to-prompt 二、Conditional Diffusion&#xff08;classifier-free guidance) Classifier-free guidance方法训练&…

Honeywell PM43 loadfile.lp RCE漏洞复现(CVE-2023-3710)

0x01 产品简介 Honeywell PM43 是美国霍尼韦尔&#xff08;Honeywell&#xff09;公司的一款打印机产品。 0x02 漏洞概述 Honeywell PM43 P10.19.050004之前版本存在输入验证错误漏洞&#xff0c;攻击者可通过该漏洞在服务器端任意执行代码&#xff0c;写入后门&#xff0c;获…

【数据结构】链表OJ题(顺序表)(C语言实现)

✅✅✅✅✅✅✅✅✅✅✅✅✅✅✅✅ ✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨ &#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1…

minio配置监听(对象操作日志)

minio配置监听对象操作 本文档适用于minio2021.3.17版本 有时我们需要查看minio中对象操作的日志&#xff0c;比如像监听minio某一个桶中的删除事件&#xff0c;就需要配置监听。minio支持将监听的结果输出到es、pg、amq等等&#xff0c;下面介绍一下将minio对象操作监听结果输…

leetcode:1422. 分割字符串的最大得分(python3解法)

难度&#xff1a;简单 给你一个由若干 0 和 1 组成的字符串 s &#xff0c;请你计算并返回将该字符串分割成两个 非空 子字符串&#xff08;即 左 子字符串和 右 子字符串&#xff09;所能获得的最大得分。 「分割字符串的得分」为 左 子字符串中 0 的数量加上 右 子字符串中 1…