【C语言】结构体详解 -《探索C语言的 “小宇宙” 》

LuckiBit

目录

  • C语言结构体(`struct`)详解
    • 结构体概览表
    • 1. 结构体的基本概念
      • 1.1 结构体定义
      • 1.2 结构体变量声明
    • 2. 结构体成员的访问
      • 2.1 使用点运算符(`.`)访问成员
        • 输出
      • 2.2 使用箭头运算符(`->`)访问成员
        • 输出
    • 3. 结构体的初始化
      • 3.1 结构体初始化
        • 输出
      • 3.2 使用指定初始化器
        • 输出
    • 4. 结构体的大小
      • 输出
    • 5. 结构体作为函数参数
      • 5.1 传递结构体的副本
        • 输出
      • 5.2 传递结构体指针
        • 输出
    • 6. 结构体的嵌套
        • 输出
    • 7. 结构体与数组
        • 输出
    • 8. 结构体的内存对齐
      • 8.1 对齐示例
        • 输出
      • 8.2 结构体对齐与`#pragma pack`
        • 输出
    • 9. 类型定义(`typedef`)简化结构体声明
        • 输出
    • 10. 嵌入式系统中的应用
      • 10.1 示例:硬件寄存器配置
        • 输出
    • 11. 拓展技巧
      • 11.1 结构体指针的算术运算
        • 输出
      • 11.2 结构体与联合体(`union`)的比较
      • 示例:结构体与联合体的比较
        • 输出
    • 7. 结束语
    • 相关文章:

C语言结构体(struct)详解

结构体概览表

功能描述
定义结构体定义一个结构体类型
声明结构体变量声明一个结构体变量
访问成员使用点运算符(.)和箭头运算符(->)访问成员
初始化结构体在声明时初始化结构体
计算大小使用sizeof计算结构体的大小
作为函数参数传递结构体或结构体指针作为函数参数
结构体嵌套结构体中包含其他结构体
结构体与数组结构体作为数组元素或包含数组的成员
内存对齐结构体的内存对齐和填充
类型定义(typedef使用typedef简化结构体声明
嵌入式应用在嵌入式系统中使用结构体
拓展技巧结构体指针运算和联合体比较

1. 结构体的基本概念

1.1 结构体定义

结构体通过struct关键字定义。定义结构体时,需要指定结构体的名称以及结构体内部的成员变量。

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

在上面的示例中,定义了一个Person结构体,其中包含三个成员:name(字符数组)、age(整数)和height(浮点数)。

1.2 结构体变量声明

定义结构体后,可以声明结构体变量来使用它。例如:

struct Person person1;

这里声明了一个Person结构体类型的变量person1

2. 结构体成员的访问

2.1 使用点运算符(.)访问成员

可以通过点运算符(.)访问结构体的成员变量。例如:

#include <stdio.h>struct Person {char name[50];int age;float height;
};int main() {struct Person person1;// 初始化结构体成员person1.age = 25;person1.height = 175.5;snprintf(person1.name, sizeof(person1.name), "Alice");// 输出结构体成员printf("Name: %s\n", person1.name);printf("Age: %d\n", person1.age);printf("Height: %.2f\n", person1.height);return 0;
}
输出
Name: Alice
Age: 25
Height: 175.50

2.2 使用箭头运算符(->)访问成员

如果结构体变量是指针类型,则可以通过箭头运算符(->)访问其成员。例如:

#include <stdio.h>struct Person {char name[50];int age;float height;
};void printPerson(struct Person *p) {printf("Name: %s\n", p->name);printf("Age: %d\n", p->age);printf("Height: %.2f\n", p->height);
}int main() {struct Person person1 = {"Bob", 30, 180.0};struct Person *ptr = &person1;printPerson(ptr);return 0;
}
输出
Name: Bob
Age: 30
Height: 180.00

3. 结构体的初始化

3.1 结构体初始化

可以在定义结构体变量的同时进行初始化。例如:

#include <stdio.h>struct Person {char name[50];int age;float height;
};int main() {struct Person person2 = {"Alice", 30, 160.0};printf("Name: %s\n", person2.name);printf("Age: %d\n", person2.age);printf("Height: %.2f\n", person2.height);return 0;
}
输出
Name: Alice
Age: 30
Height: 160.00

