3

Here is a simple test program that illustrates the problem I faced:

#include <iostream>
#include <stdlib.h>
#include <inttypes.h>
#include <vector>

using namespace std;
typedef unsigned char Byte;

int main( )
{
    uint32_t ui32 = 12;
    size_t sizeofUi32 = sizeof ui32;
    cout << "sizeofUi32: " << sizeofUi32 << endl;
    vector<Byte> v(10);
    std::copy(&ui32, &ui32 + sizeof ui32, &v[4]);

    uint32_t result = 0;
    std::copy(&v[4], &v[4] + sizeof ui32, &result);

    cout << "Result: " << result << " sizeofUi32: " << sizeofUi32 << endl;

    return 0;
}

output:

sizeofUi32: 4    
Result: 12 sizeofUi32: 17179869184

I thought this issue might be due to std::copy accepting iterators not pointers, but from what I got in SO here,

a pointer IS an iterator

So there must be a simple issue with my sample code, that I' missing. But I can't spot it. Could you please explain, what's wrong here?

EDIT 1:

So from the answers I got the idea, that to deserialize a vector of bytes, if I know the right order and types of stored data in vector, I can avoid using std::copy and just assign vector values to variables of proper types. It works, but is it safe?

uint32_t a = v[4];
uint8_t b = v[8];
Community
  • 1
  • 1
rightaway717
  • 2,631
  • 3
  • 29
  • 43

2 Answers2

8

The immediate problem is here:

std::copy(&ui32, &ui32 + sizeof ui32, &v[4]);
                       ^^^^^^^^^^^^^

&ui32 has type uint32_t *, and adding anything to that already takes into account the object's size. You're effectively attempting to copy sizeof ui32 uint32_t objects, but you only have a single one, so you should be using + 1.

That aside, using std::copy with pointers of different types is likely not to give you the results you expect. It has the effect of v[4] = ui32;, which works as long as ui32 is inside Byte's range, which it is here, but that's not something you can rely on in general.

The second std::copy has roughly the same problem, but in the opposite direction.

What you could do is:

std::copy((Byte*) &ui32, (Byte*) (&ui32 + 1), &v[4]);
// or std::copy((Byte*) &ui32, (Byte*) &ui32 + sizeof ui32, &v[4]);
...
std::copy(&v[4], &v[4] + sizeof ui32, (Byte*) &result);
  • So, from what I've got from your answer, I need to cast pointers used in `std::copy` to the same type to achieve the desired result, right? – rightaway717 Dec 21 '14 at 14:53
  • @rightaway717 Not just to the same type, specifically to `Byte*` (`unsigned char*`). If you attempt to convert the other way around, if you attempt to use `uint32_t*` for all three arguments, you might find that `&v[4]` is not suitably aligned. –  Dec 21 '14 at 14:59
3

The problem isn’t with std::copy but with pointer arithmetic. As you’ve said, “a pointer IS an iterator”. But more importantly, it’s strongly typed. So a pointer-to-uint32_t is different from a pointer-to-unsigned char.

Adding &ui32 + sizeof ui32 effectively treats ui32 as if it were the beginning of a contiguous array with 4 elements (of type uint32_t).

Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214