从位段-对齐-到结构体字节大小(精简版)

位段

	位段:位段是通过结构体来实现的一种以位(bit位)为单位的数据存储结构,它可以把数据以位的形式紧凑的储存,并允许程序员对此结构的位进行操作。位段的成员必须是整型家族的成员(char、int、unsigned int、signed int……)(即基本类型除浮点型)
	基本类型位段:

在这里插入图片描述
指定数据位段大小

struct packed_data{
     unsigned char a : 4;
     unsigned int b : 19;
     unsigned char c : 3;
     unsigned int d : 5;
 };
// 输出为4字节, 4+19+3+5<32(位)

对齐(x86下,GCC)

	各数据类型位段长度不同,编译器为了提高CPU数据访问速度,规定每次访相问字节大小,所设定的规则每次读取相同字节大小数据,对数据复合数据做的处理叫做对齐。
	一般是4个字节(x86下,GCC),高于4字节是4字节的整数倍叫自然对齐,不足4字节的对齐叫做字节对齐,可以根据开发人员需求更改;

	数组 :按照基本数据类型对齐,第一个对齐了后面的自然也就对齐了。
	联合 :按其包含的长度最大的数据类型对齐。
	结构体: 结构体中每个数据类型都要对齐。

	其中结构体对其最麻烦;

结构体的对齐

	先举个例子:
#include <stdio.h>
struct packed_data{
     unsigned char a;  //char位段1字节
     unsigned int b;  //int位段4字节
     unsigned char c;  
     unsigned int d; 
 };
 int main()
 {
 	struct packed_data data;
 	data.a = 6;
 	data.b = 12;
 	data.c = 76;
 	data.d = 45;
 	
 	printf("%lu\n", sizeof(data)); //打印结构体字节大小
	
	return 0;
}
	编译运行得到16字节
	编译器是这样处理的

在这里插入图片描述
第一个数据char,位段1个字节,第二个int数据,位段是4字节,第二个int数据加入char后超出4字节空间大小,不能使数据对齐,直接存放入下一个空间
举几个例子

struct packed_data{
     char a;
     char b;
     char c;  
     int d; 
 };
 //结构体占8字节

struct packed_data{
     int a;
     char b;
     char c;  
     char d; 
 };
 //结构体占8字节

struct packed_data{
     long int a;
     char b;
     short c;  
     char d; 
 };
 //结构体占8字节

修改对齐规则

	1.__attribute__选项
struct stu
{
  char sex;
  int length;
  char name[10];
}__attribute__((aligned (1)));  //两个下划线
	//指定1字节对齐    注意:(不同编译器环境效果不同)
	//我linux GCC 无效果,依然是按4字节对其处理,输出20字节。
	//输出无规律,不太实用

struct stu
{
  char sex;
  int length;
  char name[10];
}__attribute__((aligned (8)));

//输出为24  不太实用 可以看下第二种方法
	2.#pragma pack(n);指定对齐数(单位:位)
  • 使用伪指令#pragma pack (n),C编译器将按照n个字节对齐。
  • 使用伪指令#pragma pack (),取消自定义字节对齐方式。
    其中的n就是对齐系数。VS中默认的值为8;linux中的默认值为4。
#pragma pack(2);    //指定对其字节数为2字节
 struct packed_data{
     unsigned char a;
     unsigned int b;
     char name[10];
     unsigned int d;    
 }data;

data按照指定2字节对齐,输出结果为20;

更深入理解请参考:https://embedx.blog.youkuaiyun.com/article/details/108306754

