В C ++ 11 как лучше всего выполнить следующий код:

// definitions
vector<char> rcvbuf; 

 struct somethingParams
{
  char Magic[4]; 
  int Version; 
  int MsgID; 
  int MsgLen;
};

Код:

struct somethingParams mParams; 
memcpy(&mParams, rcvbuf.data(), sizeof(somethingParams)); 
rcvbuf.erase(rcvbuf.begin(), rcvbuf.begin + sizeof(somethingParams));

Из того, что я читал, memcpy не рекомендуется в C ++. Я так понимаю, что это "С-стиль". Есть ли лучший способ достичь того, что я делаю выше? (т.е. копирование данных из вектора в структуру.)

Просто посмотрю, смогу ли я сделать его более "C ++".

Спасибо.

0
lppier 29 Окт 2015 в 04:14

2 ответа

Лучший ответ

memcpy кошерный в C ++. Конечно, его не следует использовать для создания копии массива (для этого у нас есть std::copy, а также векторы и другие забавные вещи), но случай инициализации структуры из двоичных данных по своей сути небезопасен для типов, поэтому C ++ не дает здесь ничего, чего нет в C.

Однако вы должны помнить об использовании memcpy только с типами, которые можно легко копировать. Вы можете использовать static_assert, чтобы убедиться, что вы не нарушаете это правило и не запускаете неопределенное поведение.

static_assert(std::is_trivially_copyable<somethingParams>::value,
              "somethingParams must be trivially copyable");
5
Brian 29 Окт 2015 в 01:24

Ваш код сбивает с толку. У вас есть vector из char (длины, скажем, n) с одной стороны.

И у вас есть структура с массивом char длиной 4 и тремя int сек.

На самом деле я не вижу, что слепой memcpy принесет разумный результат.

Но, возможно, я смогу вызвать некоторые мысли, которые помогут вам копать дальше:

  1. Элемент vector из char может быть заменен объектом string? Объект string предназначен для внутреннего хранения массива char. Я считаю, что это больше C ++, но это зависит от ваших требований.
  2. memcpy просто копирует память. Я не уверен, что вы этого хотите; Я считаю, что структура после копии содержит мусор. На мой взгляд, непредсказуемо.
  3. Для копирования из контейнера (vector - это контейнер STL) стандартный способ сделать это в C ++ (помимо вышеупомянутого std::copy function) выполняется путем перебора элементов и их копирования. Смотри ниже.

Наиболее знакомы с итераторами:

for (vector<char>::iterator it = rcvbuf.begin(); it != rcvbuf.end(); it++) {
    //copy element *it into something
}

C ++ 11 представил автоматический диапазон для циклов:

for (auto elem : rcvbuf) {
    //copy element elem into something
}

Дальнейшая разработка:

Вектор содержит 16 элементов. Четвертый элемент - это нулевой символ, имитирующий конец строки в стиле C. Без нулевого символа массив char тоже будет содержать мусор.

Я удалил erase для простоты. Он просто стирает элементы в указанном диапазоне (итераторами); в вашем случае он просто удаляет sizeof(somethingParams) байтов / char из вектора, при условии, что он содержит достаточно элементов, в противном случае вероятна ошибка сегментации.

Посмотрите на результат ниже, это полная чушь.

char t[] = {'a', 'b', 'c', '\0', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o'};
vector<char> rcvbuf(t, t + 16);

struct somethingParams {
    char Magic[4] = {'w', 'x', 'y', 'z'};
    int Version{99};
    int MsgID{99};
    int MsgLen{99};
} mParams;


memcpy(&mParams, rcvbuf.data(), sizeof(somethingParams));
//rcvbuf.erase(rcvbuf.begin(), rcvbuf.begin() + sizeof (somethingParams));

cout << "Magic:   " << mParams.Magic << endl;
cout << "Version: " << mParams.Version << endl;
cout << "MsgID:   " << mParams.MsgID << endl;
cout << "MsgLen:  " << mParams.MsgLen << " bytes" << endl;

Результат (int на моей машине занимает 4 байта):

Magic:   abc
Version: 1734763876
MsgID:   1802135912
MsgLen:  1869507948 bytes
1
Ely 29 Окт 2015 в 08:56