也谈C/C++中的位域

C/C++中的位域在百度百科里面介绍了一些基础知识,但遗憾的是没有和内存示意相结合,初学者容易理解偏差。
以下内容是假设读者已初步理解掌握了百度百科“位域”的基础上,再加以补充说明:《Soloe@csdn,转载请说明》
1、位域中的变量字段,最小占用是其变量说明的类型在“正常占用”下的大小。
什么意思呢,看下面这个例子:

struct A
{
    int a:5;
    int b:3;
};

这里a和b总共占用了8bit,那么这个结构体的大小是否为1字节?
答案是no,笔者电脑上 sizeof(A) 结果为4,这是因为在x86下int占4个字节。

2、继续前面的案例,a和b在这四个字节中处于哪个位置?
高位 低位

高位低位
00000100000000110000000100000000
bbbaaaaaa

答案如上图所示,a占了最低位的5个bit,b占了a左边的3个比特

3、从结构体位域转换成正常变量,需要注意哪些事项:
看下面的例子:

#include <iostream>
#include <memory.h>
using namespace std;
struct A
{
    int a:5;
    int b:3;
};
int main(void)
{
    char str[100] = "0134324324afsadfsdlfjlsdjfl";
    struct A d;
    memcpy(&d, str, sizeof(A));
    cout << d.a << endl;
    cout << d.b << endl;
    return 0;
}

输出是多少?
答案是:

-16
1

原因是int a和int b是有符号数,要注意符号位的扩展。

高位低位
00000100000000110000000100000000
‘4’‘3’‘2’‘1’

其中d.a和d.b占用d低位一个字节(00110000),d.a : 10000, d.b : 001
d.a内存中二进制表示为10000,由于d.a为有符号的整型变量,输出时要对符号位进行扩展,所以结果为-16(二进制为11111111111111111111111111110000)

d.b内存中二进制表示为001,由于d.b为有符号的整型变量,输出时要对符号位进行扩展,所以结果为1(二进制为00000000000000000000000000000001)

《Soloe@csdn,转载请说明》
最后给出一个面试的案例:

struct mybitfields
{
    unsigned short a : 4;
    unsigned short b : 5;
    unsigned short c : 7;
} test
void main(void)
{
    int i;
    test.a=2;
    test.b=3;
    test.c=0;
    i=*((short *)&test);
    printf("%d\n",i);
}

聪明的你,能直接在评论中给出答案吗?

