Итерация по вектору членов группы с указателями и смещениями - PullRequest
0 голосов
/ 30 января 2019

Я пытаюсь оптимизировать фрагмент кода, не гоняясь за указателями, как сейчас.Я хочу создать постоянное смещение, чтобы добавить к сохраненному указателю, чтобы перейти к следующей записи данных, см. Код ниже.Тем не менее, данные находятся рядом с классом или структурой, содержащей разные типы данных.

Так что я получаю правильное поведение в приведенном ниже фрагменте кода, то есть вывод 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], в указанном выше случае с некоторой формой постоянного смещения (как добавление числа бит между начальным адресом каждой записи данных.)?

1 Ответ

0 голосов
/ 30 января 2019

Нет, это не разрешено C ++.Арифметика указателя определяется только в том случае, если вы остаетесь внутри одного и того же массива (или после его конца).

expr.add # 4

Когда выражение J с целочисленным типом добавляется или вычитается из выражения P типа указателя, результат имеет тип P.

  • (4.1) Если Pвычисляет нулевое значение указателя, а J оценивает 0, результатом является нулевое значение указателя.

  • (4.2) В противном случае, если P указывает на элемент x[i] объекта массива x с n элементами, выражения P + J и J + P (где J имеет значение j) указывают на (возможно, гипотетический) элемент x[i+j], если 0≤i+j≤n ивыражение P - J указывает на (возможно, гипотетический) элемент x[i−j], если 0≤i−j≤n.

  • (4.3) В противном случае поведение не определено.

(4.1) не применяется, потому что вы не работаете с nullptr s.(4.2) не применяется, потому что вы работаете с double*, поэтому x в стандартной кавычке должно быть массивом double, то есть членом b вашей структуры.Оставлять свои границы с помощью арифметики с указателями, в соответствии с оставшимся (4.3), является неопределенным поведением.

То, что вы пытаетесь сделать здесь, это именно то, что хороший компилятор должен (и будет) делать в любом случае:1050 *

volatile double output;

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

https://godbolt.org/z/S9qkTf

Обратите внимание, как при разборке выполняется арифметика указателей, которую вы хотите (поскольку компилятор знает, что он работает на целевой платформе ).Вот самый внутренний цикл:

.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] выполняет всю дополнительную арифметику указателя бесплатно.

...