由于复制构造函数/删除数组导致内存损坏?
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<unsigned char>
?
imageshack.com/f/idjwk2pEp
保罗有哪些成员?...
imageshack.com/f/exSaUdG3p
在调用ClearMemory
后是否清除pszMessage
指针以避免释放两次?否则我能想到的就是你在某处有缓冲区溢出。 delete[]
没有损坏。
【参考方案1】:
您的问题是您的 SFrame
结构不是安全可分配的,但是您将其实例放置在将复制的 std::vector
中。
要么:
-
将工作用户定义的复制构造函数和赋值运算符添加到您的
SFrame
结构或
将指针成员替换为std::vector
。
现在你的结构中有很多成员。如果您在复制构造函数中只漏掉了一个,或者您对分配的内存的处理有问题,那么您将得到一个损坏的副本。由于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 )
;
请注意,复制构造函数和赋值操作符(和析构函数)现在已经不存在了,从而使代码更容易处理,并且在复制过程中不会丢失任何成员。相反,我们让编译器生成副本,编译器将总是复制每个成员。
现在,您必须重新编译使用该结构的代码库,您将不可避免地遇到编译器错误。然而,这些错误通常很容易修复。大多数可能会要求您
-
删除带有
delete [] somepointer;
的行,其中somePointer
现在是一个向量,并且
如果传递指向缓冲区开头的指针,则传递&vector[0]
或vector.data()
,因为向量基本上是new[]/delete[]
的包装器。
回到您的原始代码,您的赋值运算符的一个问题是您未能删除先前分配的内存,因此您有内存泄漏。此外,鉴于您编写复制操作的方式,您没有检查自分配。但是,这可能不是唯一的错误,因为我们没有看到您如何使用这些 SFrame
实例。
因此,最好更改为矢量,修复编译器错误,重新构建和测试您的应用程序。
【讨论】:
如果我想在向量的某个位置添加一些数据怎么办?我应该手动跟踪大小对吗?数据块中会有空值,但需要整个块的大小。 @PatriciaAydin 使用vector::insert
。
但是确定大小? myvector.size() 是否会返回块的大小,即使它有空终止字符?或者是矢量如何确定大小。如果是这样,我会像我已经做的那样保持手动尺寸
@PatriciaAydin - 向量通过使用 size()
成员知道自己的大小。无需标记变量来表示大小。以上是关于由于复制构造函数/删除数组导致内存损坏?的主要内容,如果未能解决你的问题,请参考以下文章