结构上的位移
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
部分,它应该和我在单个变量上执行它一样。我相信 x
和 y
的值在这种情况下是正确的。但是,如果是这种情况,则意味着需要将值从 x
转移到 y
。
我知道我可以强制转换,但是如果我想处理一个需要转换为两个 64 位变量或更大的 64 位变量的 16 字节字符串怎么办?
这样的移动结构可能吗?有更好的选择吗?
Is shifting structures like this possible?
不,不是真的。即使 x
和 y
成员位于相邻的内存位置,对其中任何一个的 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;
}
由于结构填充和对齐,我不确定这是否可行,但是,假设您通过将结构对齐到 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
部分,它应该和我在单个变量上执行它一样。我相信 x
和 y
的值在这种情况下是正确的。但是,如果是这种情况,则意味着需要将值从 x
转移到 y
。
我知道我可以强制转换,但是如果我想处理一个需要转换为两个 64 位变量或更大的 64 位变量的 16 字节字符串怎么办?
这样的移动结构可能吗?有更好的选择吗?
Is shifting structures like this possible?
不,不是真的。即使 x
和 y
成员位于相邻的内存位置,对其中任何一个的 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;
}