1

So to this is part 2 of a question I asked and was answered yesterday. So today I am coming back with a part 2. I am not sure if this should be this somewhere else so if a moderator wants to move it feel free.

So I am not going to reintroduce my problem here, so please go read part 1 Iterating through a vector of stucts's members with pointers and offsets

So I have come up with a solution to the problem so let me post a modified snippet of code that represents the solution I am going for,

#include <iostream>
#include <vector>

// knows nothing about foo
class Readfoo
{ 
    private:
    int offSetSize;
    char* pchar;

    public:
    void SetPoint(double* apstartDouble, int aoffSetSize)  
    {
        offSetSize = aoffSetSize;
        pchar = static_cast<char*> (static_cast<void*>(apstartDouble));
    };

    const double& printfoo(int aioffset) const
    {
       return *(static_cast<double*> (static_cast<void*>(pchar + aioffset*offSetSize)));
    };
};

// knows nothing about readFoo
struct foo
{ 
    int a[5];
    double b[10];
};

int main() 
{
    // populate some data (choose b [2] or other random entry.).
    std::vector<foo> bar(10);
    for(int ii = 0; ii < bar.size(); ii++) 
        bar[ii].b[2] = ii;

    // access b[2] for each foo using an offset.
    Readfoo newReadfoo;
    newReadfoo.SetPoint(&(bar[0].b[2]), sizeof(foo)/sizeof(char));
    for(int ii = 0; ii < bar.size(); ii++)
        std::cout<<"\n"<<newReadfoo.printfoo(ii);

    return 0; 
}

This, in my opinion, is legal, well I suppose that is what I am asking. Since now, in essence, I am converting my 'interpretation' of the struct foo and the vector bar (an array of foos) into a single array of bytes, or chars.

I.e. In this interpretation the data structure is a single array of chars, of foo size times bar size. When I iterate through this with an integral type I am in essence moving to some hypothetical char element (point 4.2 in the answer to part 1). The printfoo function then combines the next 8 bytes to form a double to return.

So is this legal and other than moving out of bounds of the bar vectors is there any reason why this will not work (I have tested it and it has yet to fail.)?

YSC
  • 38,212
  • 9
  • 96
  • 149
Bevan Jones
  • 199
  • 1
  • 2
  • 15
  • 1
    You can't tell from observing a program that it *doesn't* have undefined behaviour. – molbdnilo Jan 31 '19 at 17:27
  • 1
    `sizeof(foo)/sizeof(char)` will provide an offset to the 2nd element in `bar`. `sizeof(char)` is always `1`. – David C. Rankin Jan 31 '19 at 17:29
  • @DavidC.Rankin I realize that sizeof(foo)/sizeof(char) is basically the sizeof(char) and is pretty redundant – Bevan Jones Jan 31 '19 at 17:31
  • Doesn't your compiler warn of an `"invalid static_cast from type ‘double*’ to type ‘char*’"` if you remove the cast to `static_cast` first? Doesn't that raise red flags for you? (You should never cast to `void*` to avoid a static cast error). You can cast to `char*` (e.g. `pchar = (char*)apstartDouble;`) without violating the strict-aliasing rule, but a direct static cast will fail. What is it you are trying to accomplish beyond simply investigating pointers and addressing? – David C. Rankin Jan 31 '19 at 17:42
  • The code in the original question does not suddenly become legal by taking a detour through `void*`, `char*` and `void*` again. That it works _now_ doesn't mean your compiler won't find some fun optimization later (after further code changes) that breaks your program because the code didn't follow the rules. – Max Langhof Jan 31 '19 at 17:42
  • @DavidC.Rankin OP wants to read the same subobject from several structs in a vector, where the reading function isn't supposed to know about the structs or the vector. As elaborated on for the original question, you can't really do that in C++ - if you want to access memory, you should prove (using the type system, not circumventing it) what you think you'll find there. – Max Langhof Jan 31 '19 at 17:44
  • imho you should at least shortly summarize what the other question is about to make this one more selfcontained. Links can break at any time even when it is a link to another SO quesiton – 463035818_is_not_an_ai Jan 31 '19 at 17:52

1 Answers1

4

So is this legal ...

No it is not.

In the following expression:

pchar + aioffset*offSetSize

you manipulate pchar as if it were a pointer to a char array, which it is not (it's a pointer to a double array). And this is undefined behavior:

[expr.add]/6

For addition or subtraction, if the expressions P or Q have type “pointer to cv T”, where T and the array element type are not similar, the behavior is undefined. [ Note: In particular, a pointer to a base class cannot be used for pointer arithmetic when the array contains objects of a derived class type. — end note ]

In your case P is pchar and has type pointer to char but the array element it points to is a double.


... and other than moving out of bounds of the bar vectors is there any reason why this will not work (I have tested it and it has yet to fail.)?

Yes: Does the C++ standard allow for an uninitialized bool to crash a program?


To go further: pointer manipulation in C++ is a red flag. C++ pointer manipulation is dark magic which will burn your soul and consume your dog. C++ offers a lot of tools to write generic code. My advice: Ask about what you're trying to achieve rather than about your attempted solution. You'll learn a lot.

Community
  • 1
  • 1
YSC
  • 38,212
  • 9
  • 96
  • 149
  • Well I am going to be using it anyhow, since it works, and will work for the foreseeable problems I have, and I have no reason to see how a compiler would have any way to know it was not moving through a char array and thus has to implement it has a char + some number of bytes. But when I get burned I will come here and tell you I have been burned and you can say, "I told you so" Re: your last comment, it may be dark magic, but to be honest pointer manipulation and 'close to the hardware' type operations is where C++ outshines other languages. – Bevan Jones Jan 31 '19 at 17:43
  • And I don't know maybe I am arguing symatics, but in response to, 'In your case P is pchar and has type pointer to char but the array element it points to is a double', but double is just an array of 8 chars. So pchar points to the first byte of the array... – Bevan Jones Jan 31 '19 at 17:45
  • 3
    I told you so (just being early, ignore me for now). – YSC Jan 31 '19 at 17:47
  • _"but double is just an array of 8 chars"_ That is certainly not true in a semantic sense, although you are allowed to inspect memoy byte-wise. But reading a `double` from a `char*` is certainly UB by the standard. – Max Langhof Jan 31 '19 at 17:48
  • @BevanJones c++ outshines in providing you powerful abstractions. Pointers are an abstraction of what is actually going on in memory and they come with their own rules. Dont confuse the abstraction with the actual thing it models – 463035818_is_not_an_ai Jan 31 '19 at 17:51
  • 1
    Note: accessing though a `"a char or unsigned char type."` does not violate the strict aliasting rule -- however, this does place 100% of the responsibility on the programmer to avoid *Undefined Behavior*. – David C. Rankin Jan 31 '19 at 17:59
  • @DavidC.Rankin does that imply that I am responsible for making sure that 'the next 7 bytes' exist, and all 8 bytes actually represent a double? – Bevan Jones Jan 31 '19 at 19:29
  • You are responsible for ensuring all 8 bytes are (1) memory you can validly access, (2) is properly initialized, and (3) used in a manner that is consistent with the type of data stored at that location. There is no magic to it, but the compiler can no longer help you identify invalid use of the memory or with your pointer arithmetic used to access it. It is common in C to do what you are doing, but not as much in C++. That doesn't make what you are doing illegal, it makes what you are doing very prone to invoking *Undefined Behavior* unless done correctly. – David C. Rankin Jan 31 '19 at 21:30