我正在学习C中的位操作,我编写了一个简单的程序。但程序失败了。有人可以查看这段代码吗? 基本上我想提取并重新组合一个4字节的“长”变量到它的感应字节,反之亦然。这是我的代码:
printf("sizeof char= %d\n", sizeof(char));
printf("sizeof unsigned char= %d\n", sizeof(unsigned char));
printf("sizeof int= %d\n", sizeof(int));
printf("sizeof long= %d\n", sizeof(long));
printf("sizeof unsigned long long= %d\n", sizeof(unsigned long long));
long val = 2;
int k = 0;
size_t len = sizeof(val);
printf("val = %ld\n", val);
printf("len = %d\n", len);
char *ptr;
ptr = (char *)malloc(sizeof(len));
//converting 'val' to char array
//val = b3b2b1b0 //where 'b is 1 byte. Since 'long' is made of 4 bytes, and char is 1 byte, extracting byte by byte of long into char
//do{
//val++;
for(k = 0; k<len; k++){
ptr[k] = ((val >> (k*len)) && 0xFF);
printf("ptr[%d] = %02X\n", k,ptr[k]);
}
//}while(val < 12);
//reassembling the bytes from char and converting them to long
long xx = 0;
int m = 0;
for(m = 0; m< len; m++){
xx = xx |(ptr[m]<<(m*8));
}
printf("xx= %ld\n", xx);
为什么我看不到xx返回2?此外,无论'val'的值如何,ptr [0]似乎存储1 :( 请帮忙
提前致谢
答案 0 :(得分:4)
ptr[k] = ((val >> (k*len)) && 0xFF);
应该是
ptr[k] = ((val >> (k*8)) & 0xFF);
&&
用于条件语句和&amp;按位和。
此外,当您将值拆分为字符时,您希望使用与字节中一样多的位移位循环的每次迭代。这几乎总是8,但可能是其他东西。头文件limits.h
包含有关该信息的信息。
答案 1 :(得分:3)
我注意到的一些事情:
因此,您的代码的固定版本将是:
printf("sizeof char= %d\n", sizeof(char));
printf("sizeof unsigned char= %d\n", sizeof(unsigned char));
printf("sizeof int= %d\n", sizeof(int));
printf("sizeof long= %d\n", sizeof(long));
printf("sizeof unsigned long long= %d\n", sizeof(unsigned long long));
long val = 2;
int k = 0;
size_t len = sizeof(val);
printf("val = %ld\n", val);
printf("len = %d\n", len);
unsigned char *ptr;
ptr = (unsigned char *)malloc(len);
//converting 'val' to char array
//val = b3b2b1b0 //where 'b is 1 byte. Since 'long' is made of 4 bytes, and char is 1 byte, extracting byte by byte of long into char
//do{
//val++;
for(k = 0; k<len; k++){
ptr[k] = ((val >> (k*8)) & 0xFF);
printf("ptr[%d] = %02X\n", k,ptr[k]);
}
//}while(val < 12);
//reassembling the bytes from char and converting them to long
long xx = 0;
int m = 0;
for(m = 0; m< len; m++){
xx = xx |(ptr[m]<< m*8);
}
printf("xx= %ld\n", xx);
此外,将来,这样的问题更适合https://codereview.stackexchange.com/
答案 2 :(得分:1)
正如其他人现在提到的那样,我不确定ptr[k] = ((val >> (k*len)) && 0xFF);
是否符合您的要求。 &&
运算符是一个布尔运算符。如果(value >> (k*len))
是某个非零值,并且0xFF
是某个非零值,则存储到ptr[k]
中的值将为1。这就是布尔运算符的工作方式。也许您打算使用&
代替&&
。
此外,您已选择使用适用于unsigned
类型的移位运算符,但对于已签名类型具有各种非可移植方面。 xx = xx |(ptr[m]<<(m*8));
可能会调用未定义的行为,例如,因为它看起来可能会导致有符号整数溢出。
在C中,sizeof (char)
总是 1,因为sizeof
运算符会告诉您有多少char
用于表示类型。例如。 sizeof (int)
告诉您有多少char
用于表示int
。这是CHAR_BIT
的变化。因此,您的代码不应该依赖sizeof
类型。
事实上,如果您希望代码是可移植的,那么您不应期望能够在int
中存储大于32767或小于-32767的值。这与大小无关,因为可能存在填充位。总结一下:sizeof
a类型不一定反映它可以存储的值集合!
为其应用选择变量的类型,可移植。如果您的应用程序不需要超出该范围的值,那么int
将会正常运行。否则,您可能想要考虑使用long int
,它可以便携地存储(包括)-2147483647和2147483647之间的值。如果您需要超出该值的值,请使用long long int
,它将为您提供至少包含-9223372036854775807和922337203685477580之间值的保证范围。除此之外的任何内容都可能需要多精度算术库,例如{{3 }}
如果您不希望使用负值,则应使用unsigned
类型。
考虑到你的可移植选择整数类型,现在有意义的是你可以设计一种可移植的方式将这些整数写入文件,并从文件中读取这些整数。您需要将符号和绝对值提取到unsigned int
:
unsigned int sign = val < 0; /* conventionally 1 for negative, 0 for positive */
unsigned int abs_val = val;
if (val < 0) { abs_val = -abs_val; }
...然后构造一个由abs_val
和sign
组成的8位块数组,合并在一起。我们已经决定使用便携式决策,我们的int
只能存储16位,因为我们只存储-32767和32767之间的值。结果,不需要循环或按位移位。我们可以使用乘法来移动我们的符号位,并使用除法/模数来减少我们的绝对值。考虑到符号通常与最重要的位一起使用,它位于数组的开始(大端)或结束(小端)。
unsigned char big_endian[] = { sign * 0x80 + abs_val / 0x100,
abs_value % 0x100 };
unsigned char lil_endian[] = { abs_value % 0x100,
sign * 0x80 + abs_val / 0x100 };
为了扭转这个过程,我们相反地执行相反的操作(即,使用除法和模来代替乘法,乘法代替除法和加法,提取符号位并改变值):< / p>
unsigned int big_endian_sign = array[0] / 0x80;
int big_endian_val = big_endian_sign
? -((array[0] % 0x80) * 0x100 + array[1])
: ((array[0] % 0x80) * 0x100 + array[1]);
unsigned int lil_endian_sign = array[1] / 0x80;
int lil_endian_val = lil_endian_sign
? -((array[1] % 0x80) * 0x100 + array[0])
: ((array[1] % 0x80) * 0x100 + array[0]);
long
的代码变得有点复杂,使用二元运算符变得值得。符号和绝对值的提取基本保持不变,唯一的变化是变量的类型。我们仍然不需要循环,因为我们做出了一个决定,我们只关心值可表示的值。以下是我如何从long val
转换为unsigned char[4]
:
unsigned long sign = val < 0; /* conventionally 1 for negative, 0 for positive */
unsigned long abs_val = val;
if (val < 0) { abs_val = -abs_val; }
unsigned char big_endian[] = { (sign << 7) | ((abs_val >> 24) & 0xFF),
(abs_val >> 16) & 0xFF,
(abs_val >> 8) & 0xFF,
abs_val & 0xFF };
unsigned char lil_endian[] = { abs_val & 0xFF,
(abs_val >> 8) & 0xFF,
(abs_val >> 16) & 0xFF,
(sign << 7) | ((abs_val >> 24) & 0xFF) };
...以下是我将如何转换回签名值:
unsigned int big_endian_sign = array[0] >> 7;
long big_endian_val = big_endian_sign
? -((array[0] & 0x7F) << 24) + (array[1] << 16) + (array[2] << 8) + array[3]
: ((array[0] & 0x7F) << 24) + (array[1] << 16) + (array[2] << 8) + array[3];
unsigned int lil_endian_sign = array[3] >> 7;
long lil_endian_val = lil_endian_sign
? -((array[3] & 0x7F) << 24) + (array[2] << 16) + (array[1] << 8) + array[0]
: ((array[3] & 0x7F) << 24) + (array[2] << 16) + (array[1] << 8) + array[0];
我将让您为unsigned
和long long
类型设计一个方案......并开放发言以征求意见: