std :: cout没有正确打印由unsigned char数组的reinterpret_cast创建的std :: string

时间:2014-07-07 20:22:10

标签: c++ cout stdstring reinterpret-cast

我有一个unsigned char数组,存储1个字节的十六进制字符,我想对这些值进行逐位运算。

使用reinterpret_cast将它们转换为字符串(想法使用std :: stringstream和std :: bitset进行必要的操作)后,我尝试打印字符串以查看内容是什么。相当奇怪的是,我注意到std :: cout没有给出预期的结果,但是使用printf做了!

这是一个简单的例子:

int main(int argc, char *argv[])
{

    unsigned char my_txt[] = {
        0x52, 0x5f, 0x73, 0x68, 0x7e, 0x29, 0x33, 0x74, 0x74, 0x73, 0x72, 0x55
    };
    unsigned int my_txt_len = 12;

    std::string my_std_string(reinterpret_cast<const char *>(my_txt), my_txt_len);

    for (size_t i=0;i<my_txt_len;i++)
        printf("%02X ", my_std_string[i]);      // Works fine!
    printf("\n");

    std::cout << my_std_string << std::endl;    // Bad stuff happens :S

    return 0;
}

输出:

52 5F 73 68 7E 29 33 74 74 73 72 55     // Expected
R_sh~)3ttsrU                            // ??

决心找到一个解决方案,我纠结了一段时间 - 猜测reinterpret_cast可能会导致这种行为。我最终发现这样做:

std::cout << std::hex << (int)my_std_string[0] << std::dec << std::endl;
无论如何,

产生了期望的结果,至少对于第一个角色。迭代循环给出了其他11个字节的正确值。

有人可以解释为什么这会发生在std :: cout而不是printf?起初,我想也许我需要把它归还给unsigned char,但这样做没有任何效果。为什么转换为int会给出正确的输出?

使用reinterpret_cast之后对存储在字符串中的值进行按位运算是否安全?在我看来,所有这些都可能毫无意义,因为我相信我可以直接对无符号字符进行二进制数学运算,不是吗?这里的建议将不胜感激。

对于那些好奇的人,我正在尝试编写一个自定义C ++控制台应用程序(Windows 7 64位计算机上的Microsoft Visual Studio 2010),以使用供应商的API与CAN-USB适配器连接。我期望接收(作为更大的接收帧结构的一部分)8字节无符号字符数组,其值为十六进制,我需要处理这些值以获得我的应用程序的可用数据。然后将处理后的数据存储在协议缓冲区中,以便在matlab中进一步解释。

很抱歉,如果这看起来像一个愚蠢的问题 - 我来自硬件背景,并且暂时没有做过任何严肃的编程(SO上的第一篇文章!)。

3 个答案:

答案 0 :(得分:2)

更改

std::cout << my_std_string << std::endl;    // Bad stuff happens :S

for( std::size_t i = 0; i < my_txt_len ; i++ )
{
    std::cout << std::hex << static_cast<unsigned>(my_std_string[i]) << " " ;
}
std::cout << std::endl;

std::string是字符串的表示,而不是纯字节数组。因此,将其传递给std::cout将显示一个字符串。您的printf正在打印unsigned char数组的各个值。相当于stl的是std::vector<unsigned char>

您需要添加static_cast<unsigned>()。否则std::cout会将每个unsigned char值打印为char ascii字符。输出将为R _ s h ~ ) 3 t t s r U。您必须通过隐式告知来阻止此转换。

答案 1 :(得分:0)

我正在切换到Python,仅用于show:

>>> s = [ 0x52, 0x5f, 0x73, 0x68, 0x7e, 0x29, 0x33, 0x74, 0x74, 0x73, 0x72, 0x55]
>>> ''.join(map(chr, s))
'R_sh~)3ttsrU'

我的意思是,这些是你的字节的ASCII等价物。这就是std::string的构造函数所做的:获取一系列以空字符结尾的字符,并从那里构建一个字符串。您reinterpret_castunsigned char*之间的char*演员阵容,这是为数不多的安​​全事件之一。

您可能想要的是使用整数的文本表示来构建字符串。使用std::ostringstream

std::ostringstream os;
os << std::hex << std::setfill('0') << std::uppercase;
for (size_t i=0;i<my_txt_len;i++)
    os << std::setw(2) << my_txt[i] << " ";
std::string txt = os.str();

std::cout << txt;

使用printf您没有遇到此问题,因为printf中的参数类型由格式字符串设置,在您的情况下%X表示读取整数并写入十六进制。如果您传递char/unsigned char,它会自动提升为整数(但要注意!大多数类型以这种方式提升,只有charsshorts)。

答案 2 :(得分:0)

  

有人可以解释为什么这会发生在std :: cout而不是printf?起初,我想也许我需要将它转换回unsigned char但是这样做没有任何效果。为什么cast to int会给出正确的输出?

您告诉printf以十六进制输出,因此以十六进制输出。究竟是什么谜?