【问题标题】:Reverse bytes for 64-bit value64 位值的反转字节
【发布时间】:2014-02-02 06:13:24
【问题描述】:

我正在尝试反转 64 位地址指针的字节以进行分配并使用以下代码:

char swapPtr(char x){
x = (x & 0x00000000FFFFFFFF) << 32 | (x & 0xFFFFFFFF00000000) >> 32;
x = (x & 0x0000FFFF0000FFFF) << 16 | (x & 0xFFFF0000FFFF0000) >> 16;
x = (x & 0x00FF00FF00FF00FF) << 8  | (x & 0xFF00FF00FF00FF00) >> 8;
return x;
}

但是,它只是把一切都搞砸了。但是,类似的功能对于 64 位长度非常有效。指针有什么不同需要做的吗?

我进行函数调用的方式会不会是个问题?

对于指针:

*(char*)loc = swapPtr(*(char*)loc);

很久了:

*loc = swapLong(*loc);

【问题讨论】:

  • char 不是正确的数据类型。它(通常)是一个 8 位字节。如果您使用的是 64 位字符的平台,请提及。
  • 是的,它在 x86_64 架构上
  • 那么一个 char 是 8 位。它不可能保存 64 位的值。

标签: c byte reverse swap endianness


【解决方案1】:

您不能将char x 用作指针!!!! char 只有一个字节长。

你至少需要

unsigned long int swapPtr(unsigned long int x) {

或者更好,使用指针的类型

void* swapPtr(void* x) {

当您开始移位指针时,您的编译器很可能会抱怨;在这种情况下,最好将参数显式转换为无符号 64 位整数:

#include <stdint.h>
uint64_t x;

还要注意,你必须用变量的地址来调用,所以你用

result = swapLong(&loc);

不是*loc(它查看loc 指向的位置- 值,而不是地址)。

完整的程序:

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

uint64_t swapLong(void *X) {
  uint64_t x = (uint64_t) X;
x = (x & 0x00000000FFFFFFFF) << 32 | (x & 0xFFFFFFFF00000000) >> 32;
x = (x & 0x0000FFFF0000FFFF) << 16 | (x & 0xFFFF0000FFFF0000) >> 16;
x = (x & 0x00FF00FF00FF00FF) << 8  | (x & 0xFF00FF00FF00FF00) >> 8;
return x;
}

int main(void) {
  char a;
  printf("the address of a is 0x%016llx\n", (uint64_t)(&a));
  printf("swapping all the bytes gives 0x%016llx\n",(uint64_t)swapLong(&a));
}

输出:

the address of a is 0x00007fff6b133b1b
swapping all the bytes gives 0x1b3b136bff7f0000

EDIT你可以使用类似的东西

#include <inttypes.h>

printf("the address of a is 0x%016" PRIx64 "\n", (uint64_t)(&a));

PRIx64 扩展为“您需要以十六进制打印 64 位数字的格式字符串”。比上面的要干净一点。

【讨论】:

  • 只是不要为这样的任务使用固定宽度的类型。适当的类型是uintptr_t。另外你可以“#if UINTPRT_SIZE &gt; UINT32_SIZE”假设宽度为64位的部分。
  • @jensgustedt 感谢您的澄清。我没有意识到有一个uintptr_t - 应该考虑看看。每天学习一些东西!
  • 次要:考虑printf("0x016" PRIx64, (uint64_t)(&amp;a))printf("0x%016llx\n", (unsigned long long)(&amp;a)) 而不是发布的格式和整数匹配?
  • @chux - 那确实会更好。有几种方法可以改善这种情况......当我靠近电脑(而不是手机)时可能会这样做
【解决方案2】:

这是将 64 位值从 LE 转换为 BE 或反之亦然的替代方法。

您基本上可以将此方法应用于任何类型,只需定义var_type

typedef long long var_type;

按指针反转:

void swapPtr(var_type* x)
{
    char* px = (char*)x;
    for (int i=0; i<sizeof(var_type)/2; i++)
    {
        char temp = px[i];
        px[i] = px[sizeof(var_type)-1-i];
        px[sizeof(var_type)-1-i] = temp;
    }
}

按值反转:

var_type swapVal(var_type x)
{
    var_type y;
    char* px = (char*)&x;
    char* py = (char*)&y;
    for (int i=0; i<sizeof(var_type); i++)
        py[i] = px[sizeof(var_type)-1-i];
    return y;
}

【讨论】:

  • +1 获得不错的通用解决方案。 (如果想确保 8 位字节交换,可能是 uint8_t 而不是 char?)
  • 谢谢你。我很确定char 在标准中被定义为单个字节。这不会相应地在 LE 和 BE 之间建立“映射”吗?换句话说,LE 处理器上的多字节字中的字节顺序与 BE 处理器上的字节顺序相反,无论单个字节的大小如何......对吗?
  • char 在 C 中是 byte,但在 C 中 byte 不一定是 8 位 - 它至少是 8 位。 uint8_t,在 C 中,正好是 8 位。见stackoverflow.com/questions/18576822/…
  • (参见上面的评论。)根据定义 LE 和 BE 有相反的顺序。但是在这个 BE/LE 业务中存在一些微妙的可能性,包括使用mixed endian 的平台。我怀疑混合端在今天已经过时,并且返回的前景令人怀疑。注意:某些应用程序需要与各种串行通信交换 bit 顺序。
  • 那么单个字节的大小是由编译器和CPU架构独立定义的(它总是将一个字节称为8位单元)?奇怪...我认为定义 16 位字节的编译器将是为相同“类型”的底层架构指定的编译器。
【解决方案3】:

您也可以使用 _bswap64 内在函数(在 Skylake 架构上延迟为 2,吞吐量为 0.5)。它是汇编指令bswap r64 的包装器,因此可能是最有效的:

将 64 位整数 a 的字节顺序颠倒,并将结果存入 dst。此内在函数用于小端值和大端值之间的转换。

#include <immintrin.h>

uint64_t swapLongIntrinsic(void *X) {
    return __bswap_64((uint64_t) X);
}

注意:不要忘记标题

【讨论】:

    猜你喜欢
    • 2014-02-24
    • 2014-04-15
    • 1970-01-01
    • 2022-05-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-22
    • 1970-01-01
    相关资源
    最近更新 更多