3.2 使用指定初始化器

C99标准引入了指定初始化器,可以按顺序或指定成员进行初始化。例如:

#include <stdio.h>struct Person {char name[50];int age;float height;
};int main() {struct Person person3 = {.age = 40, .height = 180.0, .name = "Bob"};printf("Name: %s\n", person3.name);printf("Age: %d\n", person3.age);printf("Height: %.2f\n", person3.height);return 0;
}
输出
Name: Bob
Age: 40
Height: 180.00

4. 结构体的大小

结构体的大小取决于其成员的数量和类型,以及内存对齐的规则。可以使用sizeof运算符来获取结构体的大小。例如:

#include <stdio.h>struct Person {char name[50];int age;float height;
};int main() {printf("Size of Person: %zu bytes\n", sizeof(struct Person));return 0;
}

输出

在不同平台上的输出可能不同,例如:

  • 32位系统Size of Person: 60 bytes
  • 64位系统Size of Person: 64 bytes

5. 结构体作为函数参数

5.1 传递结构体的副本

结构体可以作为函数参数传递。如果传递的是结构体的副本,则会创建结构体的一个副本,可能会影响性能。

#include <stdio.h>struct Person {char name[50];int age;float height;
};void printPerson(struct Person p) {printf("Name: %s\n", p.name);printf("Age: %d\n", p.age);printf("Height: %.2f\n", p.height);
}int main() {struct Person person1 = {"Alice", 30, 160.0};printPerson(person1);return 0;
}
输出
Name: Alice
Age: 30
Height: 160.00

5.2 传递结构体指针

为了提高效率,可以将结构体的指针传递给函数,这样只需传递指针而不是整个结构体。

#include <stdio.h>struct Person {char name[50];int age;float height;
};void updateAge(struct Person *p, int newAge) {p->age = newAge;
}int main() {struct Person person1 = {"Bob", 30, 180.0};updateAge(&person1, 35);printf("Updated Age: %d\n", person1.age);return 0;
}
输出
Updated Age: 35

6. 结构体的嵌套

结构体可以嵌套其他结构体。例如:

#include <stdio.h>struct Address {char street[100];char city[50];int postalCode;
};struct Person {char name[50];int age;struct Address address;  // 嵌套的结构体
};int main() {struct Person person4 = {"John",28,{"123 Main St", "Metropolis", 12345}};printf("Name: %s\n", person4.name);printf("Address: %s, %s %d\n", person4.address.street, person4.address.city, person4.address.postalCode);return 0;
}
输出
Name: John
Address: 123 Main St, Metropolis 12345

7. 结构体与数组

结构体可以作为数组的元素,也可以包含数组作为成员。例如:

#include <stdio.h>struct Person {char name[50];int age;float height;
};int main() {struct Person people[2] = {{"Alice", 30, 160.0},{"Bob", 40, 180.0}};for (int i = 0; i < 2; i++) {printf("Person %d: %s, %d, %.2f\n", i+1, people[i].name, people[i].age, people[i].height);}return 0;
}
输出
Person 1: Alice, 30, 160.00
Person 2: Bob, 40, 180.00

8. 结构体的内存对齐

结构体的内存对齐与填充是为了提高数据访问的效率。在C语言中,结构体的内存布局可能会受到对齐要求的影响,导致结构体的实际大小可能大于成员变量总和的大小。编译器通常会在成员之间插入填充字节,以确保每个成员的地址对齐。

8.1 对齐示例

#include <stdio.h>struct Example {char c;       // 1 byteint i;        // 4 bytes, 3 bytes of paddingshort s;      // 2 bytes
};int main() {printf("Size of Example: %zu bytes\n", sizeof(struct Example));return 0;
}
输出
Size of Example: 12 bytes

在这个例子中,Example结构体的大小是12字节。虽然char占1字节,int占4字节,short占2字节,成员变量的总和为7字节,但由于内存对齐要求,Example结构体实际上占用12字节。

8.2 结构体对齐与#pragma pack

在某些情况下,可以使用#pragma pack指令来控制结构体的对齐方式,从而减少内存占用。

