Iterating through a vector of stucts's members with pointers and offsets

自作多情 提交于 2020-01-03 20:56:56

问题


I am trying to optimize a piece of code by not pointer chasing, as much as I currently am. I want to create a constant offset to add to a stored pointer to get to the next data entry, see below code. However, the data is sitting side a class or struct, containing different data types.

So I get the correct behavior in the below code snippet, ie the output is 1, 2, 3.

#include <iostream>
#include <vector>

// knows nothing about foo
class Readfoo
{ 
    private:
    int offset;
    double* pdouble;

    public:
    void SetPoint(double* apdouble, int aoffset)  
    {
        offset = aoffset;
        pdouble = apdouble;
    };

    const double& printfoo(int aidouble) const
    {
       return *(pdouble + offset*aidouble);
    };
};

// 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);
    bar[0].b[2] = 1;
    bar[1].b[2] = 2;
    bar[2].b[2] = 3;

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

My question is two-fold:

  1. Is this code actually legal, and will it ever produce undefined behavior?
  2. If it is not legal is there a way to, without storing the actual pointers, iterate through foo accessing b[2], in the above case, with some form of constant offset (Like adding the number of bits between the starting address of each data entry.)?

回答1:


No, this is not legal C++. Pointer arithmetic is only defined if you stay inside the same array (or one past its end).

expr.add#4

When an expression J that has integral type is added to or subtracted from an expression P of pointer type, the result has the type of P.

  • (4.1) If P evaluates to a null pointer value and J evaluates to 0, the result is a null pointer value.

  • (4.2) Otherwise, if P points to element x[i] of an array object x with n elements, the expressions P + J and J + P (where J has the value j) point to the (possibly-hypothetical) element x[i+j] if 0≤i+j≤n and the expression P - J points to the (possibly-hypothetical) element x[i−j] if 0≤i−j≤n.

  • (4.3) Otherwise, the behavior is undefined.

(4.1) does not apply because you're not operating on nullptrs. (4.2) does not apply because you are working with double*, so x in the standard quote must be a double array, i.e. the b member of your struct. Leaving its bounds with pointer arithmetic, according to the remaining (4.3), is undefined behavior.

What you are trying to do here is exactly what a good compiler should (and will) do under the hood anyway:

volatile double output;

void bar(std::vector<foo> bar, int innerOffset)
{
    for (foo& f : bar)
        output = f.b[innerOffset];
}

https://godbolt.org/z/S9qkTf

Notice how the disassembly does the pointer arithmetic you want (because the compiler knows that it works on the target platform). Here is the innermost loop:

.L3:
    movsd   xmm0, QWORD PTR [rax+24+rsi*8]
    add     rax, 104
    movsd   QWORD PTR output[rip], xmm0
    cmp     rdx, rax
    jne     .L3

104 bytes is exactly how big one foo is. The [rax+24+rsi*8] expression is doing all the additional pointer arithmetic for free.



来源:https://stackoverflow.com/questions/54441263/iterating-through-a-vector-of-stuctss-members-with-pointers-and-offsets

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!