У меня есть структура Foo;
typedef struct {
int bar;
char baz;
} Foo;
Предположим, я объявляю массив Foo как;
Foo* arr = new Foo[300];
И приступим к инициализации каждого члена циклом. Я очень хотел бы иметь возможность получить массив всех панелей участников;
int* barr_arr = ...
Как это сделать наиболее эффективно? Есть ли способ использовать структуру памяти, чтобы мне не нужно было перебирать весь массив Foo?
Поскольку мы заранее знаем структуру памяти, можем ли мы использовать тот факт, что мы знаем адрес каждого члена, если мы умно подходим к выравниванию?
Вы не можете получить простой int* из вашего Foo*, потому что между целыми числами есть строки (так что обычная арифметика указателя уже приведет к неопределенному поведению). В C++ можно создавать итераторы, представляющие целые числа в этом диапазоне, которые затем можно использовать со всеми функциями STL. Между прочим, вы должны выбрать между C и C++ в ваших тегах.
@RSahu У меня есть данные, в которых иногда мне нужно посмотреть на всю полосу, базу и т. д. Одного элемента, а в других случаях мне нужно посмотреть на всю базу, но на панель мне наплевать. Например, скажем, у меня есть сетка глубин земли, воздуха и неба. Иногда мне нужно знать глубину каждого в данной ячейке, иногда мне нужно знать глубину земли в каждой ячейке.
@MaxLanghof Я удалил строку из примера - мои типы данных в основном числовые, и уловки из C вполне применимы.
Извините, но это все еще не C. Вам нужно решить, какой язык вы используете, поскольку ответы также могут отличаться. Они не одинаковы!
Неважно, что у вас есть между ними. Указательная арифметика вас просто подведет.
Если вы используете new в своем фактическом коде, C++ - правильный тег. @Sidyll пытается подчеркнуть, что, хотя вы можете написать C++, который почти полностью похож на код C, существуют значительные различия в стандартизированном поведении и компиляторах, которые очень важны, особенно для такого вопроса.
Это недостаточная причина для создания int*, указывающего на массив, содержащий все элементы barFoo. Вы можете передать ссылки на Foo и вытащить член bar. Я не думаю, что это такая уж большая проблема.
@sidyll, извините, вы правы, ключевое слово new делает это только C++. Удален тег.
@RSahu: Зависит. Если ему нужно передать массив bar в какой-то другой API, он должен каким-то образом его сконструировать.
@RSahu конкретным примером может быть то, что я хочу отправить непрерывный массив полос на графический процессор для параллельной обработки
@Linuxios есть.





Если Foo обычно существуют в массивах, и часто требуется доступ к соответствующим массивам bar и baz, я бы предложил переработать ваши структуры данных, чтобы они лучше соответствовали вашей проблеме. Очевидно, мы не читаем код, который вызвал этот вопрос, но, учитывая предоставленную информацию, я мог бы предложить что-то вроде:
struct FooArray {
int* bars;
char* bazes;
size_t n_elements;
};
Это устраняет необходимость выделять новый буфер для массива bar, что, в зависимости от того, сколько Foo обрабатывается, может привести к значительной экономии памяти.
Я также хотел бы отметить, что, если вы не работаете на низком уровне и на самом деле не нуждаетесь в int*, но можете обойтись с std::vector<int>, то ответ @R Sahu, вероятно, будет более подходящим решением.
Я рассматривал это решение, но иногда мне нужен быстрый доступ, например. Foo [3] и все его члены. Полагаю, мне было бы лучше узнать, в каких из них мой код тратит больше времени, и оптимизировать в этом направлении.
@ user1151695: Я не думаю, что индексация нескольких целочисленных массивов "медленная" ... вы всегда можете написать удобную функцию, которая сделает это за вас. С другой стороны, распределение ...
@ user1151695: Но да. Сначала профилируйте, потом оптимизируйте :).
What is the most efficient way to do this? Is there some way to exploit the memory layout such that I need not loop over the entire Foo array?
Я не думаю, что можно делать это без зацикливания. Вы можете упростить свой код, используя std::transform, но std::transform делает цикл.
Кроме того, я бы рекомендовал использовать std::vector вместо выделения массива с помощью new.
std::vector<Foo> arr(300);
....
std::vector<int> bArr(arr.size());
std::transform(arr.begin(), arr.end(), bArr.begin(), [] -> (Foo const& f) { return f.bar; });
Цель движет дизайном.
Если ваше основное использование - передача всех членов bar в строке, то же самое для членов baz, затем создайте отдельные контейнеры:
std::vector<int> bar;
std::vector<char> baz;
Тогда передать bar как массив очень просто: просто используйте bar.data().
Когда вы инициализируете первый массив, вы можете получить указатель на поле внутри каждого элемента и сохранить его в отдельном массиве.
struct Foo
{
int bar;
float baz;
};
const int SIZE = 5;
Foo foos[SIZE];
int *bars[SIZE];
for(int c = 0; c < SIZE; c++) {
foos[c].bar = c;
foos[c].baz = c;
bars[c] = &foos[c].bar; // Grab pointer to field
}
for(int c = 0; c < SIZE; c++) {
std::cout << "Bar Value: " << *bars[c] << std::endl;
}
Если вы добавите к вашему Foo конструктор, который принимает размер массива, у вас может быть только один объект Foo. Затем вы можете сделать так, чтобы вы могли получить доступ либо ко всем векторным данным, либо к отдельным элементам с нижним индексом:
#include <iostream>
#include <vector>
#include <memory>
struct Foo
{
std::vector<int> bars;
std::vector<char> bazs;
std::size_t size;
Foo(size_t size, int bar = 0, char baz = 0) :
bars(size, bar), bazs(size, baz), size{size}
{
}
auto operator[](size_t n)
{
// if (n >= size) ...
struct
{
int &bar;
char &baz;
} temp{ bars[n], bazs[n] };
return temp;
}
};
int main()
{
Foo arr(30, 100, 'a'); // 30 items
std::cout << arr[29].bar << std::endl;
std::cout << arr[29].baz << std::endl;
std::cout << arr.bars[29] << std::endl;
std::cout << arr.bazs[29] << std::endl;
std::unique_ptr<Foo> arr2 = std::make_unique<Foo>(25, 10, 'b'); // 25 items
std::cout << arr2->operator[](15).bar << std::endl;
std::cout << arr2->operator[](15).baz << std::endl;
arr2->bars[15] = 11;
std::cout << arr2->bars[15] << std::endl;
arr2->bazs[15] = 'c';
std::cout << arr2->bazs[15] << std::endl;
return 0;
}
Демо: https://ideone.com/TiVwOT
100
a
100
a
10
b
11
c
Это догадка о том, что OP на самом деле пытается сделать. Это хорошее предположение, но вопрос был бы лучше, если бы в этом не было необходимости.