memcpy没有复制到缓冲区

时间:2015-01-05 02:02:10

标签: c++ vector memcpy unsigned-char

我有一个类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的标题应该写得很好。

有谁能说明我为什么会遇到这个问题?

谢谢!

3 个答案:

答案 0 :(得分:3)

您保留的元素不适合正常使用。仅在调整矢量大小时才会创建元素。虽然可能以某种方式看起来是有效的,但对于具有构造函数的类型,情况会有所不同 - 您可以看到构造函数未被调用。这是未定义的行为 - 您正在访问在这种情况下不允许的元素。

.reserve()操作通常与.push_back()一起使用以避免重新分配,但这不是这种情况。

如果您使用.size(),则不会修改.reserve()。您应该使用.resize()代替。


或者,您可以将复制操作与.push_back().reserve()一起使用,但是您需要放弃使用memcpy,而是将std::copystd::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属性。您已经集成了addFromaddBytesFrom方法,这些方法在内部使用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);