由于复制构造函数/删除数组导致内存损坏?

时间:2014-08-27 23:13:06

标签: c++ arrays struct memcpy

我有一个名为SFrame的结构,它包含许多元素,特别是2个无符号字符*的元素。我在我的类中创建了这个结构的成员变量,但是我在我的类中的函数中的每次迭代时都重新初始化它(除非某个布尔值为真)。我这样做的方式如下:

if (false == m_bRemainderNeedsProcessing)
{
    // ... calls before and after the initialization are unimportant and not shown
    m_sFrame = SFrame();
}

然后我将m_sFrame传递给一个函数来分配它的一些元素,然后我需要在我的struct中为我的pszMessage变量分配一个unsigned char数组。

            m_sFrame.iMessageSize = m_sFrame.iPayloadLength;
            m_sFrame.iOriginalMessageSize = m_sFrame.iPayloadLength;
            m_sFrame.pszMessage = new unsigned char[m_sFrame.iPayloadLength + Constants::RSSL_DECODE_PADDING];
            m_sFrame.pszOriginalMessage = new unsigned char[m_sFrame.iPayloadLength + Constants::RSSL_DECODE_PADDING];

这些SFrame实例存储在SFrame的矢量中,即

std::vector<SFrame>;

我希望能够为每次迭代重用m_sFrame,但我必须确保如果我要清除SFrame的内容,那么当我将它存储在向量中时,SFrame会被复制到向量中而不会丢失它是指定的值。为此,我为SFrame创建了一个复制构造函数:

我附上了部分SFrame复制构造函数的图像。

在我的函数结束时,我通过执行以下操作清除pszMessage中的内存(以及几乎相同的pszOriginalMessage):

ClearMemory(m_sFrame.pszMessage);

ClearMemory函数执行以下操作:

void CPCAPParser::ClearMemory(unsigned char *pszBuffer)
{
    if(pszBuffer != NULL)
    {
        delete [] pszBuffer;
    }
}

问题是,这个函数似乎删除了它应该做的更多....因为经过多次迭代后,我得到一个未处理的异常:访问违规......

