遍历带有指针和偏移量的stucts成员向量 [英] Iterating through a vector of stucts's members with pointers and offsets
问题描述
我正在尝试通过不像我目前那样多地追踪指针来优化一段代码.我想创建一个常量偏移量以添加到存储的指针中,以获取下一个数据条目,请参见下面的代码.但是,数据位于类或结构的旁边,包含不同的数据类型.
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;
}
我的问题有两个:
- 此代码确实合法,并且会产生未定义的行为吗?
- 如果不合法,有一种方法可以在不存储实际指针的情况下,通过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).
将具有整数类型的表达式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 andJ
evaluates to0
, the result is a null pointer value.
(4.2)
否则,如果P
指向具有n个元素的数组对象x
的元素x[i]
,则表达式P + J
和J + 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 nullptr
s. (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];
}
请注意反汇编如何执行所需的指针算术(因为编译器知道它在目标平台上可以正常工作).这是最里面的循环:
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屋!