#include <stdio.h>#pragma pack(1)  // 设置结构体对齐为1字节struct PackedExample {char c;int i;short s;
};#pragma pack()  // 恢复默认对齐int main() {printf("Size of PackedExample: %zu bytes\n", sizeof(struct PackedExample));return 0;
}
输出
Size of PackedExample: 7 bytes

使用#pragma pack(1)可以将PackedExample结构体的对齐方式设置为1字节,从而减少结构体的实际大小为7字节,但可能会影响访问效率。

9. 类型定义(typedef)简化结构体声明

使用typedef可以为结构体定义一个新的类型名,使得结构体的声明更加简洁。例如:

#include <stdio.h>typedef struct {char name[50];int age;float height;
} Person;int main() {Person person1 = {"Charlie", 28, 175.0};printf("Name: %s\n", person1.name);printf("Age: %d\n", person1.age);printf("Height: %.2f\n", person1.height);return 0;
}
输出
Name: Charlie
Age: 28
Height: 175.00

10. 嵌入式系统中的应用

在嵌入式系统中,结构体用于管理硬件寄存器、配置参数以及存储设备状态等。结构体能够帮助开发者以更结构化的方式访问硬件资源,提高代码的可读性和维护性。

10.1 示例:硬件寄存器配置

#include <stdio.h>
#include <stdint.h>// 定义硬件寄存器配置结构体
typedef struct {volatile uint32_t CONTROL;volatile uint32_t STATUS;volatile uint32_t DATA;
} UART_RegDef_t;int main() {UART_RegDef_t UART1;  // 假设这是一个UART寄存器的实例// 设置UART寄存器UART1.CONTROL = 0x01;  // 启用UARTUART1.STATUS = 0x00;   // 清除状态UART1.DATA = 0x55;     // 发送数据// 打印寄存器的配置printf("UART1 CONTROL: 0x%X\n", UART1.CONTROL);printf("UART1 STATUS: 0x%X\n", UART1.STATUS);printf("UART1 DATA: 0x%X\n", UART1.DATA);return 0;
}
输出
UART1 CONTROL: 0x1
UART1 STATUS: 0x0
UART1 DATA: 0x55

在这个示例中,结构体UART_RegDef_t用于表示UART寄存器的配置,包含CONTROLSTATUSDATA寄存器。通过这种方式,可以方便地设置和读取寄存器的值。

11. 拓展技巧

11.1 结构体指针的算术运算

可以对结构体指针进行算术运算,通常用于数组访问。例如:

#include <stdio.h>struct Person {char name[50];int age;float height;
};int main() {struct Person people[3] = {{"Alice", 30, 160.0},{"Bob", 40, 180.0},{"Charlie", 25, 170.0}};struct Person *ptr = people;  // 指向结构体数组的指针for (int i = 0; i < 3; i++) {printf("Person %d: %s, %d, %.2f\n", i+1, (ptr+i)->name, (ptr+i)->age, (ptr+i)->height);}return 0;
}
输出
Person 1: Alice, 30, 160.00
Person 2: Bob, 40, 180.00
Person 3: Charlie, 25, 170.00

11.2 结构体与联合体(union)的比较

结构体和联合体都可以存储多个数据项,但结构体的每个成员都占有独立的内存空间,而联合体的所有成员共享同一块内存。使用结构体时每个成员都可用,而使用联合体时只有一个成员可以使用。

示例:结构体与联合体的比较

#include <stdio.h>typedef union {int intValue;float floatValue;char charValue;
} UnionType;typedef struct {int intValue;float floatValue;char charValue;
} StructType;int main() {UnionType u;StructType s;u.intValue = 10;printf("Union intValue: %d\n", u.intValue);u.floatValue = 5.5;  // 修改联合体中的值printf("Union floatValue: %f\n", u.floatValue);// 注意:这时intValue的值是不确定的s.intValue = 10;s.floatValue = 5.5;s.charValue = 'A';printf("Struct intValue: %d\n", s.intValue);printf("Struct floatValue: %f\n", s.floatValue);printf("Struct charValue: %c\n", s.charValue);return 0;
}
输出
Union intValue: 10
Union floatValue: 5.500000
Struct intValue: 10
Struct floatValue: 5.500000
Struct charValue: A

