结构体与共用体-------C语言经典题目(3)

结构体

1.如何定义和使用结构体指针?

1.结构体指针的定义

首先需要定义结构体类型,例如表示学生信息的结构体:

struct Student {char name[50];int age;float score;
};

接着,使用struct 关键字和指针符号* 声明结构体指针:

struct Student *pStudent;  // 声明指向Student结构体的指针)

2.结构体指针的初始化

1.可以指向现有结构体变量:
struct Student stu1 = {"Alice", 20, 90.5};
struct Student *pStu = &stu1;  // 指针指向stu1的地址)
2.使用malloc 在堆上分配内存:
struct Student *pStu = (struct Student*)malloc(sizeof(struct Student));
if (pStu != NULL) 
{pStu->age = 22;  // 通过指针初始化成员strcpy(pStu->name, "Bob");
}
// 使用后需手动释放内存
free(pStu);  // 避免内存泄漏)

3.通过指针访问结构体成员

1.箭头运算符->
printf("姓名:%s\n", pStu->name);  // 输出:Bob)
2.解引用与点运算符结合
printf("年龄:%d\n", (*pStu).age);  // 输出:22)

4.结构体指针的应用场景

1.函数参数传递

传递指针可以避免复制大型结构体,提升效率:

void updateScore(struct Student *s, float newScore) 
{s->score = newScore;  // 直接修改原结构体成员)
}
2.动态数据结构

用于构建链表、树等动态结构:

struct Node {int data;struct Node *next;  // 指向下一个节点的指针)
};
3.遍历结构体数组

通过指针操作数组元素:

struct Student stus[3] = {{"Tom", 18, 85.5}, ...};
struct Student *p = stus;
for (int i = 0; i < 3; i++) 
{printf("%s的分数:%.1f\n", p->name, p->score);p++;  // 指针移动到下一个元素)
}

2. 如何将结构体作为函数参数?

主要有三种方式:值传递、指针传递和数组传递。

1.结构体值传递

将结构体变量作为参数直接传递给函数。函数会复制整个结构体的副本。

优点:函数内部对结构体的修改不会影响外部变量。

缺点:如果结构体较大,会比较耗时间。

// 定义结构体
typedef struct {int id;char name[20];
} Student;// 值传递:函数接收结构体副本
void printStudent(Student stu) 
{printf("ID: %d, Name: %s\n", stu.id, stu.name);
}int main() 
{Student tom = {1, "Tom"};printStudent(tom);  // 传递结构体变量return 0;
}

2.结构体指针传递

将结构体变量的地址作为参数传递给函数,函数通过指针操作结构体。

优点:无需复制整个结构体,因此效率高,可在函数中修改原始结构体的值。

缺点:需要使用指针语法->,增加代码复杂度。

typedef struct {int id;char name[20];
} Student;// 指针传递:函数接收结构体指针
void updateStudent(Student *stu, int newId, char *newName) 
{stu->id = newId;        // 通过指针访问成员(-> 操作符)strcpy(stu->name, newName);
}int main() 
{Student tom = {1, "Tom"};updateStudent(&tom, 2, "Jerry");  // 传递结构体地址printf("ID: %d, Name: %s\n", tom.id, tom.name);  // 输出修改后的值return 0;
}

3.结构体数组传递

当函数参数是结构体数组时,可传递数组名。

typedef struct {int id;char name[20];
} Student;// 传递结构体数组(数组名作为指针)
void printStudents(Student students[], int count) 
{for (int i = 0; i < count; i++) {printf("ID: %d, Name: %s\n", students[i].id, students[i].name);}
}int main() 
{Student class[] = {{1, "Alice"},{2, "Bob"}};printStudents(class, 2);  // 传递结构体数组return 0;
}

实际开发中,指针传递时最常用的方式,既能避免复制开销,又能灵活操作结构体内容。

3. 结构体的内存对齐是什么?如何按指定字节对齐?

结构体的内存对齐是指编译器在为结构体分配内存时,按照一定规则(如成员自身大小、指定对齐字数等)来调整成员的存储位置,使得每个成员的地址满足特定对齐要求的过程。

内存对齐的目的是提高内存访问效率。

