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

news2024/9/22 5:33:21

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 byte
    int i;        // 4 bytes, 3 bytes of padding
    short 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;  // 启用UART
    UART1.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.coloradmin.cn/o/1947998.html

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

相关文章

【NPU 系列专栏 2 -- NVIDIA 的 H100 和 H200 是什么?】

请阅读【嵌入式及芯片开发学必备专栏】 文章目录 NVIDIA H100 和 H200 芯片NVIDIA H100 芯片简介NVIDIA H100 主要特点NVIDIA H100 应用场景NVIDIA H100 使用举例NVIDIA H200 芯片简介NVIDIA H200 主要特点NVIDIA H200 应用场景NVIDIA H200 使用举例Summary NVIDIA H100 和 H20…

How do I increase max_new_tokens

题意&#xff1a;怎样增加 max_new_tokens 的值 问题背景&#xff1a; Im facing this error while running my code: 运行代码时遇到如下错误&#xff1a; ValueError: Input length of input_ids is 1495, but max_length is set to 20. This can lead to unexpected…

lua 游戏架构 之 游戏 AI (五)ai_autofight_find_way

这段Lua脚本定义了一个名为 ai_autofight_find_way 的类&#xff0c;继承自 ai_base 类。 lua 游戏架构 之 游戏 AI &#xff08;一&#xff09;ai_base-CSDN博客文章浏览阅读238次。定义了一套接口和属性&#xff0c;可以基于这个基础类派生出具有特定行为的AI组件。例如&…

Vue实现简单小案例

