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

Posted

技术标签:

【中文标题】由于复制构造函数/删除数组导致内存损坏?【英文标题】:Memory Corruption Due to Copy Constructor/Deleting Array? 【发布时间】:2014-08-27 23:13:06 【问题描述】:

我有一个名为 SFrame 的结构,其中包含许多元素,特别是 2 个类型为 unsigned char* 的元素。我在我的类中创建了这个结构的一个成员变量,但是我在我的类中的一个函数的每次迭代中重新初始化它(除了某个布尔值为真时)。我通过以下方式执行此操作:

if (false == m_bRemainderNeedsProcessing)

    // ... calls before and after the initialization are unimportant and not shown
    m_sFrame = SFrame();

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

            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(Constants::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;
    
;

【问题讨论】:

为什么是那些成员指针而不是std::vector&lt;unsigned char&gt; imageshack.com/f/idjwk2pEp 保罗有哪些成员?... imageshack.com/f/exSaUdG3p 在调用ClearMemory后是否清除pszMessage指针以避免释放两次?否则我能想到的就是你在某处有缓冲区溢出。 delete[] 没有损坏。 【参考方案1】:

您的问题是您的 SFrame 结构不是安全可分配的,但是您将其实例放置在将复制的 std::vector 中。

要么:

    工作用户定义的复制构造函数和赋值运算符添加到您的SFrame 结构或 将指针成员替换为std::vector

现在你的结构中有很多成员。如果您在复制构造函数中只漏掉了一个,或者您对分配的内存的处理有问题,那么您将得到一个损坏的副本。由于vector&lt;SFrame&gt; 会创建副本,因此使用vector&lt;SFrame&gt; 时不能使用损坏的副本。

所以不是这个,这里是使用选项 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 )
    
    
;

请注意,复制构造函数和赋值操作符(和析构函数)现在已经不存在了,从而使代码更容易处理,并且在复制过程中不会丢失任何成员。相反,我们让编译器生成副本,编译器将总是复制每个成员。

现在,您必须重新编译使用该结构的代码库,您将不可避免地遇到编译器错误。然而,这些错误通常很容易修复。大多数可能会要求您

    删除带有delete [] somepointer; 的行,其中somePointer 现在是一个向量,并且 如果传递指向缓冲区开头的指针,则传递&amp;vector[0]vector.data(),因为向量基本上是new[]/delete[] 的包装器。

回到您的原始代码,您的赋值运算符的一个问题是您未能删除先前分配的内存,因此您有内存泄漏。此外,鉴于您编写复制操作的方式,您没有检查自分配。但是,这可能不是唯一的错误,因为我们没有看到您如何使用这些 SFrame 实例。

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

【讨论】:

如果我想在向量的某个位置添加一些数据怎么办?我应该手动跟踪大小对吗?数据块中会有空值,但需要整个块的大小。 @PatriciaAydin 使用vector::insert 但是确定大小? myvector.size() 是否会返回块的大小,即使它有空终止字符?或者是矢量如何确定大小。如果是这样,我会像我已经做的那样保持手动尺寸 @PatriciaAydin - 向量通过使用 size() 成员知道自己的大小。无需标记变量来表示大小。

以上是关于由于复制构造函数/删除数组导致内存损坏?的主要内容,如果未能解决你的问题,请参考以下文章

c++的复制构造函数

解决由于具有可能已删除的默认构造函数的不变成员而导致的编译器错误

基本线程导致 malloc():内存损坏:[关闭]

为什么没有复制构造函数会导致值一起更改?

cpp►动态内存分配与析构之复制构造函数/赋值运算符

删除复制构造函数会破坏继承的构造函数