1.内存对齐的基本规则

1.成员对齐规则

结构体中的每个成员的偏移量(相对于结构体起始地址)必须是该成员类型大小的整数倍。

例如:

struct Example {char a;    // 大小 1 字节,偏移量 0(0 是 1 的倍数)int b;     // 大小 4 字节,偏移量需是 4 的倍数。由于前一个成员占 1 字节,下一个可用偏移量为 1,不是 4 的倍数,因此编译器会在 char 后填充 3 字节,使 int 的偏移量为 4
};          // 结构体总大小为 8 字节(4 + 4,最后一个成员大小的整数倍)
2.结构体整体对齐规则

结构体的总大小必须是其成员中最大对齐数的整数倍。

2.按指定字节对齐的方法

1.使用#pragma pack()---------通用方法
// 设置对齐字节数为 n(n 通常为 1、2、4、8、16 等 2 的幂次)
#pragma pack(n)  struct AlignedStruct {char a;    // 偏移量 0(1 的倍数)double b;  // 若 n=8,double 大小 8 字节,偏移量需是 8 的倍数。char 后需填充 7 字节,使 double 偏移量为 8
};             // 总大小为 16 字节(8 的倍数)#pragma pack() // 恢复默认对齐(或用 #pragma pack(pop))
2.GCC 编译器 __attribute__((aligned(n)))
// 指定结构体按 n 字节对齐(n 需是 2 的幂次)
struct AlignedStruct __attribute__((aligned(8))) {char a;double b;  // 偏移量 8(8 的倍数)
};


共用体

1.共用体和结构体有什么区别?

1.内存分配方式

结构体的每个成员有独立的内存空间,结构体的总大小是所有成员大小之和。

struct Data {int a;     // 占 4 字节char b;    // 占 1 字节(对齐后可能补 3 字节)double c;  // 占 8 字节
};           // 总大小至少为 4 + 4(对齐) + 8 = 16 字节

共用体所有成员共享一块内存空间,同一时刻只能有一个成员有效。共用体的总大小为其最大成员的大小。

union Data {int a;    char b;   double c; 
};           // 总大小为 8 字节(取最大成员 `double` 的大小)

2.内存访问特性

结构体可以同时访问所有成员,每个成员的值独立存储。

struct Data var;
var.a = 10;    // 合法
var.b = 'x';   // 合法
var.c = 3.14;  // 合法

共用体同一时间只能访问最后一次赋值的成员,访问其他成员会导致数据错误(除非成员类型兼容)

union Data var;
var.a = 10;    // 此时内存存储 `int` 类型数据
printf("%d\n", var.a);  // 合法,输出 10
printf("%f\n", var.c);  // 未定义行为(强行将 `int` 当作 `double` 解析)

3.初始化方式

结构体在定义时可以初始化所有成员:

struct Data var = {10, 'x', 3.14};  // 合法

共用体只能初始化第一个成员:

union Data var = {10};  // 合法,初始化 `a`
union Data var = {.c=3.14};  // C99 及以上支持指定成员初始化

4.典型用途

结构体用于存储同时需要存在的多个相关数据,例如学生信息(姓名、年龄、成绩),坐标点(x、y、z)等。

共用体用于节省内存,当数据在不同场景下以不同类型存在时(即互斥使用),例如表示一个变量可能是整数、字符、浮点数等,常见场景包括协议解析、内存共享等。

5.语法关键字

结构体用struct 关键字来定义:

struct 结构体名 { 成员列表; };

共用体用union 关键字来定义:

union 共用体名 { 成员列表; };

2. 共用体在内存中的存储特点是什么?

1.内存共享空间

共用体的所有成员共享同一块内存区域,该区域的起始地址相同。

例如:如果要定义一个包含int 和 char 成员的共用体,这两个成员会从同一地址开始存储。

union Data {int i;char c;
};
union Data d;  // d.i 和 d.c 共享同一块内存

2.内存大小由最大成员决定

共用体的内存占用空间等于其最大成员的大小(需考虑内存对齐)。例如:

union Example {char c;        // 1 字节int i;         // 4 字节(假设 int 为 4 字节)double dbl;    // 8 字节
};  // 共用体大小为 8 字节(由 double 决定)

