按位包含或的错误结果
Wrong result with bitwise inclusive OR
我不明白为什么 inclusive OR return 错误的结果。
char arr[] = { 0x0a, 0xc0 };
uint16_t n{};
n = arr[0]; // I get 0x000a here.
n = n << 8; // Shift to the left and get 0x0a00 here.
n = n | arr[1]; // But now the n value is 0xffc0 instead of 0x0ac0.
这个例子有什么错误?控制台应用程序,MVS Community 2017。
您已成为有符号整数提升的受害者。
将0xc0
分配给数组中的第二个元素(由于MVS默认为signed char)时,表示如下:
arr[1] = 1100 - 0000, or in decimal -64
当它被转换为 uint16_t
时,它被提升为一个整数,值为 -64
。这是:
n = 1111 - 1111 - 1100 - 0000 = -64
由于 2's complement 整数的实现。
因此:
n = 1111 - 1111 - 1100 - 0000
arr[1] = 0000 - 0000 - 1010 - 0000 (after being promoted)
n | arr[1] = 1111 - 1111 -1110-0000 = 0xffc0
我通过以下方式使其正常工作:
int arr[] = { 0x0a, 0xc0 };
int n{};
n = arr[0]; // I get 0x000a here.
n = n << 8; // Shift to the left and get 0x0a00 here.
n = n | arr[1];
std::cout << n << std::endl;
如果将 'arr' 数组保留为字符,则会出现一些截断。
意外的 0xff
是由 0xc0
的 sign bit extension 引起的。
0xc0 = 0b11000000
因此,设置了最高位,这意味着 char
的符号(如 signed char
)。
请注意,C++ 中的所有算术和按位运算至少适用于 int
(或 unsigned int
)。较小的类型在之前提升并在之后剪裁。
另请注意,char
可能已签名或未签名。这取决于编译器实现。显然,它是在 OP 的情况下签名的。为了防止意外的符号扩展,参数必须变得无符号(足够早)。
示范:
#include <iostream>
int main()
{
char arr[] = { '\x0a', '\xc0' };
uint16_t n{};
n = arr[0]; // I get 0x000a here.
n = n << 8; // Shift to the left and get 0x0a00 here.
n = n | arr[1]; // But now the n value is 0xffc0 instead of 0x0ac0.
std::cout << std::hex << "n (wrong): " << n << std::endl;
n = arr[0]; // I get 0x000a here.
n = n << 8; // Shift to the left and get 0x0a00 here.
n = n | (unsigned char)arr[1]; // (unsigned char) prevents sign extension
std::cout << std::hex << "n (right): " << n << std::endl;
return 0;
}
会话:
g++ -std=c++11 -O2 -Wall -pthread main.cpp && ./a.out
n (wrong): ffc0
n (right): ac0
生活演示 coliru
注:
我不得不将
char arr[] = { 0x0a, 0xc0 };
更改为
char arr[] = { '\x0a', '\xc0' };
以解决严重的编译器投诉。我想,这些投诉与这个问题密切相关。
我不明白为什么 inclusive OR return 错误的结果。
char arr[] = { 0x0a, 0xc0 };
uint16_t n{};
n = arr[0]; // I get 0x000a here.
n = n << 8; // Shift to the left and get 0x0a00 here.
n = n | arr[1]; // But now the n value is 0xffc0 instead of 0x0ac0.
这个例子有什么错误?控制台应用程序,MVS Community 2017。
您已成为有符号整数提升的受害者。
将0xc0
分配给数组中的第二个元素(由于MVS默认为signed char)时,表示如下:
arr[1] = 1100 - 0000, or in decimal -64
当它被转换为 uint16_t
时,它被提升为一个整数,值为 -64
。这是:
n = 1111 - 1111 - 1100 - 0000 = -64
由于 2's complement 整数的实现。
因此:
n = 1111 - 1111 - 1100 - 0000
arr[1] = 0000 - 0000 - 1010 - 0000 (after being promoted)
n | arr[1] = 1111 - 1111 -1110-0000 = 0xffc0
我通过以下方式使其正常工作:
int arr[] = { 0x0a, 0xc0 };
int n{};
n = arr[0]; // I get 0x000a here.
n = n << 8; // Shift to the left and get 0x0a00 here.
n = n | arr[1];
std::cout << n << std::endl;
如果将 'arr' 数组保留为字符,则会出现一些截断。
意外的 0xff
是由 0xc0
的 sign bit extension 引起的。
0xc0 = 0b11000000
因此,设置了最高位,这意味着 char
的符号(如 signed char
)。
请注意,C++ 中的所有算术和按位运算至少适用于 int
(或 unsigned int
)。较小的类型在之前提升并在之后剪裁。
另请注意,char
可能已签名或未签名。这取决于编译器实现。显然,它是在 OP 的情况下签名的。为了防止意外的符号扩展,参数必须变得无符号(足够早)。
示范:
#include <iostream>
int main()
{
char arr[] = { '\x0a', '\xc0' };
uint16_t n{};
n = arr[0]; // I get 0x000a here.
n = n << 8; // Shift to the left and get 0x0a00 here.
n = n | arr[1]; // But now the n value is 0xffc0 instead of 0x0ac0.
std::cout << std::hex << "n (wrong): " << n << std::endl;
n = arr[0]; // I get 0x000a here.
n = n << 8; // Shift to the left and get 0x0a00 here.
n = n | (unsigned char)arr[1]; // (unsigned char) prevents sign extension
std::cout << std::hex << "n (right): " << n << std::endl;
return 0;
}
会话:
g++ -std=c++11 -O2 -Wall -pthread main.cpp && ./a.out
n (wrong): ffc0
n (right): ac0
生活演示 coliru
注:
我不得不将char arr[] = { 0x0a, 0xc0 };
更改为char arr[] = { '\x0a', '\xc0' };
以解决严重的编译器投诉。我想,这些投诉与这个问题密切相关。