一、创建文件夹 二、引用vue.js <script src"../js/vue.js"></script> 三、准备一个容器 <div id"app"><h1>Hello,{{name}}</h1> </div> 四、创建实例 <script>new Vue({el:"#app", //el用于指…

tcache attack

Tcache Attack tcache让堆利用更加简单&#xff1a; tcache回顾&#xff1a; 在 tcache 中新增了两个结构体&#xff0c;分别是 tcache_entry 和 tcache_perthread_struct&#xff1a; /* We overlay this structure on the user-data portion of a chunk when the chunk is …

数据库概念以及增删改

1.概念 、 2.通用语法 3.数据增删改

mac大文件清理软件哪个好 mac大文件怎么清理 苹果电脑清理软件推荐免费

mac采用固态硬盘技术&#xff0c;数据存储和系统响应速度优势明显&#xff0c;但是mac的内存弊端同样体现在其固态硬盘的技术&#xff0c;导致用户无法通过机械硬盘进行扩充内存。而我们日常使用电脑会产生大量系统垃圾、用户缓存等文件&#xff0c;平时下载的电影和大型游戏安…

通信原理-实验六:实验测验

实验六 实验测验 一&#xff1a;测验内容和要求 测试需要完成以下几个步骤&#xff1a; 配置好以下网络图&#xff1b;占总分10%&#xff08;缺少一个扣一分&#xff09;根据下面图配置好对应的IP和网关以及路由等相关配置&#xff0c;保证设备之间连通正常&#xff1b;占总…

【日常记录】【JS】对一个数组,按照某个字段的值,进行分组

文章目录 1. 前言2. lodash 的分组3. Object.groupBy()参考链接 1. 前言 在开发中&#xff0c;经常会遇到一组数据&#xff0c;要按照某个字段进行分组&#xff0c;这个时候会有很多种方法&#xff0c;可以使用 forEach、reduce、等其他方法 reduce 方法 function groupBy(arr…

JS基础知识学习笔记全

JS基础知识学习笔记全 一、引入方式 1、内部脚本 &#xff08;一般定义在body下面会改善执行速度&#xff09; <body></body><!-- 内部脚本 --><script>/* 打开页面警告框显示的内容 */alert(helloJS);</script>2、外部脚本 外部专门新建一…

Redis-主从模式

目录 前言 一.主从节点介绍 二.配置redis主从结构 二.主从复制 四.拓扑结构 五.数据同步 全量复制&#xff08;Full Sync Replication&#xff09; 局部复制&#xff08;Partial Replication&#xff09; Redis的学习专栏&#xff1a;http://t.csdnimg.cn/a8cvV 前言 …

H3CNE(vlan与子接口技术)

目录 10.1 vlan间通信技术 示例一&#xff08;多臂路由&#xff09;&#xff1a; 10.2 子接口技术 示例二&#xff08;子接口技术&#xff09;&#xff1a; 10.3 vlannif接口技术 10.3.1 三层交换机与VLANNIF技术 示例三VLANNIF配置&#xff08;将交换机当成路由器使用&…

鸿蒙仓颉语言【cryptocj 库】RC2、 RC4 、AES对称加密算法

2 提供RC2、 RC4 、AES对称加密算法 前置条件&#xff1a;NA 场景&#xff1a; 支持对称加密算法。 约束&#xff1a;RC2密钥长度一般16字节&#xff0c;加密块长度8字节&#xff1b;AES加密块长度16字节 性能&#xff1a; 支持版本几何性能持平 可靠性&#xff1a; NA …

pytest:4种方法实现 - 重复执行用例 - 展示迭代次数

简介&#xff1a;在软件测试中&#xff0c;我们经常需要重复执行测试用例&#xff0c;以确保代码的稳定性和可靠性。在本文中&#xff0c;我们将介绍四种方法来实现重复执行测试用例&#xff0c;并显示当前迭代次数和剩余执行次数。这些方法将帮助你更好地追踪测试执行过程&…

3.多租户调研1

https://gitee.com/xiaoqiangBUG/hello-ruoyi-cloud.git 1.mybatis plus 的插件 TenantLineInnerInterceptor 是 MyBatis Plus 框架中的一个拦截器&#xff0c;它用于实现多租户系统的数据隔离。在多租户应用中&#xff0c;不同的租户应该只能访问到自己的数据&#xff0c;而…

URL过滤、DNS过滤和内容过滤的总结

目录 URL过滤 URL和URI URL -- 统一资源定位符 URI --- 统一资源的标识符 URL和URI之间的区别 URL过滤的方式 HTTP协议获取URL的方式 HTTP协议做控制管理的流程 HTTPS协议做控制管理的流程 1&#xff0c;配置SSL的解密功能 2&#xff0c;直接针对加密流量进行过滤 例…

javaEE-03-cookie与session

文章目录 Cookie创建Cookie获取Cookie更新CookieCookie 生命控制Cookie 有效路径 Session 会话创建和获取sessionSession 域数据的存取Session 生命周期控制浏览器和 Session 之间关联 Cookie Cookie 是服务器通知客户端保存键值对的一种技术,客户端有了 Cookie 后&#xff0c…

深入解析 GPT-4o mini:强大功能与创新应用

&#x1f4e2;博客主页&#xff1a;https://blog.csdn.net/2301_779549673 &#x1f4e2;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff01; &#x1f4e2;本文由 JohnKi 原创&#xff0c;首发于 CSDN&#x1f649; &#x1f4e2;未来很长&#…

品牌故事线:如何在活动策划中保持品牌信息的连贯性?

在活动运营中保持品牌一致性和传达清晰的品牌信息&#xff0c;是确保活动成功并提升品牌形象的关键。 以下是一些具体的方法和建议。 码字不易&#xff0c;如果回答对你有所帮助&#xff0c;请不吝给一个三连哦&#xff01; 一、明确品牌定位与核心价值 首先&#xff0c;需…

一起刷C语言菜鸟教程100题(27-35)

先说明这个虽然菜鸟教程也有答案&#xff0c;但是这个专栏的博客是自己过手写了一遍&#xff0c;有自己的理解&#xff0c;有些习题自己是变化了一些&#xff0c;更适合练手&#xff0c;也会写的更普遍一些~ 今天我们一起继续刷题&#xff0c;链接放在这里供大家自行使用 C 语…