我有一个类std::vector<unsigned char> mPacket
作为数据包缓冲区(用于发送UDP字符串)。有一个相应的成员变量mPacketNumber
可以跟踪到目前为止已经发送了多少个数据包。
我在课堂上做的第一件事就是预留空间:
mPacket.reserve(400);
然后,在我想要发送数据包的情况下运行的循环中:
mPacket.clear(); //empty out the vector
long packetLength = 0; //keep track of packetLength for sending udp strings
memcpy(&mPacket[0], &&mPacketNumber, 4); //4 bytes because it's a long
packetLength += 4; //add 4 bytes to the packet length
memcpy(&mPacket[packetLength], &data, dataLength);
packetLength += dataLength;
udp.send(mPacket.data(), packetLength);
除了我意识到什么都没发送!多么奇怪。
所以我挖得更深一些,发现mPacket.size()
返回零,而packetLength
返回我认为数据包的大小。
我想不出mPacket
长度为零的原因 - 即使我处理错误的数据,mPacketNumber
的标题应该写得很好。
有谁能说明我为什么会遇到这个问题?
谢谢!
答案 0 :(得分:3)
您保留的元素不适合正常使用。仅在调整矢量大小时才会创建元素。虽然可能以某种方式看起来是有效的,但对于具有构造函数的类型,情况会有所不同 - 您可以看到构造函数未被调用。这是未定义的行为 - 您正在访问在这种情况下不允许的元素。
.reserve()
操作通常与.push_back()
一起使用以避免重新分配,但这不是这种情况。
如果您使用.size()
,则不会修改.reserve()
。您应该使用.resize()
代替。
或者,您可以将复制操作与.push_back()
和.reserve()
一起使用,但是您需要放弃使用memcpy
,而是将std::copy
与std::back_inserter
一起使用.push_back()
,使用std::copy(reinterpret_cast<unsigned char*>(&mPacketNumber), reinterpret_cast<unsigned char*>(&mPacketNumber) + sizeof(mPacketNumber), std::back_inserter(mPacket))
std::copy(reinterpret_cast<unsigned char*>(&data), reinterpret_cast<unsigned char*>(&data) + dataLength, std::back_inserter(mPacket));
将元素推送到另一个容器:
reinterpret_cast
这些{{1}}令人烦恼,但代码仍有一个优点 - 如果您的估算值太低,您将无法获得缓冲区溢出。
答案 1 :(得分:2)
vector
,当您致电size()
时,不会计算元素。在向量内部有一个计数器变量,用于保存该信息,因为向量分配了大量内存,并且无法确切知道数据的结尾位置。当您使用vector
对象的方法添加/删除元素时,它会更改计数器变量,因为它们已编程为这样做。
您将数据直接添加到其数组指针中,这不会唤醒您的矢量对象的任何反应,因为它不使用任何方法。数据存在,但是矢量没有确认,因此计数器保持为0,size()
返回0.
您应该用size()
替换所有packageLength
次调用,或者使用向量中的方法添加/删除/读取数据,或使用动态分配的数组而不是向量,或创建自己的包含数组的类,并按照您喜欢的方式管理它。说实话,在这种情况下使用vector
并不是真的有意义。
Vector是一个传统的高级面向对象组件,在大多数情况下,它应该以这种方式使用。
自己的Array类示例:
如果您使用自己动态分配的数组,则必须始终记住其长度才能使用它。所以让我们创建一个类,这将减少我们的一些松懈。此示例具有基于memcpy
的元素转移,[]
符号完美无缺。它具有原始的最大长度,但在必要时会自行扩展。
此外,这是一个内联课程。某些IDE可能会要求您在头文件和源文件中实际分隔它,因此您可能必须自己执行此操作。
如有必要,请自行添加更多方法。应用此功能时,不要使用memcpy ,除非您要手动更改arraySize属性。您已经集成了addFrom
和addBytesFrom
方法,这些方法在内部使用memcpy(假设调用数组是目标),单独增加arraySize
。如果您确实想使用memcpy,可以使用setSize
方法强制新数组大小而不修改数组。
#include <cstring>
//this way you can easily change types during coding in case you change your mind
//more conventional object-oriented method would use templates and generic programming, but lets not complicate too much now
typedef unsigned char type;
class Array {
private:
type *array;
long arraySize;
long allocAmount; //number of allocated bytes
long currentMaxSize; //number of allocated elements
//private call that extends memory taken by the array
bool reallocMore()
{
//preserve old data
type *temp = new type[currentMaxSize];
memcpy(temp, array, allocAmount);
long oldAmount = allocAmount;
//calculate new max size and number of allocation bytes
currentMaxSize *= 16;
allocAmount = currentMaxSize * sizeof(type);
//reallocate array and copy its elements back into it
delete[] array;
array = new type[currentMaxSize];
memcpy(array, temp, oldAmount);
//we no longer need temp to take space in out heap
delete[] temp;
//check if space was successfully allocated
if(array) return true;
else return false;
}
public:
//constructor
Array(bool huge)
{
if(huge) currentMaxSize = 1024 * 1024;
else currentMaxSize = 1024;
allocAmount = currentMaxSize * sizeof(type);
array = new type[currentMaxSize];
arraySize = 0;
}
//copy elements from another array and add to this one, updating arraySize
bool addFrom(void *src, long howMany)
{
//predict new array size and extend if larger than currentMaxSize
long newSize = howMany + arraySize;
while(true)
{
if(newSize > currentMaxSize)
{
bool result = reallocMore();
if(!result) return false;
}
else break;
}
//add new elements
memcpy(&array[arraySize], src, howMany * sizeof(type));
arraySize = newSize;
return true;
}
//copy BYTES from another array and add to this one, updating arraySize
bool addBytesFrom(void *src, long byteNumber)
{
//predict new array size and extend if larger than currentMaxSize
int typeSize = sizeof(type);
long howMany = byteNumber / typeSize;
if(byteNumber % typeSize != 0) howMany++;
long newSize = howMany + arraySize;
while(true)
{
if(newSize > currentMaxSize)
{
bool result = reallocMore();
if(!result) return false;
}
else break;
}
//add new elements
memcpy(&array[arraySize], src, byteNumber);
arraySize = newSize;
return true;
}
//clear the array as if it's just been made
bool clear(bool huge)
{
//huge >>> 1MB, not huge >>> 1KB
if(huge) currentMaxSize = 1024 * 1024;
else currentMaxSize = 1024;
allocAmount = currentMaxSize * sizeof(type);
delete[] array;
array = new type[currentMaxSize];
arraySize = 0;
}
//if you modify this array out of class, you must manually set the correct size
bool setSize(long newSize) {
while(true)
{
if(newSize > currentMaxSize)
{
bool result = reallocMore();
if(!result) return false;
}
else break;
}
arraySize = newSize;
}
//current number of elements
long size() {
return arraySize;
}
//current number of elements
long sizeInBytes() {
return arraySize * sizeof(type);
}
//this enables the usage of [] as in yourArray[i]
type& operator[](long i)
{
return array[i];
}
};
答案 2 :(得分:0)
mPacket.reserve();
mPacket.resize(4 + dataLength); //call this first and copy into, you can get what you want
mPacket.clear(); //empty out the vector
long packetLength = 0; //keep track of packetLength for sending udp strings
memcpy(&mPacket[0], &&mPacketNumber, 4); //4 bytes because it's a long
packetLength += 4; //add 4 bytes to the packet length
memcpy(&mPacket[packetLength], &data, dataLength);
packetLength += dataLength;
udp.send(mPacket, packetLength);