7. 结束语

  1. 本节内容已经全部介绍完毕,希望通过这篇文章,大家对C语言中的结构体 struct 有了更深入的理解和认识。
  2. 感谢各位的阅读和支持,如果觉得这篇文章对你有帮助,请不要吝惜你的点赞和评论,这对我们非常重要。再次感谢大家的关注和支持!点我关注❤️

相关文章:

  • 指针的神秘探险:从入门到精通的奇幻之旅 !

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

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

相关文章

【CSS】1 像素问题

CSS 中的 1 像素问题指的是在⾼分辨率屏幕上显示的 1 像素边框或者细线在实际显示时会⽐ 1 个物理像素更宽或更粗&#xff0c;从⽽导致边框或者细线看上去⽐预期的更粗或者更宽。 造成这个问题的原因是由于⾼分辨率屏幕的像素密度⽐传统的屏幕要⾼&#xff0c;所以在屏幕上显示…

一个C++模板工厂的编译问题的解决。针对第三方库的构造函数以及追加了的对象构造函数。牵扯到重载、特化等

一窥模板的替换和匹配方式&#xff1a;偏特化的参数比泛化版本的还要多&#xff1a;判断是不是std::pair&#xff1c;,&#xff1e;。_stdpair模板参数太多-CSDN博客 简介 在一个项目里&#xff0c;调用了第三封的库&#xff0c;这个库里面有个类用的很多&#xff0c;而且其构…

边界网关IPSEC VPN实验

拓扑&#xff1a; 实验要求&#xff1a;通过IPSEC VPN能够使PC2通过网络访问PC3 将整个路线分为三段 IPSEC配置在FW1和FW2上&#xff0c;在FW1与FW2之间建立隧道&#xff0c;能够传递IKE&#xff08;UDP500&#xff09;和ESP数据包&#xff0c;然后在FW1与PC2之间能够流通数据…

GitHub 详解教程

1. 引言 GitHub 是一个用于版本控制和协作的代码托管平台&#xff0c;基于 Git 构建。它提供了强大的功能&#xff0c;使开发者可以轻松管理代码、追踪问题、进行代码审查和协作开发。 2. Git 与 GitHub 的区别 Git 是一个分布式版本控制系统&#xff0c;用于跟踪文件的更改…

学术研讨 | 基于区块链的隐私计算与数据可信流通研讨会顺利召开

近日&#xff0c;由国家区块链技术创新中心组织的“基于区块链的隐私计算与数据可信流通研讨会”顺利召开&#xff0c;会议邀请了来自全国高校和科研院所的相关领域专家&#xff0c;围绕基于区块链与隐私计算技术的应用需求、研究现状、发展趋势、重点研究方向与研究进展等内容…

Go并发GMP调度模型

如何知道一个对象是分配在栈上还是堆上&#xff1f; Go和C不同&#xff0c;Go的逃逸分析是在编译器完成的&#xff1b;go局部变量会进行逃逸分析。如果变量离开作用域后没有被引用&#xff0c;则优先分配到栈上&#xff0c;否则分配到堆上。那么如何判断是否发生了逃逸呢&#…

数据结构之《队列》

在数据结构之《栈》章节中学习了线性表中除了顺序表和链表外的另一种结构——栈&#xff0c;在本篇中我们将继续学习另一种线性表的结构——队列&#xff0c;在通过本篇的学习后&#xff0c;你将会对栈的结构有充足的了解&#xff0c;在了解完结构后我们还将进行栈的实现。一起…

vue3-02声明响应式状态ref()

一、使用 组合式 API 中&#xff0c;推荐使用 ref() 函数来声明响应式状态&#xff0c;例如 import { ref } from vue const count ref(0)注意点1&#xff1a;若想获取ref定义的参数&#xff0c;必须获取参数的value值&#xff0c; 比如&#xff1a; console.log(count, co…

2024海外电商数据分析之南美篇

南美洲&#xff0c;一片广袤而充满活力的大陆&#xff0c;以其独特的地理位置和丰富的自然资源&#xff0c;孕育了15个国家和4.3亿人口。与北美洲的三国&#xff08;美国、加拿大和墨西哥&#xff09;及中美洲的七国相比&#xff0c;南美洲以其年轻的人口结构和巨大的市场潜力&…