3.同一时间仅存储一个成员的值

每次只能向共用体的一个成员赋值,后续赋值会覆盖之前成员的数据。例如:

d.i = 100;     // 此时内存中存储 int 类型的 100
d.c = 'A';     // 此时内存中存储 char 类型的 'A'(覆盖之前的 int 数据)

4.内存对齐规则

共用体的内存对齐方式遵循其成员中对齐要求最严格的成员。例如:

若成员包含double ,则共用体的起始地址会按 8 字节对齐。

5.访问成员的类型安全问题

由于共用体成员共享内存,访问时必须明确当前存储的是哪个成员的类型,否则会导致未定义行为,产生段错误。

3. 如何用共用体判断大小端?

利用共用体所有成员共享内存的特性,

1.定义一个包含 int 类型和 char 数组的共用体

2.给 int 成员赋值为1

3.检查char 数组的第一个字节:

        小端模式下最低位字节存放在低地址,所以第一个字节是1

        大端模式下最高位字节存放在低地址,所以第一个字节是0

程序运行后会直接输出当前系统的字节序类型。

union EndianCheck {int num;char bytes[sizeof(int)];
};int main() 
{union EndianCheck ec;ec.num = 1;if (ec.bytes[0] == 1) {printf("小端模式(Little-Endian)\n");} else {printf("大端模式(Big-Endian)\n");}return 0;
}

位域

1.什么是结构体位域?如何定义和使用位域?

结构体位域是一种允许在结构体中直接定义以位为单位的成员的机制,主要用于优化内存,比如处理寄存器配置。

定义:

位域通过在结构体成员声明中使用        类型   成员名  :  位数

struct 结构体名 {类型 成员名1: 位数1;  // 位域成员类型 成员名2: 位数2;// 普通成员(非位域)类型 普通成员名;
};

类型:必须是int 、 unsigned int  、 signed  int  

位数:表示该成员占用的二级制位数,取值为正整数。

例如:定义一个表示颜色分量(RGB)的结构体,每个分量占5位(共15位,用unsigned int 存储)

struct Color {unsigned int red:   5;  // 红色分量,占 5 位(0-31)unsigned int green: 5;  // 绿色分量,占 5 位unsigned int blue:  5;  // 蓝色分量,占 5 位unsigned int alpha: 7;  // 透明度,占 7 位(0-127),剩余 1 位未使用
};

使用:

1.声明变量并赋值:

struct Color c;// 像普通结构体成员一样赋值
c.red = 20;   // 20 是合法值(20 < 32,未超过 5 位范围)
c.green = 31; // 最大值 31(2^5 - 1)
c.blue = 0;
c.alpha = 127; // 最大值 127(2^7 - 1)

2.访问位域成员:

通过结构体变量名 + 成员运算符 .  直接访问:

printf("Red: %u\n", c.red);   // 输出:20
printf("Alpha: %u\n", c.alpha); // 输出:127

2. 位域的应用场景有哪些?

位域是一种允许在结构体中定义以位为单位的字段的特性,其核心优势是节省内存空间并直接操作二进制位。

1.硬件寄存器映射

嵌入式设备的寄存器通常由多个独立位段组成,通过位域可直接读写寄存器的特定位段,无需手动进行位运算(如& 、 |):

// 寄存器地址:0x40000000
// 位31-24:保留  
// 位23-16:时钟频率选择(8位)  
// 位15-8:使能标志(1位有效,其余保留)  
// 位7-0:数据长度(8位)  
struct peripheral_reg {unsigned int reserved1:8;   // 位31-24(保留)unsigned int clk_freq:8;    // 位23-16(时钟频率)unsigned int enable:1;      // 位15(使能标志,其余7位保留)unsigned int data_len:8;    // 位7-0(数据长度)
};
volatile struct peripheral_reg *reg = (volatile struct peripheral_reg*)0x40000000;// 使用位域操作:
reg->enable = 1;        // 使能设备
reg->clk_freq = 0b1010; // 设置时钟频率

不过,如果需要频繁的对字段进行位运算(异或、移位),位域不如直接操作整数来得高效。

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

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

相关文章

