如何使用#define语句在C中的类型`float`变量上执行字节交换?

时间:2014-10-27 21:09:07

标签: c bit-manipulation c-preprocessor

我正在修补我从字节交换例程库1获得的一段代码,并学习按位操作,#define的使用,指针和类型转换:

#define __bswap_constant_32(x) \
  ((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >>  8) |               \
  (((x) & 0x0000ff00) <<  8) | (((x) & 0x000000ff) << 24))

使用以下函数中的代码段按预期方式工作:

void swapbytes32(void *pt)
{
    *((unsigned int *)pt) = (((  *((unsigned int *)pt) & 0xff000000) >> 24) | ((  *((unsigned int *)pt) & 0x00ff0000) >>  8) |   \
     ((  *((unsigned int *)pt) & 0x0000ff00) <<  8) | ((  *((unsigned int *)pt) & 0x000000ff) << 24));
}

例如

之类的电话
float x = 1.0;
swapbytes32((void*)(&x)); 

给出x = 0x0 0 80 3f 并交换x = 0x3f 80 0 0

但请注意所有尴尬的类型转换。正确使用类型铸造让我感到困惑。在上面的函数中,我用它来抑制错误,例如:

  

在函数'swapbytes32'中:hex_testi_3.c:200:31:警告:   解除引用'void *'指针[默认启用] *((unsigned int   *)pt)=(((* pt)&amp; 0xff000000)&gt;&gt; 24)| ((* pt)&amp; 0x00ff0000)&gt;&gt; 8)| \

到目前为止,我认为这是我所理解的:

  • 无法取消引用void指针,因为您不知道隐含类型的字节长度或有关如何在操作中使用数据的信息。
  • 上面的类型转换告诉编译器将地址中的数据类型视为unsigned int,但不会更改存储数据的按位结构。

我不能完全确定的是,在某些情况下,为什么编译器会根据我执行类型转换的位置,或者不按预期执行转换。

如何使用上述#define语句(或类似语句)对类型float变量执行字节交换?

4 个答案:

答案 0 :(得分:3)

在float和它的位表示之间转换为int的简单方法是使用union而不是转换指针。如果可以保证int的大小与float的大小相同,则应该保持另一个的整个位模式,而不进行任何中间类型转换。在stdint.h之间,以及float的IEEE754单一标准化(几乎无处不在,但您可以使用#ifdef __STDC_IEC_559__进行测试),这在大多数系统上都应该是安全的。

您可以编写一个简单的包装器宏,在将float传递给上面给出的交换实现之前将其转换为int:

#define BSWAP_FLOAT(F) \
  __bswap_constant_32((union { uint32_t i; float f; }){ .f = (F) }.i)

这会在就地创建匿名联合,使用浮点值初始化其f字段,并通过i字段(然后传递给交换宏)读回其位模式。

要将宏返回的返回值转换为另一个浮点数,请将其包装在另一个相反方式的union文字表达式中,使用int初始化并提取浮点数(显示为第二个包装器)为简单起见:

#define BSWAP_FLOAT2(F) \
  ((union { uint32_t i; float f; }){ .i = BSWAP_FLOAT(F) }.f)

就地联合文字对于这类事情非常有用。

看到它的实际效果:

#include <stdio.h>
#include <stdint.h>

#define __bswap_constant_32(x) \
  ((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >>  8) |               \
  (((x) & 0x0000ff00) <<  8) | (((x) & 0x000000ff) << 24))

#define BSWAP_FLOAT(F) \
  __bswap_constant_32((union { uint32_t i; float f; }){ .f = (F) }.i)

#define FLOAT_BITS(F) ((union { uint32_t i; float f; }){ .f = (F) }.i)

int main(void) {
    float f = 4.0f;
    printf("%x %x\n", FLOAT_BITS(f), BSWAP_FLOAT(f));
}

答案 1 :(得分:2)

此代码调用未定义的行为,因为通过不兼容的类型(float与{{1}访问对象,严重违反了严格的别名规则 })。您可以利用此规则的例外情况:允许使用指向字符类型的指针检查对象的表示。

另外,你真的不需要每次拼出演员阵容;为什么不只是声明指针一次?像这样:

unsigned int

用法:

void swap_byte_order(void *ptr, size_t n)
{
    unsigned char *p = ptr, *q = p + n - 1;
    for (; p < q; p++, q--) {
        unsigned char tmp = *p;
        *p = *q;
        *q = tmp;
    }
}

更清洁,严格遵守,整体更好。

答案 2 :(得分:1)

当我起草这个问题时,我意识到我比我意识到的更接近答案。这是我到目前为止所得到的:

尝试在define语句中使用指针。

例如,如果我定义以下

#define bswap_constant_32_(px) \
                    *(px)=  ( ((  *(px) & 0xff000000) >> 24) \
                            | ((  *(px) & 0x00ff0000) >>  8) \
                            | ((  *(px) & 0x0000ff00) <<  8) \
                            | ((  *(px) & 0x000000ff) << 24));

如果

unsigned int n = 123456;

以下任一作品:

n = bswap_constant_32(&n);
bswap_constant_32(&n);

// n before: 0x40       e2        1        0
//    after: 0x 0        1       e2       40

因为数据类型(以及位结构)匹配。

然而,这有效:

 float x = 1.0;
 bswap_constant_32((unsigned int*)(&x));

但这并不是:

 x=bswap_constant_32((unsigned int*)(&x));

结果:0x0 3f 0 47

编译器似乎执行数据类型转换并在将数据传递给x之前重新排列基础位。

答案 3 :(得分:1)

你是非常正确的,你是亲密的,指针提供了答案。您可以使用简单的强制转换操作特定地址的float值,并将结果分配给unsigned integer。您可以完全删除下面的swapped,并使用printf (" __bswap_constant_32 : %02x\n\n", __bswap_constant_32(*ai));生成相同的输出。看看:

#include <stdio.h>

#define __bswap_constant_32(x) \
((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >>  8) |               \
(((x) & 0x0000ff00) <<  8) | (((x) & 0x000000ff) << 24))

int main (void) {

    float f = 123.45;
    unsigned int *ai = (unsigned int *)&f;
    unsigned int swapped;

    printf ("\nSwapping bytes of float: '%.2f'\n\n", f);
    printf (" %.2f as unsigned int: %02x\n\n", f, *ai);

    swapped = __bswap_constant_32(*ai);

    printf ("  __bswap_constant_32  : %02x\n\n", swapped);

    return 0;
}

<强>输出:

$ ./bin/bswc

Swapping bytes of float: '123.45'

 123.45 as unsigned int: 42f6e666

  __bswap_constant_32  : 66e6f642