简体   繁体   中英

std::cout not properly printing std::string created by reinterpret_cast of unsigned char array

I have an unsigned char array that stores 1 byte hex characters and I would like to do bitwise operations on these values.

After using reinterpret_cast to get them into a string (with the idea of using std::stringstream and std::bitset to do the necessary operations), I tried printing the string to see what the contents were. Rather strangely, I noticed that std::cout did not give the expected result, however using printf did!

Here is a quick example:

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;
}

With output:

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

Determined to find a solution, I tinkered around for a while - guessing that reinterpret_cast may be causing this behaviour. I eventually found that doing this:

std::cout << std::hex << (int)my_std_string[0] << std::dec << std::endl;

produced the desired result, at least for the first character anyway. Iterating through a loop gave the correct values for the other 11 bytes as well.

Can someone explain why this happens with std::cout and not with printf? At first, I thought that maybe I needed to cast it back to unsigned char but doing this had no effect. Why does casting to int give the correct output?

Is it safe to do bitwise operations on the values stored in a string after using reinterpret_cast as I have used? It has occurred to me that all of this may be rather pointless as I believe I can just do binary math on the unsigned char's directly, no? Advice here would be appreciated.

For those who are curious, I am trying to write a custom C++ console application (Microsoft Visual Studio 2010 on a Windows 7 64-bit machine) to interface with a CAN-USB adapter using the vendor's API. I expect to receive (as part of larger 'receive frame' structs) 8 byte unsigned character arrays with values in hex and I need to process these values to get usable data for my application. This processed data is then being stored in a protocol buffer for further interpretation in matlab.

Sorry if this seems like a silly question - I'm coming from a hardware background and haven't done any serious programming in a while (first post on SO!).

Change

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

to

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;

A std::string is a representation of a string, not purely an array of bytes. Therefore, passing it to std::cout will show a string. Your printf is printing individual values of your unsigned char array. The stl equivalent of this is an std::vector<unsigned char> .

You need to add the static_cast<unsigned>() . Otherwise std::cout will print each unsigned char value as a char ascii character. The output will be R _ sh ~ ) 3 ttsr U . You have to prevent this conversion by implicitly telling it.

I'm switching to Python, just for show:

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

I mean, these are the ASCII equivalents for your bytes. And that's what the constructor of std::string does: take an array of nul-terminated characters, and build a string from there. Your reinterpret_cast casts between unsigned char* and char* , and that is one of the few safe things it does.

What you probably want is to build a string with the text representation of the integers. For that use 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;

With printf you didn't have this problem because the type of the argument in printf is set by the format string, in your case %X means read as integer and write in hexadecimal. If you pass a char/unsigned char it is automatically promoted to integer (but beware! most types are not promoted that way, only chars and shorts ).

Can someone explain why this happens with std::cout and not with printf? At first, I thought that maybe I needed to cast it back to unsigned char but doing this had no effect. Why does casting to int give the correct output?

You told printf to output in hex, so it output in hex. What's the mystery exactly?

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM