0

let's say i have an array of float someArray[10], witch i have assigned values for all of its elements. I know that someArray has 40 bytes allocated memory because every float has 4 bytes.

now i want to read these 40 bytes of memory with two byte by two byte from the beginning and interpret them as unit16_t so i will have and array of unit16_t intArray[20]. i don't want to use union.

i also read these questions but did't help:

read memory reinterpret

yekanchi
  • 813
  • 1
  • 12
  • 30

2 Answers2

3

This is very tricky, extremely low-level stuff. Are you absolutely sure you cannot solve this in any other way? Have you taken endianness into account? You are? You have? OK.

C++ does not allow you to take a contiguous region in memory and interpret it as anything you want. Pretty much the only thing you are allowed to do is to interpret a pointer to the beginning of a contiguous block of memory that belongs to one single array as a char*, unsigned char* or since C++17 also std::byte* (see strict aliasing rules, section [basic.lval] in the Standard, for C++17 it’s § 6.10 (8)). Essentially that means you can interpret an array as a sequence of bytes. That’s it, but it’s enough to copy the memory around. And that’s how you convert between unrelated types.

Using C-style arrays (they are error prone, so avoid if possible):

#include <cstddef>
#include <cstdint>
#include <cstring>

int main()
{
    constexpr std::size_t item_count = 10;

    float float_array[item_count] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
    std::uint16_t int_array[item_count * (sizeof(float) / sizeof(uint16_t))];

    std::memcpy(
            reinterpret_cast<char*>(int_array),
            reinterpret_cast<char*>(float_array),
            item_count * sizeof(float));
}

Or with std::array:

#include <array>
#include <cstddef>
#include <cstdint>
#include <cstring>

int main()
{
    std::array<float, 10> float_array = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
    std::array<std::uint16_t, float_array.size() * (sizeof(float) / sizeof(uint16_t))> int_array;

    std::memcpy(
            reinterpret_cast<char*>(int_array.data()),
            reinterpret_cast<char*>(float_array.data()),
            float_array.size() * sizeof(float));
}
besc
  • 2,507
  • 13
  • 10
  • Why `reinterpret_cast`? – eerorika Nov 26 '18 at 18:25
  • @eerorika I am innocent! I just copied besc's list \*whistles\* – Swordfish Nov 26 '18 at 18:55
  • @eerorika I double checked. Indeed the standard does not mention signed char. Removed it from the answer. – besc Nov 26 '18 at 18:58
  • You might want to ad a link to [basic.lval/11.8](http://eel.is/c++draft/basic.lval#11.8) – Swordfish Nov 26 '18 at 19:02
  • @eerorika *Why `reinterpret_cast`?* – don't know [conv.ptr/2](http://eel.is/c++draft/conv#ptr-2) – Swordfish Nov 26 '18 at 19:16
  • I fail to see how creating an exact copy of 10 floats in memory to another place in memory, then casting a pointer to it is any more defined than simply looking at the memory in place. Either way the results have all the potential issues of endian-ness, alignment, misinterpreted values. – ttemple Nov 26 '18 at 22:11
  • @ttemple *[...] then casting a pointer to it [...]* – huh? where? – *is any more defined than simply looking at the memory in place* – It is guaranteed what happens that way. – Swordfish Nov 26 '18 at 23:07
  • I guess we can agree to disagree. I see no benefit in making a copy of memory to simply view it as a different type. This scenario happens often when communicating with hardware. Since the hardware and compilers usually don't change for the life of an embedded application, once the endian and alignment issues are worked out, life goes on in "the wonderful world of undefined behavior". – ttemple Nov 27 '18 at 13:02
  • @ttemple The benefit is that you don’t run the risk that your wonderful life ends suddenly when you change a line in your source code. You cannot reason about UB. You maybe can proof that one specific UB-ridden piece of code works with a single compiler in a single configuration and version for a single platform. You change even one parameter in this setup – and that includes the affected source code – and you have to proof everything all over again. In reality often enough no memory is copied because the compiler recognizes the pattern and optimizes it. There’s no reason to take the risk. – besc Nov 27 '18 at 16:36
-3
float someArray[10]{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
void * void_ptr_to_array  = static_cast<void*>(someArray);
uint16_t * ptr_to_array = static_cast<uint16_t*>(void_ptr_to_array);

for(int i = 0; i < 20; ++i)
    std::cout << ptr_to_array[i] << '\n';

Outputs

0
16256
0
16384
0
16448
0
16512
0
16544
0
16576
0
16608
0
16640
0
16656
0
16672
ttemple
  • 852
  • 5
  • 20
  • Sometimes when you are dealing with hardware you have to do stupid stuff. – ttemple Nov 26 '18 at 17:38
  • Then you should call that out. UB land is not fun and just saying this is a way, without documenting that you'll be in UB land, isn't nice. MOst of the time you should be fine, *but*, you could also be in for a world of hurt. – NathanOliver Nov 26 '18 at 17:45
  • 1
    It would be more constructive to point out exactly what is undefined about it. This will certainly 'view' the 40 bytes that were entered as floats, and show what they are when interpreted as uint16_t values, which seems to be what was asked. – ttemple Nov 26 '18 at 17:50
  • 1
    The reinterpret_cast is UB. C++ only allows you to cast to char* (or its signed/unsigned variants) here. The correct solution is cast to char* and memcpy. – besc Nov 26 '18 at 17:53
  • 1
    And that is the undefined part. treating a float array as a `uint16_t` array like you have us UB. It violates strict aliasing. – NathanOliver Nov 26 '18 at 17:54
  • What do you hope to achieve with that detour over `void*`? – Swordfish Nov 26 '18 at 18:14
  • according to https://stackoverflow.com/questions/573294/when-to-use-reinterpret-cast the problem with reinterpret cast is it is not guaranteed to preserve the address, whereas static cast does. Therefore, to assure preserving the address, this is probably better than reinterpret cast. – ttemple Nov 26 '18 at 18:16
  • @ttemple `reinterpret_cast` also lets you cast to `char*` to look at an object's memory representation. It won't change the address there either. This code is UB and should not be used, and using `static_cast` in no way fixes the problem. `memcpy` is the portable solution. – François Andrieux Nov 26 '18 at 18:26
  • Francois, it would benefit me if you would clarify what is undefined behavior now. besc pointed out that the reinterpret cast was UB. I researched it, and agree. So I change the answer to clear that up. If I simply want to view what is in the memory locations where the float array was stored, I'm not seeing the benefit of copying the memory somewhere else to do so. if memcpy really copies the memory, it isn't going to fix endian issues, nor convert the floats properly to int's, etc. – ttemple Nov 26 '18 at 18:37
  • It is undefined because the standard says it is. It may or may not work correctly on your PC with the current compiler version, but it is certainly not guaranteed to work correctly in general. See https://timsong-cpp.github.io/cppwp/n3337/basic.lval#10 – Donnie Nov 26 '18 at 19:19