我附上了一些可能有助于传达问题的图片。真的需要帮助:(如果有人需要我添加更多细节,请告诉我。

由于

http://imageshack.com/f/pduGDLGZp(常量:: RSSL_DECODE_PADDING的长度为7,因此总共有13个字节已经设置 - 在内存块的开头显而易见。)

http://imageshack.com/f/exRaaEmip - 我在哪里调用ClearMemory(内存地址显然仍然相同)。

我会发布更多图片,但我没有足够的代表...

SFrame:

struct SFrame
{
    int* ipTemp_int_ptr;
    int* ipTemp_int_ptr_actual;
    int* piTimestampPos;
    int* piOffset;
    int iIP_Header_Length;
    int iTCP_Header_Length;
    int iTCP_Source_Port;
    int iTCP_Dest_Port;
    long long uiSequenceNumber;
    long long uiInitialSequenceNumber;
    long long uiAckNumber;
    int iIp_total_length;
    int iActual_frame_length;
    int iOriginal_frame_length;
    int iCaptured_frame_length;
    int iTotalPayloadLength;
    int iTotalMsgLoad;
    int iPayloadLength;
    int iBytesComplete;
    int iFragmentID;
    int iRemainder;
    int iMessageSize;
    int iOriginalMessageSize;
    long long iNextExpectedSequenceNum;
    std::string strSourceAddress;
    std::string strDestAddress;
    std::string strTimestamp;
    unsigned char* pszMessage;
    unsigned char* pszOriginalMessage;
    unsigned int uiClientID;
    int iStartOfRemainder;
    int iAccumulatedMsgLength;

    SFrame() : ipTemp_int_ptr ( NULL ),
        ipTemp_int_ptr_actual ( NULL ),
        piTimestampPos ( NULL ),
        piOffset ( NULL ),
        pszMessage ( NULL ),
        pszOriginalMessage ( NULL ),
        iIP_Header_Length( 0 ),
        iTCP_Header_Length ( 0 ),
        iTCP_Source_Port ( 0 ),
        iTCP_Dest_Port ( 0 ),
        iIp_total_length ( 0 ),
        iActual_frame_length ( 0 ),
        iOriginal_frame_length ( 0 ),
        iCaptured_frame_length ( 0 ),
        uiSequenceNumber( 0 ),
        uiInitialSequenceNumber ( 0 ),
        uiAckNumber( 0 ),
        iPayloadLength ( 0 ),
        iNextExpectedSequenceNum ( 0 ),
        uiClientID ( 0 ),
        iMessageSize ( 0 ),
        iOriginalMessageSize ( 0 ),
        iFragmentID( 0 ),
        iTotalPayloadLength( 0 ),
        iBytesComplete( 0 ),
        iAccumulatedMsgLength ( 0 ),
        iRemainder ( 0 ),
        iStartOfRemainder( 0 ),
        iTotalMsgLoad ( 0 )
    {
    }

    SFrame(const SFrame &c_rSrc)
    {
        *this = c_rSrc;
    }

    SFrame &SFrame::operator=(const SFrame &c_rSrc)
    {
        iIP_Header_Length = c_rSrc.iIP_Header_Length;
        iTCP_Header_Length = c_rSrc.iTCP_Header_Length;
        iTCP_Source_Port = c_rSrc.iTCP_Source_Port;
        iTCP_Dest_Port = c_rSrc.iTCP_Dest_Port;
        iIp_total_length = c_rSrc.iIp_total_length;
        iActual_frame_length = c_rSrc.iActual_frame_length;
        iOriginal_frame_length = c_rSrc.iOriginal_frame_length;
        iCaptured_frame_length = c_rSrc.iCaptured_frame_length;
        iPayloadLength = c_rSrc.iPayloadLength;
        uiSequenceNumber = c_rSrc.uiSequenceNumber;
        uiInitialSequenceNumber = c_rSrc.uiInitialSequenceNumber;
        uiAckNumber = c_rSrc.uiAckNumber;
        iNextExpectedSequenceNum = c_rSrc.iNextExpectedSequenceNum;
        uiClientID = c_rSrc.uiClientID;
        iFragmentID = c_rSrc.iFragmentID;
        iMessageSize = c_rSrc.iMessageSize;
        iOriginalMessageSize = c_rSrc.iOriginalMessageSize;
        iTotalPayloadLength = c_rSrc.iTotalPayloadLength;
        iBytesComplete = c_rSrc.iBytesComplete;
        iAccumulatedMsgLength = c_rSrc.iAccumulatedMsgLength;
        iRemainder = c_rSrc.iRemainder;
        iStartOfRemainder = c_rSrc.iStartOfRemainder;
        iTotalMsgLoad = c_rSrc.iTotalMsgLoad;

        strSourceAddress = c_rSrc.strSourceAddress;
        strDestAddress = c_rSrc.strDestAddress;
        strTimestamp = c_rSrc.strTimestamp;

        pszMessage = (c_rSrc.pszMessage == NULL) ? NULL : new unsigned char[c_rSrc.iMessageSize];
        pszOriginalMessage = (c_rSrc.pszOriginalMessage == NULL) ? NULL : new unsigned char[c_rSrc.iOriginalMessageSize];

        if(pszMessage != NULL)
        {
            memcpy(pszMessage, c_rSrc.pszMessage, c_rSrc.iMessageSize);
        }


        if(pszOriginalMessage != NULL)
        {
            memcpy(pszOriginalMessage, c_rSrc.pszOriginalMessage, c_rSrc.iOriginalMessageSize);
        }

        return *this;
    }

    ~SFrame()
    {
        delete [] pszMessage;
        delete [] pszOriginalMessage;
    }
};

1 个答案:

答案 0 :(得分:0)

您的问题是您的SFrame结构不安全,但是您将这个实例放在std::vector中进行复制。

或者:

  1. 工作用户定义的复制构造函数和赋值运算符添加到SFrame结构或
  2. 将指针成员替换为std::vector
  3. 现在你的结构中有很多成员。如果您在复制构造函数中只丢失了一个,或者您对已分配内存的处理有问题,那么您将获得一个损坏的副本。由于vector<SFrame>会创建副本,因此使用vector<SFrame>打印副本是不可能的。

    所以不是这样,这是使用选项2的修复:

    #include <vector>
    struct SFrame
    {
        std::vector<int> ipTemp_int_ptr;
        std::vector<int> ipTemp_int_ptr_actual;
        std::vector<int> piTimestampPos;
        std::vector<int> piOffset;
        int iIP_Header_Length;
        int iTCP_Header_Length;
        int iTCP_Source_Port;
        int iTCP_Dest_Port;
        long long uiSequenceNumber;
        long long uiInitialSequenceNumber;
        long long uiAckNumber;
        int iIp_total_length;
        int iActual_frame_length;
        int iOriginal_frame_length;
        int iCaptured_frame_length;
        int iTotalPayloadLength;
        int iTotalMsgLoad;
        int iPayloadLength;
        int iBytesComplete;
        int iFragmentID;
        int iRemainder;
        int iMessageSize;
        int iOriginalMessageSize;
        long long iNextExpectedSequenceNum;
        std::string strSourceAddress;
        std::string strDestAddress;
        std::string strTimestamp;
        std::vector<unsigned char> pszMessage;
        std::vector<unsigned char> pszOriginalMessage;
        unsigned int uiClientID;
        int iStartOfRemainder;
        int iAccumulatedMsgLength;
    
        SFrame() : 
            iIP_Header_Length( 0 ),
            iTCP_Header_Length ( 0 ),
            iTCP_Source_Port ( 0 ),
            iTCP_Dest_Port ( 0 ),
            iIp_total_length ( 0 ),
            iActual_frame_length ( 0 ),
            iOriginal_frame_length ( 0 ),
            iCaptured_frame_length ( 0 ),
            uiSequenceNumber( 0 ),
            uiInitialSequenceNumber ( 0 ),
            uiAckNumber( 0 ),
            iPayloadLength ( 0 ),
            iNextExpectedSequenceNum ( 0 ),
            uiClientID ( 0 ),
            iMessageSize ( 0 ),
            iOriginalMessageSize ( 0 ),
            iFragmentID( 0 ),
            iTotalPayloadLength( 0 ),
            iBytesComplete( 0 ),
            iAccumulatedMsgLength ( 0 ),
            iRemainder ( 0 ),
            iStartOfRemainder( 0 ),
            iTotalMsgLoad ( 0 )
        {
        }
    };
    

    请注意,复制构造函数和赋值运算符(以及析构函数)现在已经消失,从而使代码更容易处理,并且在复制期间没有机会丢失任何成员。相反,我们让编译器生成副本,编译器将始终让每个成员都被复制。

    现在,您的代码库必须重新编译,并且您将不可避免地遇到编译器错误。但是,这些错误通常很容易修复。大多数人可能会要求你

    1. 删除delete [] somepointer;的行,其中somePointer现在是向量和
    2. 如果将指针传递给缓冲区的开头,则会传递&vector[0]vector.data(),因为向量基本上是new[]/delete[]的包装。
    3. 回到原始代码,您的赋值运算符的一个问题是您无法删除先前分配的内存,因此您有内存泄漏。另外,考虑到编写复制操作的方式,您没有检查自我分配。但是,这可能不是唯一的错误,因为我们没有看到您如何使用这些SFrame个实例。

      因此,最好更改为矢量,修复编译器错误,重建和测试应用程序。