简体   繁体   中英

C++ Unable to convert vector<char> to int

I am using boost ASIO to send message over TCP stream. I send the body size first in strictly 4 bytes length. Than on server side I make a vector<char> which I resize to 4 bytes and I put the message body size there. Here is how I convert vector of char std::vector<char> size; to int:

_packet.body_size = static_cast<int>(_packet.size[0]);

This scenario works when the value which is kept inside _packet.size[0] is not bigger of 124.

在此处输入图片说明

And in this scenario works. body_size is set to 124 as you can see.

However if the value gets bigger than 124 like 128 for example I am not able to parse it correctly in the same way as I did with 124 .

Take a look:

在此处输入图片说明

See to what number is set body_size . Why am I not able to convert bigger than 124 numbers?

Where is mistake and how can I fix it?

To construct a (4-byte/32-bit) integer from the 4 single-byte char values in your vector, and to do so safely , you will need to 'mask-in' each character into the relevant 8 bits of that integer.

You can do this using a combination of bitwise or ( | ) and bit-shift ( << ) operations in a short loop:

#include <iostream>
#include <vector>

int main()
{
    std::vector<char> cVec = { -128, 0, 0, 0 };
    int32_t iVal = 0;
    for (size_t i = 0; i < 4; ++i) {
        iVal |= static_cast<int>(cVec[i]) << (i * 8);
    }
    std::cout << iVal << std::endl;
    return 0;
}

The above will work for systems that use Little-Endian byte sequences for integers (most common processors, like the Intel x86/x64 family, use this system). On Big-Endian systems, you will need to add the bytes in reverse order, using the following as the 'body' of the for loop:

        iVal |= static_cast<int>(cVec[3-i]) << (i * 8);

Note: It may be tempting to just cast the address of the vector's data to a pointer-to-int and then dereference that (as I suggested in the comments). However, this is unsafe and introduces undefined behaviour , as it violates the Strict Aliasing Rules of the C++ language.

To directly answer the question:

You've extracted just the first byte, then converted it to an integer. If the subsequent bytes have meaningful information, they are "lost".

What you intended was to reinterpret all four bytes as an integer.

That would look like this:

_packet.body_size = *reinterpret_cast<int*>(&_packet.size[0]);

However, the other answers are correct in that this is not safe . You cannot take a sequence of char s and pretend that an int object exists there. Contrary to popular belief, it's not "all just bytes". 🙂 (Though it will often appear to work on your system, to be fair.)

The safe approach is std::memcpy :

assert(packet.size.size() >= sizeof(_packet.body_size));
std::memcpy(&_packet.body_size, &packet.size[0], sizeof(_packet.body_size));

… or std::copy :

assert(packet.size.size() >= sizeof(_packet.body_size));
std::copy(
   packet.size.begin(),
   packet.size.end(),
   static_cast<char*>(&_packet.body.size)
);

This works because the opposite conversion (pretending an int is a sequence of char s) is valid and safe.

You'd be better off, though, just bitmasking in the individual bytes to get endian-safety (and to ensure int size mismatches don't kill you!), as Adrian has already shown.

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