-1

For using pointer arithmetics the type of a void pointer is converted multiple times.

The vector containing the data comes from an external source and returns a void pointer to access its data. Furthermore, the stride is also given by the external object and it heeds alignment requirements. To keep the example short it does not fully reflect those two facts.

// Example data from external source.
struct Data {
    float x;
    float y;

    Data() : x(5), y(8) {}
};

// Example vector from external source.
size_t elements = 3;
std::vector<Data> list;
list.resize(elements);

// Pointer conversions.
const void * voidPointer = list.data(); // The external object containing the vector returns a void pointer.
const uint8_t * countPointer = static_cast<const uint8_t*>(voidPointer);

// Pointer arithmetics.
unsigned stride = sizeof(Data); // The external object returning the stride heeds alignment requirements.
for (size_t counter = 0; counter < elements; ++counter, countPointer += stride) {
    const float * pointsToFloatX = reinterpret_cast<const float*>(countPointer);
    printf("%f, ", *pointsToFloatX); // Expecting something like 5.0000, 5.0000, 5.0000
}

The conversion to uint8_t does not affect the (float) data the pointer is pointing to since only the type of the pointer is converted?

Why const works on the countPointer though it gets incremented? Does const mean that the data the pointer points to may not be altered?

Why I have to use reinterpret_cast<const float*> instead of static_cast<const float*> to convert countPointer inside the loop?

Roi Danton
  • 7,933
  • 6
  • 68
  • 80
  • 4
    Such kind of pointer manipulation is undefined behavior. – AlexD Jul 26 '15 at 09:52
  • @AlexD It might be legal if `&list[0]` had been used instead of `&list` – M.M Jul 26 '15 at 10:49
  • @MattMcNabb But the vector is of `Data` type. Casting `Data*` to `uint8_t*` (via `void*`) does not look OK. – AlexD Jul 26 '15 at 10:56
  • @AlexD How would you do the pointer arithmetics when the type of the vector elements varies and the only information you have is the size of stride? – Roi Danton Jul 26 '15 at 11:00
  • @MattMcNabb I've corrected the question. – Roi Danton Jul 26 '15 at 11:02
  • @AlexD any type may be aliased by a character type , and character types do not have alignment requirements. Strictly speaking I'm not sure if going via `void *` is legal, but directly casting is legal. – M.M Jul 26 '15 at 11:09
  • @MattMcNabb But does `uint8_t` have to be `char`? – AlexD Jul 26 '15 at 11:19
  • @AlexD it has to be a character type (probably `unsigned char`). All the non-character integer types are at least 16 bit – M.M Jul 26 '15 at 11:24
  • @MattMcNabb Do you mean in theory or in practice? If we take the standard literally, all types from `char` to `long long` could be 1 byte, no? Regardless of that, `struct` padding and alignment is a source of UB. – AlexD Jul 26 '15 at 16:15
  • @AlexD `long long` could be 1 byte, but not `uint8_t` since it must have at least 64 bits. `uint8_t` has no alignment requirements; and the `float *` must be correctly aligned because each member of a struct must be correctly aligned for itself. You could use a `static_assert` to double check there is no padding in `Data` . – M.M Jul 27 '15 at 00:23
  • @MattMcNabb Not 8 bits? O am I too sleepy? – AlexD Jul 27 '15 at 00:25
  • @AlexD the standard has minimum requirements for type sizes: `int` 16bit, `long` 32bit, `long long` 64bit – M.M Jul 27 '15 at 00:26
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/84332/discussion-between-alexd-and-matt-mcnabb). – AlexD Jul 27 '15 at 00:33

1 Answers1

2

The conversion to uint8_t does not affect the (float) data the pointer is pointing to since only the type of the pointer is converted?

That's right. But when casting pointers, keep in mind that there are alignment requirements. Also, note that &list points to vector<Data>; you would use list.data() to get the Data* array.

Why const works on the countPointer though it gets incremented? Does const mean that the data the pointer points to may not be altered?

Yes. Write float * const to make the pointer itself constant or const float * const to get a constant pointer to constant data.

Why I have to use reinterpret_cast<const float*> instead of static_cast<const float*> to convert countPointer inside the loop?

Compatible types can be cast with static_cast, for others use reinterpret_cast. void* is compatible with any other. Avoid using reinterpret_cast unless you are very sure you know what you are doing.

Don Reba
  • 13,814
  • 3
  • 48
  • 61
  • Nice answer to a somewhat rambling question! +1 – rholmes Jul 26 '15 at 11:03
  • `uint8_t` and `float` are not compatible types b/c `uint8_t` has a shorter length in memory and therefore stores less information than `float`? – Roi Danton Jul 26 '15 at 11:12
  • 1
    @RoiDanton, they are not compatible because `uint8_t` is not a base class for `float` and because there is no special rule that says they should be compatible (like there is for `void*` and enum/integral pointer conversion, for example). – Don Reba Jul 26 '15 at 11:30
  • @rholmes I've updated the question to tell why a void pointer is used in the example and where the stride comes from. – Roi Danton Jul 29 '15 at 12:11