反转64位值的字节顺序

7

我正在尝试为一个任务反转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);

3
char不是正确的数据类型。它通常只占用一个8位字节。如果您使用的平台支持64位的char,请说明。 - Mat
1
是的,它在x86_64架构上。 - nix
4
一个 char 是8位,不可能保存一个64位的值。 - Mat
3个回答

9

您不能将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

编辑,你可以使用类似于:

#include <inttypes.h>

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

PRIx64 会转换成“以十六进制形式打印 64 位数字所需的格式字符串”。这比上面的方法更加简洁。


4
不要为这样的任务使用固定宽度类型。适当的类型是uintptr_t。此外,您可以在假定宽度为64位的部分中添加#if UINTPRT_SIZE>UINT32_SIZE - Jens Gustedt
@jensgustedt 感谢您的澄清。我没有意识到有一个 uintptr_t - 应该想到去查找。每天都在学习! - Floris
2
е»әи®®дҪҝз”Ёprintf("0x016" PRIx64, (uint64_t)(&a))жҲ–иҖ…printf("0x%016llx\n", (unsigned long long)(&a))жқҘжӣҝд»ЈеҺҹжңүзҡ„д»Јз ҒпјҢд»ҘзЎ®дҝқж јејҸе’Ңж•ҙж•°еҢ№й…ҚгҖӮ - chux - Reinstate Monica
1
@chux - 那确实会更好。有几种方法可以改进这个问题...等我到了电脑旁边再做(而不是手机)。 - Floris

4

你还可以使用_bswap64内置函数(在Skylake架构上的延迟为2,吞吐量为0.5)。它是一种汇编指令bswap r64的包装器,可能是最有效的选择:

反转64位整数a的字节顺序,并将结果存储在dst中。这个内置函数提供了小端和大端值之间的转换。

#include <immintrin.h>

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

注意:不要忘记标题。

3

以下是将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?对于好的通用解决方案点个赞。 - chux - Reinstate Monica
谢谢chux。我非常确定char在标准中被定义为单个字节。这难道不会相应地使LE和BE之间的“映射”成立吗?换句话说,在LE处理器上多字节单词中字节的顺序与BE处理器上的顺序相反,而单个字节的大小则无关紧要...对吗? - barak manos
在C语言中,char是一个字节,但是C语言中的byte不一定是8位 - 它至少是8位。在C语言中,uint8_t恰好是8位。请参见http://stackoverflow.com/questions/18576822/sizeof-and-when-a-byte-is-larger-than-8-bits/18576877。 - chux - Reinstate Monica
按照定义,LE和BE的顺序是相反的。但是在BE/LE业务中存在微妙的可能性,包括使用混合字节序的平台(参见http://en.wikipedia.org/wiki/PDP-11)。我怀疑混合字节序今天已经过时,并且未来有返回的疑虑。注意:一些应用程序需要与各种串行通信交换位顺序。 - chux - Reinstate Monica
那么单个字节的大小是由编译器和CPU架构独立定义的(它总是将一个字节称为8位单位)?奇怪...我以为定义16位字节的编译器将是指定为相同“类型”的底层架构的编译器。 - barak manos
C语言保留了早期字节定义,即一组位,其中8位是常见的。当前文献(CPU手册)主要将字节定义为确切的8位。在一般情况下,“编译器定义16位字节-->相同“类型”是正确的,但CPU的本机整数位宽和使用它的C代码不一定匹配。显然,8位处理器不能拥有符合C标准的8位int,因为int必须至少范围为-327676到32767。同样,64位处理器仍然可以有C代码,其中int为32位。 - chux - Reinstate Monica

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接