【LLM】-08-搭建问答系统-语言模型,提问范式与 Token

目录 1、语言模型 1.1、训练过程&#xff1a; 1..2、大型语言模型分类&#xff1a; 1.3、指令微调模型训练过程&#xff1a; 2、Tokens 3、Helper function辅助函数 (提问范式) 4、计算token数量 1、语言模型 大语言模型&#xff08;LLM&#xff09;是通过预测下一个词…

一款允许使用Docker部署本地托管的、基于 Web 的 PDF 操作工具

大家好&#xff0c;今天给大家分享的是一个基于Spring Boot开发的开源项目&#xff0c;旨在提供一个功能强大的基于Docker的本地托管PDF操作工具Stirling PDF。 项目介绍 Stirling-PDF是一个全面的PDF工具箱&#xff0c;适用于个人和企业用户&#xff0c;尤其对于那些重视数据…

js 优雅的实现模板方法设计模式

在JavaScript中&#xff0c;优雅地实现模板方法设计模式通常意味着我们要遵循一些最佳实践&#xff0c;如清晰地定义算法的骨架&#xff08;模板方法&#xff09;&#xff0c;并确保子类能够灵活地扩展或修改这些算法中的特定步骤。由于JavaScript是一种动态语言&#xff0c;我…

CasaOS设备使用Docker安装SyncThing文件同步神器并实现远程管理

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

巨量数据表分页问题

1 背景 分页性能问题,之前遇到过这类问题,拿出来再讨论下 2 分析 分页性能问题,特别是在数据量大的情况下,是一个常见的问题。通常,当我们使用类似 LIMIT 和 OFFSET 的SQL语句进行分页时,性能问题尤其明显。这是因为随着 OFFSET 的增加,数据库需要跳过更多的行才能获…

C++树形结构(1 基础)

目录 一.基础&#xff1a; 1.概念&#xff1a; 2.定义&#xff1a; Ⅰ.树的相关基础术语&#xff1a; Ⅱ.树的层次&#xff1a; 3.树的性质&#xff1a; 二.存储思路&#xff1a; 1.结构体存储&#xff1a; 2.数组存储&#xff1a; 三.树的遍历模板&#xff1a; 四.信…

用 python scipy 库模拟拥塞控制模型

接着昨天的继续说&#xff0c;参见 inflight 守恒建模。 欧拉数值解看起来不够优雅&#xff0c;所以我打算找个别的方式试一下&#xff0c;顺便学一下 python&#xff0c;我不会编程&#xff0c;但也不是一点也不会&#xff0c;我稍微会一点&#xff0c;所以想进一步学习一点。…

记录unraid docker更新的域名

背景&#xff1a;级联 一、安装内容 unraid更新docker&#xff0c;之前一直失败&#xff0c;修改网络后可以进行安装。 二、查看域名 查看域名&#xff0c;发现是走github的&#xff0c;怪不得有一些docker无法正常更新 三、解决方法 更改代理&#xff0c;这里为unraid的…

STM32智能城市交通管理系统教程

目录 引言环境准备智能城市交通管理系统基础代码实现&#xff1a;实现智能城市交通管理系统 4.1 数据采集模块 4.2 数据处理与控制模块 4.3 通信与网络系统实现 4.4 用户界面与数据可视化应用场景&#xff1a;城市交通管理与优化问题解决方案与优化收尾与总结 1. 引言 智能城…

Vue2高级用法

Vue2高级用法 1、mixin复用【vue不会用了&#xff0c;了解一下】1.1 基础使用1.2 选项合并1.3 全局混入1.4 细数 mixin 存在的问题 2、vue.js 动画特效&#xff06; 常见组件库介绍2.1 进入/离开基础使用示例2.2 进入/离开自定义过度类名2.3 进入/离开动画钩子2.4 多组件过渡与…

c++树(一)定义,遍历

目录 树的定义 树的基本术语 树的初始起点&#xff1a;我们定义为根 树的层次&#xff1a; 树的定义&#xff1a; 树的性质 性质1&#xff1a; 性质2&#xff1a; 树形结构存储的两种思路 树的遍历模板 树上信息统计方式1-自顶向下统计 树上信息统计方式2-自底向上统…