我正在修补我从字节交换例程库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)| \
到目前为止,我认为这是我所理解的:
unsigned int
,但不会更改存储数据的按位结构。我不能完全确定的是,在某些情况下,为什么编译器会根据我执行类型转换的位置,或者不按预期执行转换。
如何使用上述#define
语句(或类似语句)对类型float
变量执行字节交换?
答案 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