结构上的位移

Bitshift on structures

由于结构填充和对齐,我不确定这是否可行,但是,假设您通过将结构对齐到 4/8 字节来解决这个问题,是否可以在结构上进行移位,就好像它是单个变量?

我想做的是获取一个字符串(最多 8 个字节)并将其移入 64 位变量的高位。

就像我这样做:

#include <stdint.h>
#include <string.h>
void shiftstr(uint64_t* t,char* c,size_t len){
    memcpy(t, c, len);
    //now *t==0x000000617369616b
    *t<<=(sizeof(uint64_t)-len)*8;
    //now *t==0x617369616b000000
}
int main(){
    uint64_t k = 0;
    char n[] = "kaisa";
    shiftstr(&k, n,strlen(n));
    return 0;
}

这工作得很好,但如果我有两个 uint32_t 而不是 uint64_t,作为单个变量或结构。

#include <stdint.h>
#include <string.h>
struct U64{
    uint32_t x;
    uint32_t y;
};
void shiftstrstruct(struct U64* t, char* c, size_t len){
    memcpy(t, c, len);
    /*
     At this point I think
     x == 0x7369616b
     y == 0x00000061
     But I could be wrong
     */
    //but how can I perform the bit shift?
    //Where
    //x==0x0000006b
    //y==0x61697361
    
}
int main(){
    char n[] = "kaisa";
    struct U64 m = {0};
    shiftstrstruct(&m, n, strlen(n));
    return 0;
}

直到memcpy部分,它应该和我在单个变量上执行它一样。我相信 xy 的值在这种情况下是正确的。但是,如果是这种情况,则意味着需要将值从 x 转移到 y

我知道我可以强制转换,但是如果我想处理一个需要转换为两个 64 位变量或更大的 64 位变量的 16 字节字符串怎么办?

这样的移动结构可能吗?有更好的选择吗?

Is shifting structures like this possible?

不,不是真的。即使 xy 成员位于相邻的内存位置,对其中任何一个的 bit-shift 操作都作为对单个变量的整数操作执行。因此,您不能将位“移出”一个位并“移入”另一个位:在移位过程中“脱落”的位将丢失。

Is there a better alternative?

您必须自己实现这样的 multi-component bit-shift – 复制可能会丢失的位,并在将其他位内部移动到每个位之后,以某种方式将这些位屏蔽回结果中'component' 变量。具体如何执行此操作在很大程度上取决于用例。

这是一个包含两个 uint64_t 成员的结构的 right-shift 函数的一种可能实现(我没有为 count 添加任何 error-checking,我假设uint64_t 正好是 64 位宽):

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

typedef struct {
    uint64_t hi;
    uint64_t lo;
} ui128;

void Rshift(ui128* data, int count)
{
    uint64_t mask = (1uLL << count) - 1; // Set low "count" bits to 1
    uint64_t save = data->hi & mask;     // Save bits that fall off hi
    data->hi >>= count;                  // Shift the hi component
    data->lo >>= count;                  // Shift the lo component
    data->lo |= save << (64 - count);    // Mask in the bits from hi
    return;
}

int main()
{
    ui128 test = { 0xF001F002F003F004, 0xF005F006F007F008 };
    printf("%016llx%016llx\n", test.hi, test.lo);
    Rshift(&test, 16);
    printf("%016llx%016llx\n", test.hi, test.lo);
    return 0;
}

类似的逻辑可用于 left-shift 函数,但您随后需要保存 lo 成员的相关高位(最高有效位)并将它们屏蔽到移位的 hi 价值:

void Lshift(ui128* data, int count)
{
    uint64_t mask = ((1uLL << count) - 1) << (64 - count);
    uint64_t save = data->lo & mask;
    data->hi <<= count;
    data->lo <<= count;
    data->hi |= save >> (64 - count);
    return;
}

union是你的朋友,这就是你想要的:

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

typedef union _shift_u64{
    struct _u64{
        uint32_t x;
        uint32_t y;
    } __attribute__((__packed__)) U64;
    uint64_t x_and_y;
} SHIFT_U64;

int main(int argc, char* argv[]){
    SHIFT_U64 test;
    
    test.U64.x = 4;
    test.U64.y = 8;
    printf("test.U64.x=%d, test.U64.y=%d, test.x_and_y=%ld\n", test.U64.x, test.U64.y, test.x_and_y);

    test.x_and_y<<=1;
    printf("test.U64.x=%d, test.U64.y=%d, test.x_and_y=%ld\n", test.U64.x, test.U64.y, test.x_and_y);

    test.x_and_y>>=1;
    printf("test.U64.x=%d, test.U64.y=%d, test.x_and_y=%ld\n", test.U64.x, test.U64.y, test.x_and_y);

    return 0;
}

编辑:这个简单的程序说明了如何以另一种方式进行操作,但是您必须自己检查结转位和移位溢出以及移位下溢。 union 不关心数据,你只需要确保数据有意义。编译后,将程序的输出重定向到文件或hex-editor,读取程序的errorlevel。

Linux 示例:./a.out > a.out.bin; echo "errorlevel=$?"; xxd a.out.bin

#include <stdio.h>

typedef union _shift_it{
    struct _data{
        unsigned long x : 64;
        unsigned long y : 64;
    } __attribute__((__packed__)) DATA;
    unsigned char x_and_y[16];
} __attribute__((__packed__)) SHIFT_IT;

int main(int argc, char* argv[]){
    SHIFT_IT test;
    int errorlevel = 0;

    //bitmask for shift operation
    static const unsigned long LEFT_SHIFTMASK64 = 0x8000000000000000;
    static const unsigned long RIGHT_SHIFTMASK64 = 0x0000000000000001;

    //test data
    test.DATA.x = 0x2468246824682468; //high bits
    test.DATA.y = 0x1357135713571357; //low bits

    //binary output to stdout
    for(int i=0; i<16; i++) putchar(test.x_and_y[i]);

    //left shift
    if(test.DATA.x & LEFT_SHIFTMASK64) errorlevel += 1;
    test.DATA.x <<= 1;
    if(test.DATA.y & LEFT_SHIFTMASK64) errorlevel += 2;
    test.DATA.y <<= 1;

    //binary output to stdout
    for(int i=0; i<16; i++) putchar(test.x_and_y[i]);

    //right shift
    if(test.DATA.y & RIGHT_SHIFTMASK64) errorlevel += 4;
    test.DATA.y >>= 1;
    if(test.DATA.x & RIGHT_SHIFTMASK64) errorlevel += 8;
    test.DATA.x >>= 1;

    //binary output to stdout
    for(int i=0; i<16; i++) putchar(test.x_and_y[i]);

    //right shift
    if(test.DATA.y & RIGHT_SHIFTMASK64) errorlevel += 16;
    test.DATA.y >>= 1;
    if(test.DATA.x & RIGHT_SHIFTMASK64) errorlevel += 32;
    test.DATA.x >>= 1;

    //binary output to stdout
    for(int i=0; i<16; i++) putchar(test.x_and_y[i]);

    //left shift
    if(test.DATA.x & LEFT_SHIFTMASK64) errorlevel += 64;
    test.DATA.x <<= 1;
    if(test.DATA.y & LEFT_SHIFTMASK64) errorlevel += 128;
    test.DATA.y <<= 1;

    //binary output to stdout
    for(int i=0; i<16; i++) putchar(test.x_and_y[i]);

    return errorlevel;
}