C 如何正确输入双关语?
A.对扩大讨论的后续行动 我试图在C中模拟Z80,其中几个8位寄存器可以组合起来创建16位寄存器 这就是我试图使用的逻辑:C 如何正确输入双关语?,c,emulation,type-punning,z80,C,Emulation,Type Punning,Z80,A.对扩大讨论的后续行动 我试图在C中模拟Z80,其中几个8位寄存器可以组合起来创建16位寄存器 这就是我试图使用的逻辑: struct { uint8_t b; uint8_t c; uint16_t *bc; } regs[1]; ... regs->bc = (uint16_t *)&(regs->b); 为什么这是不正确的,我如何才能正确地做到这一点(如果需要,使用类型双关语) 我需要多次这样做,最好是在同一个结构中。要模拟可以作为两个8位寄
struct {
uint8_t b;
uint8_t c;
uint16_t *bc;
} regs[1];
...
regs->bc = (uint16_t *)&(regs->b);
为什么这是不正确的,我如何才能正确地做到这一点(如果需要,使用类型双关语)
我需要多次这样做,最好是在同一个结构中。要模拟可以作为两个8位寄存器或一个16位寄存器访问的硬件寄存器,可以使用:
union
{
struct { int8_t b, c; };
int16_t bc;
} regs[1];
然后,regs->bc
将是16位寄存器,regs->b
和regs->c
将是8位寄存器
注意:这使用了一个匿名的struct
,这样b
和c
就好像他们是工会的成员一样。如果结构
有一个名称,如下所示:
union
{
struct { int8_t b, c; } s;
int16_t bc;
} regs[1];
volatile reg_t* reg = (volatile reg_t*)0x1234;
然后,在访问b
或c
时,您必须包括它的名称,就像regs->s.b
一样。但是,C有一个特性,允许您使用不带名称的声明来实现此目的
还要注意,这需要一个C编译器。C允许使用联合来重新解释数据。C++有不同的规则。是不正确的,因为<代码> b>代码>是“代码> uut88tt 类型,指针不能使用代码> UTI1616T < /COD>不能用于访问这样的变量。它可能没有正确对齐,并且是一个 但是,自(6.7.2.1/15)起,您可以自由执行
(uint8_t*)和regs
或(struct reg_t*)和regs->b
指向经过适当转换的结构对象的指针指向其初始成员,反之亦然
在执行与硬件相关的编程时,请确保从不使用签名类型。这意味着将
intn\u t
更改为uintn\u t
至于如何正确输入双关语,请使用union:
typedef union
{
struct /* standard C anonymous struct */
{
uint8_t b;
uint8_t c;
};
uint16_t bc;
} reg_t;
然后,您可以将其分配给16位硬件寄存器处的点,如下所示:
union
{
struct { int8_t b, c; } s;
int16_t bc;
} regs[1];
volatile reg_t* reg = (volatile reg_t*)0x1234;
其中0x1234
是硬件寄存器地址
注:此并集取决于端度
b
将在大端系统上访问bc
的MS字节,但在小端系统上访问bc
的LS字节。正确的方法是通过C中的匿名联合,如其他答案所示。但是,当您想要处理字节时,可以在严格的别名规则中使用字符的特殊处理:无论类型如何,使用字符指针访问其表示的字节始终是合法的。这就是共形C
struct {
uint16_t bc;
uint8_t *b;
uint8_t *c;
} regs[1];
regs->b = (uint8_t *) &(regs->bc);
regs->c = regs->b + 1
有趣的是,对于C++编译器来说仍然有效。
< p>在C中键入双关的正确方法(或者几乎做任何事情),就是使用一个被配置成适合于一个预期目的的实现。该标准故意允许用于各种目的的实现以不适合其他目的的方式运行。根据作者的说法,它从来没有打算建议那些行为不受标准约束的程序(但将在其预期的实现上定义)应该被视为“坏的”。如果编译器的作者试图支持其客户的需求,那么无论标准是否要求他们这样做,编译器都会识别简单的类型双关结构,作者轻视客户需求的优化器不应该被信任可靠地处理任何复杂的事情。我想知道这是否会导致问题,因为b
、c
和bc
都有签名。我希望Z80有一个特定的endian,但仿真环境的endian可能不匹配。这也需要OP的代码最终解决。注意:答案中没有详细说明,但当C实现中定义了uint8_t
类型时,它应该是无符号字符
,因为:1/任何类型的大小必须是char
2/字符的大小的倍数,char
的大小至少是8位(5.2.4.2.1整数类型的大小
)3/uint8\u t的大小正好是8位。你的意思是regs->b=(uint8\u t*)®s->bc;
是吗?而且,你总是被允许广泛地转换指针。你提到的特殊规则是关于逐字节迭代任何类型,然后用左值访问它们(6.3.2.3/7).作者肯定会知道,但因为没有人大声说出来:这假设了一个小小的endian体系结构。这是一个非常安全的假设,但不是你可以假设的东西-所以这是符合C的,但从技术上讲,不一定在任何地方都能像预期的那样工作。@FiddlingBits,因为我不知道如何使用union
联合< /Calp> s)。谢谢你的建议。