让我们说你可怕的老板给你以下任务:
编写一个返回long
的第二个字节的函数。以下是要求:
uint8_t second_byte(uint32_t l);
想到一点点麻烦的解决方案是:
uint8_t second_byte(uint32_t l)
{
uint8_t b = (l >> 8) & 0xFF;
return b;
}
Boss回来了另一项要求
您购买Boss Voodoo Doll并继续:
uint8_t second_byte(uint32_t l)
{
uint8_t b = ((uint8_t*) &l)[1];
return b;
}
Boss感到专横,提出另一项要求:
去购买一些炸弹制作手册和遮蔽胶带。经过几次划痕后,你想出了以下方案:
typedef union {
uint8_t u8[4];
uint16_t u16[2];
uint32_t u32;
} long_u;
uint8_t second_byte(long_u l)
{
uint8_t b = long_u.u8[1];
return b;
}
干净漂亮。 你去找你的老板并提出解决方案。
老板提到了第一个要求(功能签名)。你和你的老板争论这个愚蠢的要求,毫无结果:你老板的老板把这个要求放下了。
任何人都可以想到一种“改变”变量类型的方法。像这样的东西(但编译的代码):
uint8_t second_byte(uint32_t l)
{
uint8_t b = ((long_u) l).u8[1];
return b;
}
通过'更改',我的意思是编写代码,说服编译器将T
类型的变量视为类型T'
而不生成额外的指令(如bit twiddling和de - / refrencing)
编译器优化注意:某些编译器(例如GCC)可能会优化上述代码尝试,但不会添加任何指令。有些可能不会(例如IAR)。我正在寻找一种不依赖于编译器优化的C语言结构。
Endianness注意:我不想专注于字节序问题。想象一下uint32_t
是一个4字节的结构
简单地说,我有一个与内存区域相关联的T
类型的变量。我想让另一个T'
类型的变量与同一区域相关联,而不需要指令成本(去/退出)
答案 0 :(得分:1)
而不是“改变”,只需“复制”:
uint8_t second_byte(uint32_t l)
{
uint8_t temp[2] ;
memcpy( temp, &l, sizeof(temp)) ;
return temp[1] ;
}
我可怕的下一步是考虑集会。
答案 1 :(得分:0)
使用union
指针进行类型转换:
uint8_t second_byte(uint32_t l)
{
uint32_t *lp = &l;
uint8_t b = ((long_u*)lp)->u8[1];
return b;
}
这会在我的系统上编译而不会发出警告(同时还有-Wall
选项)
注意:代码的正确性可能取决于机器的字节顺序。
答案 2 :(得分:0)
一个简单的演员阵容对我来说很好用gcc:
uint8_t second_byte(uint32_t l)
{
return ((union { uint8_t u8[4]; uint32_t u32; })l).u8[1];
}
声明一个中间变量可能更具可读性(并避免关于转换的可能的编译器警告):
uint8_t second_byte(uint32_t l)
{
union { uint8_t u8[4]; uint32_t u32; } data;
data.u32 = l;
return data.u8[1];
}
当我使用至少-O1
优化启用的gcc进行编译时,这两个版本的函数会生成相同的机器代码。
实际上,我在您的问题和答案中看到此功能的每个版本的完全相同的机器代码。这包括位移版本。大多数现代编译器非常智能。担心比特错误的优化通常是没有意义的,因为编译器会比你做得更好,而你只是牺牲了可读性来获得完全相同的性能。
答案 3 :(得分:0)
我们可以使用这个
typedef union {
uint8_t u8[4];
uint16_t u16[2];
uint32_t u32;
} long_u;
uint8_t second_byte(uint32_t l)
{
long_u u;
memcpy(&u,&l,sizeof(l));
return u.u8[1];
}