#c
#c #массивы #алгоритм #многомерный массив #заполнить
Вопрос:
Вот что я использую:
class something
{
char flags[26][80];
} a;
std::fill(amp;a.flags[0][0], amp;a.flags[0][0] 26 * 80, 0);
(Обновление: я должен был раньше дать понять, что использую это внутри класса.)
Ответ №1:
Простой способ инициализации 0
массива находится в определении:
char flags[26][80] = {};
Если вы хотите использовать std::fill
или хотите сбросить массив, я нахожу это немного лучше:
char flags[26][80];
std::fill( amp;flags[0][0], amp;flags[0][0] sizeof(flags) /* / sizeof(flags[0][0]) */, 0 );
fill
Выраженное в терминах размера массива позволит вам изменять размеры и сохранять fill
их нетронутыми. sizeof(flags[0][0])
1
Это в вашем случае ( sizeof(char)==1
) , но вы можете оставить его там на случай, если захотите изменить тип в любой момент.
В данном конкретном случае (массив флагов — интегральный тип) Я мог бы даже рассмотреть возможность использования memset
, даже если это наименее безопасная альтернатива (это приведет к сбою, если тип массива будет изменен на тип, отличный от pod):
memset( amp;flags[0][0], 0, sizeof(flags) );
Обратите внимание, что во всех трех случаях размеры массива вводятся только один раз, а остальные выводятся компилятором. Это немного безопаснее, поскольку оставляет меньше места для ошибок программиста (измените размер в одном месте, забудьте об этом в других).
РЕДАКТИРОВАТЬ: вы обновили код, и в нынешнем виде он не будет компилироваться, поскольку массив является закрытым, и вы пытаетесь инициализировать его извне. В зависимости от того, является ли ваш класс на самом деле агрегатом (и хотите сохранить его как таковой) или вы хотите добавить конструктор в класс, вы можете использовать разные подходы.
const std::size_t rows = 26;
const std::size_t cols = 80;
struct Aggregate {
char array[rows][cols];
};
class Constructor {
public:
Constructor() {
std::fill( amp;array[0][0], amp;array[rows][0], 0 ); // [1]
// memset( array, 0, sizeof(array) );
}
private:
char array[rows][cols];
};
int main() {
Aggregate a = {};
Constructor b;
}
Даже если array
предполагается, что он должен быть общедоступным, использование конструктора может быть лучшим подходом, поскольку это гарантирует, что array
он правильно инициализирован во всех экземплярах класса, в то время как внешняя инициализация зависит от пользовательского кода, не забывающего устанавливать начальные значения.
[1] Как упоминал @Oli Charlesworth в комментарии, использование констант — это другое решение проблемы необходимости указывать (и синхронизировать) размеры более чем в одном месте. Я использовал этот подход здесь с еще другой комбинацией: указатель на первый байт за пределами двумерного массива может быть получен путем запроса адреса первого столбца на одну строку за пределами двумерного массива. Я использовал этот подход только для того, чтобы показать, что это можно сделать, но он ничуть не лучше других, подобных amp;array[0][0] (rows*cols)
Комментарии:
1. 1. Однако альтернативой использованию
sizeof
является#define
(илиconst int
) измерения. Это имеет дополнительное преимущество в том, что оно будет работать, если вы передалиflags
его в качестве аргумента функции, так что он распадается на указатель, поэтомуsizeof
больше не будет давать правильный результат.2. Я не уверен, что смогу использовать инициализирующие фигурные скобки, потому что 2d-массив находится внутри класса. Я дам вам возможность обновить, прежде чем я соглашусь.
3. @DavidRodriguez: Можете ли вы объяснить, почему аналогичный синтаксис, т.е.
fill(amp;arr[0], amp;arr[0] sizeof(arr), 0)
Не работает для одномерного массива?4. @DhruvMullick:
sizeof
оператор выдает вам размер в байтах, а не количество элементов, еслиarr
содержит что-либо такое, чтоsizeof(*arr) != 1
указанное выше имеет неопределенное поведение и, вероятно, приведет к сбою (попробуйте получить доступ далеко за пределы конца массива). В противном случае (т.Е. Если массив сохраняется[unsigned|signed] char
, он должен работать5. На самом деле я не знал, что в синтаксисе для заполнения мы используем количество элементов.
Ответ №2:
Каков безопасный способ заполнения многомерного массива с помощью
std::fill
?
Простой инициализацией по умолчанию было бы использование привязанной инициализации.
char flags[26][80]{};
Приведенное выше действие инициализирует все элементы в символе flags
по умолчанию.
Заполнение двумерного массива с использованием std::fill
или std::fill_n
Однако для того, чтобы предоставить другое значение для инициализации, вышеуказанного недостаточно. Варианты std::fill
и std::fill_n
. (Предполагая, что массив flags
находится public
в вашем классе)
std::fill(
amp;a.flags[0][0],
amp;a.flags[0][0] sizeof(a.flags) / sizeof(a.flags[0][0]),
'0');
// or using `std::fill_n`
// std::fill_n(amp;a.flags[0][0], sizeof(a.flags) / sizeof(a.flags[0][0]), '1');
Чтобы обобщить это для любого 2d-массива любого типа с любым инициализирующим значением, я бы предложил шаблонную функцию следующим образом. Это также позволит избежать sizeof
вычисления общего количества элементов в массиве.
#include <algorithm> // std::fill_n, std::fill
#include <cstddef> // std::size_t
template<typename Type, std::size_t M, std::size_t N>
constexpr void fill_2D_array(Type(amp;arr2D)[M][N], const Type val = Type{}) noexcept
{
std::fill_n(amp;arr2D[0][0], M * N, val);
// or using std::fill
// std::fill(amp;arr2D[0][0], amp;arr2D[0][0] (M * N ), val);
}
Теперь вы можете инициализировать свой flags
подобный
fill_2D_array(a.flags, '0'); // flags should be `public` in your class!
(Смотрите Онлайн в прямом эфире)
трехмерное заполнение массива с использованием std::fill
или std::fill_n
Добавив еще один параметр размера, не относящийся к шаблону, к приведенной выше функции шаблона, это можно перенести и на 3d-массивы
#include <algorithm> // std::fill_n
#include <cstddef> // std::size_t
template<typename Type, std::size_t M, std::size_t N, std::size_t O>
constexpr void fill_3D_array(Type(amp;arr3D)[M][N][O], const Type val = Type{}) noexcept
{
std::fill_n(amp;arr3D[0][0][0], M * N * O, val);
}
Ответ №3:
это безопасно, двумерный массив представляет собой массив массивов. Поскольку массив занимал непрерывное хранилище, то и вся многомерная вещь тоже. Так что да, это нормально, безопасно и переносимо. Предполагая, что вы НЕ спрашиваете о стиле, который рассматривается в других ответах (поскольку вы используете флаги, я настоятельно рекомендую std::vector<std::bitset<80> > myFlags(26)
)
Комментарии:
1. Вы уверены, что набор битов подойдет? Я просто сохраняю либо ноль, либо единицу внутри каждого пространства в 2d-массиве. Эти флаги используются для отслеживания того, какие позиции на консоли были обновлены с помощью моей процедуры заполнения.
2. @Truncheon: вы храните 8 значений в символе, верно? И для того, чтобы получить, скажем, 7-е значение в 6-м символе, вы должны выполнить некоторый сдвиг / изменение бита и т.д. Набор битов сделает это за вас. То есть он будет хранить каждый флаг в бите, и вы можете устанавливать / снимать каждый флаг по его индексу, не беспокоясь о битовых шаблонах. Единственным недостатком является то, что размер набора битов должен быть константой времени компиляции. Если вы знакомы с boost, у них есть dynamic_bitset, который в значительной степени соответствует его названию.
3. Я считаю, что он хранит один бит в каждом байте. Это означает, что при использовании набора битов из 26 * 80 позиций и соответствующей
(row,col)->index
алгебры использование одногоbitset
будет более эффективным с точки зрения потребления памяти.4. @David: Это определенно будет более эффективно с точки зрения памяти, но читаемость пострадает, то есть ему придется написать btst[numCols*row col], который менее читаем по сравнению с v[row][col] . Решение зависит от OP и зависит от его приоритетов.
Ответ №4:
char flags[26][80];
std::fill((char*)flags, (char*)flags sizeof(flags)/sizeof(char), 0);
Ответ №5:
char[80]
Предполагается, что это замена реального строкового типа? В этом случае я рекомендую следующее:
std::vector<std::string> flags(26);
flags[0] = "hello";
flags[1] = "beautiful";
flags[2] = "world";
// ...
Или, если у вас есть компилятор C , который поддерживает списки инициализации, например, недавний компилятор g :
std::vector<std::string> flags { "hello", "beautiful", "world" /* ... */ };
Комментарии:
1. Это не пытается ответить на вопрос, просто дает совет, о котором никто не просил.