<think>我们正在讨论Linux嵌入式开发中需要重点掌握的结构体。根据引用内容,嵌入式Linux开发需要熟练掌握系统编程和硬件外设原理。在嵌入式系统中,结构体常用于描述硬件寄存器、设备信息、通信协议等。 需要重点掌握的结构体类型: 1. **硬件寄存器映射结构体**:用于内存映射I/O,直接操作硬件寄存器 - 例如:GPIO控制寄存器组 ```c typedef struct { volatile uint32_t MODER; // 模式寄存器 volatile uint32_t OTYPER; // 输出类型寄存器 volatile uint32_t OSPEEDR; // 输出速度寄存器 volatile uint32_t PUPDR; // 上拉/下拉寄存器 volatile uint32_t IDR; // 输入数据寄存器 volatile uint32_t ODR; // 输出数据寄存器 volatile uint32_t BSRR; // 置位/复位寄存器 volatile uint32_t LCKR; // 配置锁定寄存器 volatile uint32_t AFR[2]; // 复用功能寄存器 } GPIO_TypeDef; ``` 这种结构体需要精确匹配芯片手册的寄存器偏移地址[^3]。 2. **设备信息结构体**:描述外设设备的属性和状态 - 例如:字符设备结构 ```c struct cdev { struct kobject kobj; struct module *owner; const struct file_operations *ops; // 文件操作集 struct list_head list; dev_t dev; // 设备号 unsigned int count; // 设备数量 }; ``` 驱动开发中需自定义包含该结构的设备对象[^1]。 3. **通信协议结构体**:实现网络或总线协议 - 例如:CAN总线帧结构 ```c struct can_frame { canid_t can_id; // 帧ID __u8 can_dlc; // 数据长度 __u8 __pad; // 填充 __u8 __res0; // 保留 __u8 __res1; // 保留 __u8 data[8]; // 数据 } __attribute__((packed)); ``` 使用`__attribute__((packed))`避免对齐问题[^3]。 4. **内核数据结构**:进程、文件描述符等核心对象 - 例如:进程控制块精简版 ```c struct task_struct { volatile long state; // 进程状态 void *stack; // 内核栈 struct mm_struct *mm; // 内存管理 pid_t pid; // 进程ID struct files_struct *files; // 打开文件表 /* ... 数百个成员 ... */ }; ``` 系统编程中通过API间接访问[^1]。 关键技巧: - **位域(bit-field)**:紧凑存储硬件标志位 ```c struct timer_ctrl { unsigned int enable:1; // 使能位 unsigned int mode:2; // 模式选择 unsigned int irq_en:1; // 中断使能 }; ``` - **内存对齐**:使用`__attribute__((aligned(n)))`确保对齐 - **跨端序处理**:通过`htonl()`等函数转换网络字节序 这些结构体是嵌入式Linux开发的核心抽象层,连接硬件操作与系统服务[^1][^3]。 §§相关问题§§ 1. 如何验证寄存器映射结构体的地址偏移是否正确?[^3] 2. Linux字符设备驱动开发中需要实现哪些关键的结构体?[^1] 3. 在嵌入式网络编程中如何处理结构体字节序问题?[^3] </think>在Linux嵌入式开发中,结构体(struct)是实现硬件抽象、驱动开发和系统编程的核心工具。以下是需要重点掌握的结构体类型及其应用场景: ### 一、硬件操作类结构体 1. **GPIO控制结构体** 用于配置引脚模式和状态,属于硬件寄存器直接映射: ```c struct gpio_controller { volatile uint32_t MODER; // 模式寄存器 volatile uint32_t OTYPER; // 输出类型寄存器 volatile uint32_t OSPEEDR; // 输出速度寄存器 volatile uint32_t IDR; // 输入数据寄存器 } __attribute__((packed)); // 禁用内存对齐 ``` 关键点:`volatile`防止编译器优化,`packed`确保与硬件寄存器布局一致[^3]。 2. **外设描述结构体** 统一管理设备资源(如UART/I2C): ```c struct device { char *name; // 设备名 void *reg_base; // 寄存器基地址 int irq_num; // 中断号 struct file_operations *fops; // 文件操作集 }; ``` 驱动开发中通过此结构体注册设备[^1]。 --- ### 二、内核交互类结构体 1. **字符设备结构体** 实现设备文件操作接口: ```c struct file_operations { ssize_t (*read)(struct file *, char __user *, size_t, loff_t *); ssize_t (*write)(struct file *, const char __user *, size_t, loff_t *); long (*ioctl)(struct file *, unsigned int, unsigned long); }; // 实现open/read/write等系统调用 ``` 驱动开发者需实现这些回调函数[^1]。 2. **进程描述符**(精简版) 理解任务调度的基础: ```c struct task_struct { pid_t pid; // 进程ID volatile long state; // 运行状态 struct mm_struct *mm; // 内存管理 struct files_struct *files; // 打开文件表 }; ``` 与进程管理相关的系统调用依赖此结构[^1]。 --- ### 三、通信协议类结构体 1. **Socket地址结构体** 网络编程必备: ```c struct sockaddr_in { sa_family_t sin_family; // 地址族 (AF_INET) in_port_t sin_port; // 端口号 (网络字节) struct in_addr sin_addr; // IP地址 }; // 用于bind()/connect()等网络调用 ``` 2. **CAN总线帧结构体** 工业通信关键结构: ```c struct can_frame { canid_t can_id; // 帧ID __u8 can_dlc; // 数据长度 __u8 data[8]; // 数据载荷 } __attribute__((packed)); // 严格内存对齐 ``` --- ### 关键技巧与注意事项 1. **内存对齐优化** 使用`__attribute__((aligned(4)))`确保32位对齐,避免总线错误: ```c struct sensor_data { uint32_t timestamp __attribute__((aligned(4))); uint16_t value[6]; }; ``` 2. **位域操作** 高效操作寄存器标志位: ```c struct status_reg { unsigned int enable:1; // 1位使能标志 unsigned int mode:2; // 2位模式选择 unsigned int error:4; // 4位错误码 }; ``` 3. **跨端序处理** 网络数据转换宏: ```c uint32_t net_value = htonl(host_value); // 主机序转网络序 ``` > 这些结构体是连接底层硬件与Linux系统的桥梁,掌握其设计原则能显著提升驱动和系统开发效率[^1][^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值