未来教育风向标 | 教育学顶流985高校,华东师范大学《AIGC技术赋能教育数字化转型的机遇与挑战》,13所大学deepseek

今天大师兄给大家推荐的是华东师范大学祝智庭教授的《AIGC技术赋能教育数字化转型的机遇与挑战》。华东师范大学是一所985学校&#xff0c;在最新的国家学科测评中&#xff0c;软件工程为A级&#xff0c;教育学为A级。 可以说在AI和教育的结合上是国内top级别的存在。 此讲义探…

Java常用正则表达式及使用方法

在 Java 中&#xff0c;Pattern 和 Matcher 类是 java.util.regex 包的核心&#xff0c;用于处理正则表达式。 Pattern 类 Pattern 类表示编译后的正则表达式&#xff0c;它提供了一种将正则表达式字符串编译成可执行对象的方式&#xff0c;以便后续用于匹配操作。 常用方法…

车载软件架构 --- 驾驶员不感知的控制器软件运行

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 周末洗了一个澡,换了一身衣服,出了门却不知道去哪儿,不知道去找谁,漫无目的走着,大概这就是成年人最深的孤独吧! 旧人不知我近况,新人不知我过…

深度学习3.5 图像分类数据集

%matplotlib inline import torch import torchvision from torch.utils import data from torchvision import transforms from d2l import torch as d2l代码执行流程图 #mermaid-svg-WWhBmQvijswiICpI {font-family:"trebuchet ms",verdana,arial,sans-serif;font-…

Kotlin集合全解析:List和Map高频操作手册

Kotlin 中 Map 和 List 常用功能总结 List 常用功能 创建 List val immutableList listOf(1, 2, 3) // 不可变列表 val mutableList mutableListOf("a", "b", "c") // 可变列表 val emptyList emptyList<String>() // 空列表基本…

Yocto项目实战教程-第7章定制镜像菜谱与内核菜谱-7.2小节-定制应用程序

&#x1f50d; B站相应的视频教程&#xff1a; &#x1f4cc; Yocto项目实战教程-第7章-定制镜像菜谱与内核菜谱 记得三连&#xff0c;标为原始粉丝,感谢大神支持。 在嵌入式Linux系统开发中&#xff0c;定制专属应用程序往往是最贴近产品交付的那一环。而Yocto项目&#xff0c…

【图像轮廓特征查找】图像处理(OpenCV) -part8

17 图像轮廓特征查找 图像轮廓特征查找其实就是他的外接轮廓。 应用&#xff1a; 图像分割 形状分析 物体检测与识别 根据轮廓点进行&#xff0c;所以要先找到轮廓。 先灰度化、二值化。目标物体白色&#xff0c;非目标物体黑色&#xff0c;选择合适的儿值化方式。 有了轮…

C# 的 字符串插值($) 和 逐字字符串(@) 功能

这段代码使用了 C# 的 字符串插值&#xff08;$&#xff09; 和 逐字字符串&#xff08;&#xff09; 功能&#xff0c;并在 SQL 语句中动态拼接变量。下面详细解释它们的用法&#xff1a; 1. $&#xff08;字符串插值&#xff09; $ 是 C# 的 字符串插值 符号&#xff0c;允许…

mockMvc构建web单元测试学习笔记

web应用本来需要依靠tomcat这个环境运行 现在用mockMvc是为了模拟这个web环境&#xff0c;简化测试 什么是mock(模拟) 模拟对象---mock object是以可控方式模拟真实对象行为的假对象&#xff0c;通过模拟输入数据&#xff0c;验证程序达到预期结果 为什么使用mock对象 因为…

6.7.图的深度优先遍历(英文缩写DFS)

树是特殊的图&#xff0c;没有回路的图就是树 BFS与DFS的区别在于&#xff0c;BFS使用了队列&#xff0c;DFS使用了栈 一.深度优先遍历&#xff1a; 1.树的深度优先遍历&#xff1a; 树的深度优先遍历分为先根遍历和后根遍历。 以树的先根遍历为例&#xff1a; 上述图片里…

VOS3000内存满了怎么删除,录音格式如何转换呢

