如何更改变量的类型

时间:2013-12-10 07:13:00

标签: c casting

让我们说你可怕的老板给你以下任务:

编写一个返回long的第二个字节的函数。以下是要求:

  • 使用以下函数签名:uint8_t second_byte(uint32_t l);

想到一点点麻烦的解决方案是:

uint8_t second_byte(uint32_t l)
{
  uint8_t b = (l >> 8) & 0xFF;
  return b;
}

Boss回来了另一项要求

  • 不要使用bit twiddling!

您购买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'类型的变量与同一区域相关联,而不需要指令成本(去/退出)

4 个答案:

答案 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];
}