Недавно я пытался обновить некоторый код, чтобы использовать стандартные библиотечные функции C ++, а не старые функции стиля C. В частности, я попытался сделать следующее (искусственный рабочий пример для простоты — я знаю, что код некрасив, но он иллюстрирует проблему кратко):
std::vector<int> vData;
vData.push_back(10990);
vData.push_back(11990);
vData.push_back(12990);
vData.push_back(13990);
unsigned char szBuffer[100];
memset(szBuffer,0,sizeof(szBuffer));
std::copy(vData.begin(),vData.end(),szBuffer);
Я ожидал, что это будет вести себя аналогично коду, который я пытаюсь заменить:
memcpy(szBuffer,&vData[0],sizeof(int)*vData.size());
но отладка кода, ясно, что std::copy
код, который я написал, записывает только первые 4 байта unsigned char
буфер вместо полной битовой комбинации из 4 целых чисел в векторе. Может кто-нибудь сказать мне, что я сделал неправильно, или это просто, что я не могу использовать std::copy
таким образом и должен придерживаться memcpy
?
Придерживаться memcpy
, std::copy
быть умным, он понимает вовлеченные типы и правильно преобразовывает int
в unsigned char
используя стандартные преобразования. memcpy
невежественен, это то, что вы хотите.
Я ожидал, что это будет вести себя аналогично коду, который я пытаюсь заменить …
Тот std::copy
как написано не может вести себя так же, как std::memcpy
или же std::memmove
потому что несоответствие типов между элементами std::vector<int>
по сравнению с элементами unsigned char szBuffer[100]
, Один из способов преодолеть это несоответствие типов — это szBuffer
для int*
:
std::copy(vData.begin(),vData.end(),reinterpret_cast<int*>(szBuffer));
Тот reinterpret_cast
это вопрос личных предпочтений. Я бы предпочел увидеть что-то, что кричит «Опасность, опасность, Уилл Робинсон!» для чего-то, что может вызывать неопределенное поведение над приведением в стиле C, которое скрывает, но не удаляет потенциал для UB. Я (и мои руководители проекта) могу grep для reinterpret_cast
,
Потенциал для UB здесь реален, так как нет никакой гарантии, что этот бросок действителен из-за проблем с выравниванием.
Обратите внимание, что нет никакой гарантии, что std::copy
когда-либо будет реализован через memcpy
или же memmove
, В стандарте нет ни одного слова (2003 или 2011), которое бы гласило, что std::copy
должен быть реализован через memcpy
или же memmove
если возможно. (В сторону: в каждой реализации, которую я видел, std::copy
будет осуществляться через std::memmove
если это будет работать «как если бы» была применена наивная реализация.)
Единственная причина, чтобы перейти от std::memcpy
в std::copy
вот эстетика. Иногда эстетика мешает. «Глупая последовательность — это хобгоблин маленьких умов». Я рекомендую придерживаться std::memcpy
, Он делает именно то, что вы хотите, и это использование безопасно, потому что нет перекрытия и потому что буфер имеет правильный размер.
потому что стандартом является то, что поведение (если не точная реализация)
std::copy
эквивалентно:
namespace std {
template< typename InIter, typename OutIter >
OutIter std::copy( InIter begin, InIter end, OutIter outp )
{
for( ; begin != end; ++begin, ++outp )
{
*outp = *begin;
}
return outp;
}
}
это означает, что он копирует элемент за элементом, увеличивая каждый итератор и возвращает следующую позицию записи в выводе.
Это не то же самое поведение, что и у memcpy, что вам и нужно. Нет ничего плохого в использовании memcpy (даже если некий компилятор Microsoft скажет вам, что это небезопасно, что может быть, но то же самое относится к управлению грузовиком, если вы не ведете его должным образом, это не значит, что никто никогда не сможет управлять им) ,
Чтобы интерпретировать содержимое вектора как необработанную память, используйте reinterpret_cast
в unsigned char *
:
std::copy(reinterpret_cast<unsigned char *>(&*vData.begin()),
reinterpret_cast<unsigned char *>(&*vData.end()), szBuffer);
Вы должны косвенно и взять адрес начала и конца элементов, потому что это не гарантирует, что vector::iterator
тип указателя
Это одно из немногих гарантированных безопасных применений reinterpret_cast
,