#c #c 11 #vector #struct
#c #c 11 #вектор #структура
Вопрос:
Это кажется чем-то, что должно быть простым и понятным, но Google выдает очень мало.
Какой чистый, современный (C 11) способ инициализации простой структуры заголовка файла, например, следующий
typedef struct FooHeader {
uint8_t FooCount;
uint8_t BarCount;
uint32_t BazOffsets[4];
} FooHeader;
с данными, содержащимися в std::vector<unsigned char>
? Является ли хорошей идеей создать вложенный вектор и преобразовать его данные в тип структуры заголовка или?
Комментарии:
1. Предполагаете ли вы, что ваше использование может безопасно игнорировать потенциальные проблемы с конечностью платформы и упаковкой структуры и просто предполагать, что все читатели и авторы используют одну и ту же концепцию для обоих ? Это, честно говоря, то, что делает такую деятельность утомительной.
2. Да, это так, поскольку формат файла уже устоялся, и все приложения, работающие с ним до сих пор, договорились о том, как его следует читать и записывать. Также крайне маловероятно, что это программное обеспечение будет использоваться на какой-либо платформе, отличной от x86.
3. С какой целью вы хотите создать подвектор? Кроме того, есть ли у вас в вашей программе одна переменная FooHeader или std::vector<FooHeader>, которую вы хотите заполнить с помощью std::vector<unsigned char> ?
4. Если мне не нужно создавать подвектор, я не буду. Это была просто мысль. В этом случае FooHeader будет локальной переменной.
5. Как насчет
FooHeader h = *(reinterpret_cast<FooHeader*>(amp;data_vec[0]))
?
Ответ №1:
Чтобы избежать проблем с упаковкой, выравниванием и порядком следования, лучше всего читать данные на байтовом уровне (почти на всех современных аппаратных средствах вы можете использовать 8-битные байты, но упаковка часто меняется между компиляторами (или даже просто между разными флагами компиляции), и как большие, так и маленькие конечные компьютерывсе еще распространено).
Это означает, что лучше всего сделать что-то вроде:
FooHeader load_FooHeader(std::vector<unsigned char> const amp;dat) {
static_assert(
std::numeric_limits<unsigned char>::digits == 8,
"Assumes 8-bit bytes");
FooHeader retv;
retv.FooCount = dat[0];
retv.BarCount = dat[1];
//Start at the fifth byte, to allow for padding.
//If you want to use a packed format, use index = 2;
std::size_t index{4};
for (std::size_t i{0}, iend{4}; i < iend; i) {
retv.BarOffsets[i] = 0;
//Adjust ordering depending on desired endianness.
//Currently uses little endian.
for (std::size_t j{0}, jend{4}; j < jend; j) {
retv.BarOffsets[i] |= dat[index i*4 (3-j)] << (j*8);
}
}
return retv;
}