一、清理VOS3000内存&#xff08;删除旧录音文件&#xff09; 定位录音存储目录 通常录音文件存储在以下路径&#xff08;以实际配置为准&#xff09;&#xff1a; bash 复制 下载 /usr/local/vos/record # 默认录音目录 /var/log/vos/logs # 系统日志目录&#xff08;…

【图问答】DeepSeek-VL 论文阅读笔记

《DeepSeek-VL: Towards Real-World Vision-Language Understanding》 1. 摘要/引言 基于图片问答&#xff08;Visual Question Answering&#xff0c;VQA&#xff09;的任务 2. 模型结构 和 三段式训练 1&#xff09;使用 SigLIP 和 SAM 作为混合的vision encoder&#xf…

MATLAB - 模型预测控制(MPC)使用 ADMM 求解器四分之一汽车悬架悬挂系统动力学控制

系列文章目录 目录 系列文章目录 前言 一、四分车悬架模型 二、道路干扰剖面 三、设计模型预测控制器 四、设置优化求解器 五、辅助函数 前言 本例展示了如何为四分之一汽车悬架系统设计模型预测控制器 (MPC)&#xff0c;采用乘法交替方向法 (ADMM) 求解器来控制主动悬架…

基于多模态融合算法的航空武器毁伤评估技术方案

基于多模态融合算法的航空武器毁伤评估技术方案 1. 引言 航空武器毁伤评估(Damage Assessment, DA)是现代战争中的关键环节,直接影响后续作战决策。传统的人工评估方式效率低、主观性强,且在高强度战场环境下难以实时完成。因此,本研究提出一种基于多模态融合算法的自动…

LeetCode算法题(Go语言实现)_49

题目 给定整数数组 nums 和整数 k&#xff0c;请返回数组中第 k 个最大的元素。 请注意&#xff0c;你需要找的是数组排序后的第 k 个最大的元素&#xff0c;而不是第 k 个不同的元素。 你必须设计并实现时间复杂度为 O(n) 的算法解决此问题。 一、代码实现&#xff08;快速选择…

【HCIA】简易的两个VLAN分别使用DHCP分配IP

前言 之前我们通过 静态ip地址实现了Vlan间通信 &#xff0c;现在我们添加一个常用的DHCP功能。 文章目录 前言1. 配置交换机2. 接口模式3. 全局模式后记修改记录 1. 配置交换机 首先&#xff0c;使用DHCP&#xff0c;需要先启动DHCP服务&#xff1a; [Huawei]dhcp enable I…

【技术派后端篇】技术派通用敏感词替换:原理、实现与应用

在当今互联网环境下&#xff0c;数据脱敏对于国内的互联网企业而言已经成为一项标配。这不仅是为了满足合规性要求&#xff0c;更是保障用户信息安全和企业声誉的重要举措。本文将深入探讨技术派中实现数据脱敏的关键技术——通用敏感词替换&#xff0c;从算法原理到具体实现&a…

Android RK356X TVSettings USB调试开关

Android RK356X TVSettings USB调试开关 平台概述操作-打开USB调试实现源码补充说明 平台 RK3568 Android 11 概述 RK3568 是瑞芯微&#xff08;Rockchip&#xff09;推出的一款高性能处理器&#xff0c;支持 USB OTG&#xff08;On-The-Go&#xff09;和 USB Host 功能。US…

Microsoft Edge for linux debian

下载地址 https://www.microsoft.com/en-us/edge/download?formMA13FJ 安装 # 下载安装包 wget https://packages.microsoft.com/repos/edge/pool/main/m/microsoft-edge-stable/microsoft-edge-stable_135.0.3179.85-1_amd64.deb?brandM102 # 安装 sudo dpkg -i microsoft…

typedef MVS_API CLISTDEF0IDX(ViewScore, IIndex) ViewScoreArr;

查找 MVS_API 定义 我们没有在 List.h 文件中找到 MVS_API 的定义。MVS_API 很可能在 MVS 库的其他地方定义。一般来说&#xff0c;MVS_API 是控制 OpenMVS 库导入导出的宏&#xff0c;通常会出现在 MVS 的头文件中。为了回答这个问题&#xff0c;我可以提供 MVS 代码中常见的…