遍历带有指针和偏移量的stucts成员向量 [英] Iterating through a vector of stucts's members with pointers and offsets

查看:94
本文介绍了遍历带有指针和偏移量的stucts成员向量的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试通过不像我目前那样多地追踪指针来优化一段代码.我想创建一个常量偏移量以添加到存储的指针中,以获取下一个数据条目,请参见下面的代码.但是,数据位于类或结构的旁边,包含不同的数据类型.

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.

因此,我在下面的代码段中获得了正确的行为,即输出为1、2、3.

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;
}

我的问题有两个:

  1. 此代码确实合法,并且会产生未定义的行为吗?
  2. 如果不合法,有一种方法可以在不存储实际指针的情况下,通过foo访问b [2]进行迭代,在上述情况下,使用某种形式的常量偏移量(例如,将起始位置之间的位数相加)每个数据条目的地址.)?

推荐答案

否,这不是合法的C ++.仅当您停留在同一数组(或数组末尾)内时,才定义指针算术.

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

将具有整数类型的表达式J添加到指针类型的表达式P或从中减去时,结果的类型为P.

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) 如果P计算为空指针值,而J计算为0,则结果为空指针值.

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

(4.2) 否则,如果P指向具有n个元素的数组对象x的元素x[i],则表达式P + JJ + P(其中J的值为j)指向(可能是-如果0≤i+j≤n,则假定元素x[i+j],如果0≤i−j≤n,则表达式P - J指向(可能是假想的)元素x[i−j].

(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) 否则,行为是不确定的.

(4.3) Otherwise, the behavior is undefined.

(4.1)不适用,因为您未在nullptr上进行操作. (4.2)不适用,因为您正在使用double*,因此标准引号中的x必须是double数组,即结构的b成员.根据其余的(4.3),使用指针算法来限制边界是不确定的行为.

(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个字节恰好是一个foo的大小. [rax+24+rsi*8]表达式免费进行所有附加的指针运算.

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

这篇关于遍历带有指针和偏移量的stucts成员向量的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