<think> 我们有一个结构体定义,其中包含多个成员。首先,我们解释一下这个结构体的各个部分。 1. **typedef 的作用**: - `typedef` 用于为数据类型定义一个新的名字(别名)。在这个例子中,它为 `struct _AC_TIMING_EXTERNAL_T` 定义了一个别名 `AC_TIMING_EXTERNAL_T`。这样,以后就可以直接使用 `AC_TIMING_EXTERNAL_T` 来声明该结构体类型的变量,而不需要写 `struct` 关键字。 2. **结构体名称**: - `struct _AC_TIMING_EXTERNAL_T` 是结构体的标签(tag)。通常,在定义结构体时,我们会给结构体一个标签,以便在后续代码中引用。 3. **成员变量**: - 结构体内部有多个成员,每个成员都使用了(bit-field)来指定所占用的数。 - 的语法是 `类型 成员名 : 数;`。 - 在这个结构体中,所有成员都是 `U32` 类型(根据引用,`U32` 是 `uint32_t` 的别名,即无符号32整数),但每个成员只使用其中的若干。 4. **的含义**: - 允许我们将一个整型变量(这里是32的无符号整数)分成多个不同数的部分,每个部分存储一个值。 - 例如:`U32 AC_TIME_EMI_FREQUENCY : 16;` 表示这个成员占用16(2字节)。 - 这样,多个成员可以紧凑地存储在一个或多个整型变量中,节省内存空间。 5. **结构体成员列表**: - `AC_TIME_EMI_FREQUENCY`:16 - `AC_TIME_EMI_TRAS`:8 - `AC_TIME_EMI_TRPAB`:8 - `AC_TIME_EMI_TRC`:8 - `AC_TIME_EMI_TRFC`:8 - `AC_TIME_EMI_TRFCPB`:8 - `AC_TIME_EMI_TXP`:8 - `AC_TIME_EMI_TRTP`:8 - `AC_TIME_EMI_TRCD`:8 - `AC_TIME_EMI_TWR`:8 6. **总数计算**: - 16 + 8*9 = 16+72 = 88。 - 因为每个成员都是基于32的整型,但这里多个成员连续定义,编译器会将它们打包在连续的中。由于总数88超过了32,所以编译器会分配多个32单元。具体分配方式由编译器决定,但通常会连续存放,直到当前分配单元(32)不够时再分配下一个32。 7. **内存布局**: - 由于每个成员都是U32类型,但实际只使用指定数,因此编译器会将这些成员打包在一个或多个连续的32字中。 - 在这个例子中,第一个成员占16,后面9个成员各占8(共72),所以总共88。88需要3个32字(因为32*3=96>=88)。 - 具体布局由编译器根据对齐规则决定,但通常会尽量紧凑排列。 8. **为什么使用**: - 这种结构通常用于硬件寄存器映射,其中每个寄存器中的代表不同的控制或参数值。通过,可以方便地访问寄存器中的特定段。 - 例如,在嵌入式系统中,配置外部存储器(如SDRAM)的时序参数时,每个时序参数可能占用寄存器的几个,通过这样的结构体可以直接给各个字段赋值,然后一次性写入寄存器。 9. **注意事项**: - 的具体内存布局(如在内存中的排列顺序,是从高到低还是低到高)取决于编译器和硬件平台(大小端)。 - 不同编译器对的处理可能有差异,特别是在跨平台时需要注意。 10. **结构体大小**: - 由于对齐要求,结构体的大小可能不是简单的数相加。编译器可能会在成员之间或结构体末尾添加填充。但通常尽可能紧凑,所以这里可能分配3个32整型(12字节),因为88需要3个32字来存放(96),剩余8可能是填充。 ### 完整解释代码 ```c // 定义了一个结构体类型,别名为AC_TIMING_EXTERNAL_T typedef struct _AC_TIMING_EXTERNAL_T { U32 AC_TIME_EMI_FREQUENCY : 16; // 16,用于设置EMI频率 U32 AC_TIME_EMI_TRAS : 8; // 8,用于设置tRAS时序参数 U32 AC_TIME_EMI_TRPAB : 8; // 8,用于设置tRPAB时序参数 U32 AC_TIME_EMI_TRC : 8; // 8,用于设置tRC时序参数 U32 AC_TIME_EMI_TRFC : 8; // 8,用于设置tRFC时序参数 U32 AC_TIME_EMI_TRFCPB : 8; // 8,用于设置tRFCPB时序参数 U32 AC_TIME_EMI_TXP : 8; // 8,用于设置tXP时序参数 U32 AC_TIME_EMI_TRTP : 8; // 8,用于设置tRTP时序参数 U32 AC_TIME_EMI_TRCD : 8; // 8,用于设置tRCD时序参数 U32 AC_TIME_EMI_TWR : 8; // 8,用于设置tWR时序参数 } AC_TIMING_EXTERNAL_T; ``` ### 典型应用场景 这种结构体常用于配置外部存储器的时序参数。例如,在嵌入式系统中初始化SDRAM控制器时,需要设置一系列时序参数,这些参数被组织在硬件寄存器中,每个参数占据寄存器的特定。通过定义这样的结构体,可以方便地设置参数并一次性写入寄存器。 ### 示例代码 ```c AC_TIMING_EXTERNAL_T timingConfig; // 设置各个时序参数 timingConfig.AC_TIME_EMI_FREQUENCY = 0x1234; // 设置频率参数 timingConfig.AC_TIME_EMI_TRAS = 0x12; // ... 设置其他参数 // 假设将配置写入硬件寄存器地址0x10000000 volatile AC_TIMING_EXTERNAL_T *reg = (volatile AC_TIMING_EXTERNAL_T*)0x10000000; *reg = timingConfig; ``` ### 相关问题 1. 在结构体中,如果总数超过32,编译器会如何处理? 2. 使用时,如何确保不同编译器之间的兼容性? 3. 在嵌入式系统中,为什么常用来访问硬件寄存器? </think> ### C语言结构体定义解析 #### 1. 基本结构分析 ```c typedef struct _AC_TIMING_EXTERNAL_T { U32 AC_TIME_EMI_FREQUENCY : 16; // 16 U32 AC_TIME_EMI_TRAS : 8; // 8 U32 AC_TIME_EMI_TRPAB : 8; // 8 U32 AC_TIME_EMI_TRC : 8; // 8 U32 AC_TIME_EMI_TRFC : 8; // 8 U32 AC_TIME_EMI_TRFCPB : 8; // 8 U32 AC_TIME_EMI_TXP : 8; // 8 U32 AC_TIME_EMI_TRTP : 8; // 8 U32 AC_TIME_EMI_TRCD : 8; // 8 U32 AC_TIME_EMI_TWR : 8; // 8 } AC_TIMING_EXTERNAL_T; ``` 1. **类型定义机制** - `typedef` 创建了类型别名 `AC_TIMING_EXTERNAL_T` - 可直接声明变量:`AC_TIMING_EXTERNAL_T config;` - 原始结构体标签为 `_AC_TIMING_EXTERNAL_T`(C语言传统写法) 2. **技术应用** - `: N` 语法表示(bit-field) - 所有成员使用 `U32` 类型(`uint32_t` 的别名,32无符号整型)[^1][^2] - 总空间占用:$16 + (9 \times 8) = 88\text{} = 11\text{字节}$ - 实际内存分配:编译器按 $32\text{}(4\text{字节})$ 对齐,最终占 $12\text{字节}$ #### 2. 成员功能解析 | 成员名称 | 数 | 功能描述 | |---------|------|---------| | `AC_TIME_EMI_FREQUENCY` | 16 | EMI时钟频率(单:$f = \frac{\text{值}}{10}\text{MHz}$) | | `AC_TIME_EMI_TRAS` | 8 | 行激活时间($t_{RAS} = \text{值} \times t_{CK}$) | | `AC_TIME_EMI_TRPAB` | 8 | 行预充电时间($t_{RP}$) | | `AC_TIME_EMI_TRC` | 8 | 行周期时间($t_{RC} = t_{RAS} + t_{RP}$) | | `AC_TIME_EMI_TRFC` | 8 | 自动刷新周期($t_{RFC}$) | | `AC_TIME_EMI_TRFCPB` | 8 | 每bank刷新时间($t_{RFCPB}$) | | `AC_TIME_EMI_TXP` | 8 | 写入后预充电延迟($t_{XP}$) | | `AC_TIME_EMI_TRTP` | 8 | 读取后预充电时间($t_{RTP}$) | | `AC_TIME_EMI_TRCD` | 8 | 行到列延迟($t_{RCD}$) | | `AC_TIME_EMI_TWR` | 8 | 写入恢复时间($t_{WR}$) | #### 3. 内存布局示例 ```plaintext 地址偏移 | 分布 (32单元) ----------|---------------------------------------- 0x00 | [31:16] - 保留 [15:0] - FREQUENCY 0x04 | [31:24] - TWR [23:16] - TRCD | [15:8] - TRTP [7:0] - TXP 0x08 | [31:24] - TRFCPB [23:16] - TRFC | [15:8] - TRC [7:0] - TRPAB | (注:TRAS于第一单元的[23:16]) ``` #### 4. 设计原理说明 1. **硬件寄存器映射** 该结构体直接对应DRAM控制器寄存器: ```c // 寄存器地址映射 #define DRAM_TIMING_REG ((volatile AC_TIMING_EXTERNAL_T*)0x4000F000) // 配置示例 DRAM_TIMING_REG->AC_TIME_EMI_FREQUENCY = 800; // 80MHz DRAM_TIMING_REG->AC_TIME_EMI_TRAS = 20; // tRAS=20周期 ``` 2. **时序参数关系** DRAM关键时序约束关系: $$ \begin{cases} t_{RC} \geq t_{RAS} + t_{RP} \\ t_{WR} \geq t_{XP} + t_{RTP} \\ t_{RFC} > t_{RC} \times 8 \end{cases} $$ 3. **嵌入式应用优势** - **空间效率**:相比独立变量($10 \times 32\text{} = 40\text{字节}$),节省 $73\%$ 空间 - **原子操作**:单个32寄存器写入保证时序配置的原子性 - **硬件兼容**:布局匹配硬件寄存器设计[^3] #### 5. 典型配置流程 ```c void configure_dram_timing(DRAM_TYPE type) { AC_TIMING_EXTERNAL_T cfg = {0}; switch(type) { case DDR3_1600: cfg.AC_TIME_EMI_FREQUENCY = 1600; // 160MHz cfg.AC_TIME_EMI_TRAS = 24; // tRAS=15ns (160MHz周期6.25ns) cfg.AC_TIME_EMI_TRCD = 8; // tRCD=5ns // ... 设置其他参数 break; case LPDDR4_3200: // 不同存储器的时序配置 break; } // 写入硬件寄存器 memcpy((void*)DRAM_TIMING_REG, &cfg, sizeof(cfg)); dram_controller_init(); } ``` ### 相关问题 1. 在DRAM初始化流程中,为什么需要配置$t_{RAS}$和$t_{RCD}$等参数? 2. 结构体在不同字节序(大端/小端)系统中如何保证兼容性? 3. 如何计算DRAM的$t_{RFC}$参数与刷新率的关系? [^1]: U32类型定义参考 [^2]: 技术应用示例 [^3]: STM32硬件寄存器设计模式
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值