2011-12-09 149 views
0

我用C++编写下面的代码来提取Piece Table数据结构中给定范围的文本。下面是类PieceTable的存储文本的给定范围中的字符数组buffer在功能:可能的缓冲区溢出问题

void PieceTable::getTextInRange(unsigned __int64 startPos, unsigned __int64 endPos, char buffer[]){ 

    char* totalBuffer = new char[getSize() + 2]; 

    getBuffer(totalBuffer); 

    if(endPos >= getSize()) 
     endPos = getSize() - 1; 

    cout<<"startPos : "<<startPos<<endl; 
    cout<<"endPos : "<<endPos<<endl; 

    memcpy(buffer, &totalBuffer[startPos], endPos - startPos + 1); 

    buffer[endPos - startPos + 2] = '\0'; 

    if(totalBuffer != 0) 
     delete[] totalBuffer; 
    totalBuffer = 0; 
} 

此处在主方法的代码段,其我使用来测试该代码:

temp2 = new char[end - start + 2]; //changing 2 to 3 solves the problem 
pieceTable.getTextInRange(Start, end, temp2); 
for(int i = 0; i< end - start + 1; i++) 
    cout<<temp2[i]; 
cout<<endl; 

if(temp2 != 0) 
{ 
    delete[] temp2; //this line causes the heap corruption error 
    temp2 = 0; 
} 

宣言temp2char* temp2;

每当程序遇到delete[] temp2说法,有一个堆损坏错误。如果我为temp2分配内存,则不会发生此问题:
temp2 = new char[end - start + 3] 因此,基本上更改长度可解决问题。我知道我在某处弄乱了这些长度,但我无法弄清楚在哪里。

编辑: 的getSize():

__int64 PieceTable::getSize() 
{ 
    return dList.getLength(dList.getBack()); 
} 

我使用了一块表数据结构。这是它,在本文中:http://www.cs.unm.edu/~crowley/papers/sds.pdf

我可能是错的,但我不认为有任何问题getSize() ,因为我用来检索整个缓冲区的长度的函数getBuffer的工作方式如代码所示。

+1

打印'getSize()+ 2'的返回值以及'endPos - startPos + 2'的值,以确保前者大于后者。 –

+0

实际上,'getSize()+ 2'用于变量'totalBuffer',它是函数'getTextInRange'中的局部变量。 – devjeetroy

+1

你最初的分配是什么? – Vlad

回答

7

PieceTable::getTextInRange,你有这样一行:

buffer[endPos - startPos + 2] = '\0'; 

,当你分配你传递的东西为buffer你分配这样的:

temp2 = new char[end - start + 2]; 

把一些真正的数字让我们。 ...

buffer[5 - 2 + 2] = '\0'; 

temp2 = new char[5 - 2 + 2]; 

这相当于:

buffer[5] = '\0'; 

temp2 = new char[5]; 

嗯,这是你的问题。如果你这样做new char [5]你会得到一个有效索引从0到4的数组。5不是这个数组的有效索引。

可能我建议你让它成为一个规则,你只能在绝大多数情况下突破,你总是用STL的方式指定范围[begin,end]。这意味着你指定一个过去的最后期望的索引结束。这使得范围计算数学更不容易出错。此外,界面与STL工作方式的一致性使其更易于使用。例如,使用此方案计算范围的大小总是为end - begin。有一个old (circa 1982) paper by E.W. Dijkstra that gives some good reasons why this scheme for expressing ranges is the best one

+1

哦,是啊!谢谢!我知道我错过了一些非常微不足道的东西!谢谢!是的,我会记住你的建议 – devjeetroy

+0

@devjeetroy:实际上有一个颇为知名且相当古老的论文,详细介绍了表示范围的所有可能选项,并给出了一些奇妙的原因,为什么[开始,结束]是正确的答案。我没有方便的链接,但STL选择的标准基于坚实的基础。 – Omnifarious

+0

1)备注:虽然不是STL的一部分,但std :: string可以使用索引+大小而不是范围。 2)你能找到那张纸吗? – ybungalobill

2

原因改变了2到3中的代码:

temp2 = new char[end - start + 2]; 

作品是因为否则你会写过去getTextInRange缓冲区(你关闭一)结束。


endstart上述对应的参数endPosgetTextInRangestartPos,并在getTextInRange您有:

buffer[endPos - startPos + 2] = '\0'; 


您的阵列的范围为[0, endPos - startPos + 2);因此位置endPos - startPos + 2上的元素是1之后的数组。覆盖该值会导致堆损坏。

1

它是从你的代码表明你正在使用的getTextInRange其中最后指数是这样的:

endPos-startPos+2 //last index 

这几乎可以解释为什么你需要分配的内存大小如此最低

endPos-startPos+3 //number of objects : memory allocation 

也就是说,如果为N对象分配内存,则可以使用索引N-1访问数组中的最后一个对象,该索引也是最大的ind前阵列。指数N超出范围。回想一下,指数星号为0,所以它必须结束于N-1,而不是N