Search code examples
c++debuggingcalculatorheap-corruption

How do I debug my "heap corruption"? (C++)


first-time user and it seems from other posts to be a case-by-case basis. Well, I am a self-taught programmer of the C++ language. So, I finally learned about dynamically allocated memory and began thinking of new ways of programming a calculator using CLI to input a single string of text and pump out an answer. It seemed like a good jumping off point for the project and I began working on a solution. The following are my results:

#include <iostream>
#include <string>
using namespace std;

int main()
{
    string strInput;
    cin >> strInput;
    int nOperatorCount = 0;
    for(int i = 0; i<(strInput.size());i++)
    {
        if(strInput[i]=='+'||strInput[i]=='-'||strInput[i]=='*'||strInput[i]=='/')
            nOperatorCount++;
    }
    int *pnOperatorLocation = new int[nOperatorCount+1];
    int *pnOperatorType = new int[nOperatorCount];
    float *pnExpressions = new float[nOperatorCount + 1];
    pnOperatorLocation[0] = -1;
    for(int i=0,j=0; i<(strInput.size());i++)
    {
        if(strInput[i]=='+')
    {
            pnOperatorLocation[j+1] = i;
            pnOperatorType[j] = 0;
            pnExpressions[j] = atoi((strInput.substr(pnOperatorLocation[j]+1,i)).c_str());
            j++;
        }
        if(strInput[i]=='-')
        {
            pnOperatorLocation[j+1] = i;
            pnOperatorType[j] = 1;
            pnExpressions[j] = atoi((strInput.substr(pnOperatorLocation[j]+1,i)).c_str());
            j++;
        }
        if(strInput[i]=='*')
        {
            pnOperatorLocation[j+1] = i;
            pnOperatorType[j] = 2;
            pnExpressions[j] = atoi((strInput.substr(pnOperatorLocation[j]+1,i)).c_str());
            j++;
        }
        if(strInput[i]=='/')
        {
            pnOperatorLocation[j+1] = i;
            pnOperatorType[j] = 3;
            pnExpressions[j] = atoi((strInput.substr(pnOperatorLocation[j]+1,i)).c_str());
            j++;
        }
        if(i==(strInput.size()-1))
            pnExpressions[j] = atoi((strInput.substr(pnOperatorLocation[j]+1,i+1)).c_str());
    }
    for(int i=0;i<nOperatorCount+1;i++)
    {
        cout << "pnExpressions[" << i << "]: " << pnExpressions[i] << endl;
    }
    int nOperationsCount = 0;
    for(int i=0;i<nOperatorCount;i++)
    {
        if(pnOperatorType[i]==2||pnOperatorType[i]==3)
        {
            if(pnOperatorType[i+1]==0||pnOperatorType[i+1]==1)
                nOperationsCount++;
            else if((i+1)==nOperatorCount)
                nOperationsCount++;
        }
    }
    cout << "nOperationsCount: " << nOperationsCount << endl;
    float *pnNewExpressions = new float[nOperationsCount];
    for(int i=0,j=0;i<nOperatorCount;i++)
    {
        if(pnOperatorType[i]==2)
        {
            pnNewExpressions[j] = pnExpressions[i] * pnExpressions[i+1];
            if(pnOperatorType[i+1]==2||pnOperatorType[i+1]==3)
            {
                for(int k=i;k<nOperatorCount-i;k++)
                {
                    if(pnOperatorType[k+1]==2)
                        pnNewExpressions[j] = pnNewExpressions[j] * pnExpressions[k+2];
                    else if(pnOperatorType[k+1]==3)
                        pnNewExpressions[j] = pnNewExpressions[j] / pnExpressions[k+2];
                    else
                        break;
                    if(k+1>=nOperatorCount)
                        break;
                }
            }
            j++;
        }
        if(pnOperatorType[i]==3)
        {
            pnNewExpressions[j] = pnExpressions[i] / pnExpressions[i+1];
            if(pnOperatorType[i+1]==2||pnOperatorType[i+1]==3)
            {
                for(int k=i;k<nOperatorCount-i;k++)
                {
                    if(pnOperatorType[k+1]==2)
                        pnNewExpressions[j] = pnNewExpressions[j] * pnExpressions[k+2];
                    else if(pnOperatorType[k+1]==3)
                        pnNewExpressions[j] = pnNewExpressions[j] / pnExpressions[k+2];
                    else
                        break;
                    if(k+1>=nOperatorCount)
                        break;
                }
            }
            j++;
        }
        if(i+1>=nOperatorCount)
            break;
    }
    for(int i=0;i<nOperationsCount;i++)
    {
        cout << "pnNewExpressions[" << i << "]: " << pnNewExpressions[i] << endl;
    }
    float fEvaluation;
    if(pnOperatorType[0]==2||pnOperatorType[0]==3)
        fEvaluation = pnNewExpressions[0];
    else
        fEvaluation = pnExpressions[0];
    cout << "fEvaluation: " << fEvaluation << endl;
    for(int i=0,j=1;i<nOperatorCount;i++)
    {
        if(pnOperatorType[i]==0)
        {
            if(pnOperatorType[i+1]==2||pnOperatorType[i+1]==3)
            {
                fEvaluation = fEvaluation + pnNewExpressions[j];
                j++;
                cout << "fEvaluation: " << fEvaluation << endl;
            }
            else
            {
                fEvaluation = fEvaluation + pnExpressions[i+1];
                cout << "fEvaluation: " << fEvaluation << endl;
            }
        }
        if(pnOperatorType[i]==1)
        {
            if(pnOperatorType[i+1]==2||pnOperatorType[i+1]==3)
            {
                fEvaluation = fEvaluation - pnNewExpressions[j];
                j++;
                cout << "fEvaluation: " << fEvaluation << endl;
            }
            else
            {
                fEvaluation = fEvaluation - pnExpressions[i+1];
                cout << "fEvaluation: " << fEvaluation << endl;
            }
        }
    }
    cout << "fEvaluation: " << fEvaluation << endl;
    delete[] pnOperatorLocation;
    delete[] pnNewExpressions;
    delete[] pnOperatorType;
    delete[] pnExpressions;
    system("pause");
    return 0;
}

I'm so so SO sorry if you read through all that, but I've been working on a second copy that's been boiled down and a lot less complicated, but I'd figure why this one stopped working all of a sudden. It was working for about 10-15 uses and the I used an input such as "2*3*4+2*3+2" and it returned "CORRUPTION HEAP DETECTED: after normal block #184". So my question is does anyone have any idea how I can debug this based off that error-message? If not, can anyone find why my code works for simple equations like 1+1 and 8*8, but not the long complex ones like I intended it to?


Solution

  • The issue is this section of code:

    for (int i = 0, j = 0; i < nOperatorCount; i++)
    {
        if (pnOperatorType[i] == 2)
        {
            pnNewExpressions[j] = pnExpressions[i] * pnExpressions[i + 1]; // <<--- Error 
    

    The subscript j is out of bounds in the loop for the input of 2*3*4+2*3+2.

    But here is the reason why I found the error quickly -- I replaced all of those calls to new[] and delete[] with std::vector. Since you're using Visual Studio (the error is a VS error), the DEBUG version does automatic checking for vector array bounds errors. The error box that shows up gives you a subscript out of range error, and thus the VS IDE goes right to the line where the violation occurs.

    Now, the std::vector::operator[] does not check bounds in a release version, and in general operator [] is not supposed to do so. However the debugging libraries for VS turns on this check. So you can say I am taking advantage of what Visual Studio offers in terms of debugging help. But I was only afforded this once I changed to using std::vector.

    The vector::at() function serves the same purpose as using [], but it gives you the range checking, regardless of whether you are running a release or debug version. I didn't replace the calls with at() here, but if for some reason [] didn't find the error, I could always change the [] to at() calls in the code until I get an error generated.

    Here is a quick synopsis of the changes:

    #include <iostream>
    #include <string>
    #include <vector>
    //...
    std::vector<int> pnOperatorLocation(nOperatorCount + 1);
    std::vector<int> pnOperatorType(nOperatorCount);
    std::vector<float> pnExpressions(nOperatorCount + 1);
    //...
    std::vector<float> pnNewExpressions(nOperationsCount);
    

    In addition, all of the calls to delete[] are no longer necessary.

    Does this fix the error? No. I didn't want to check your algorithm/logic in trying to solve the equation. You still need to put in that effort to figure out what the purpose of that loop is, and why j goes out of bounds. However it does show that merely using modern C++ techniques can be advantageous. Using new[] and delete[] to create dynamic arrays is "old fashioned" and really not necessary, unless you really do need to use it.

    Basically, you need to ask yourself -- are you trying to code an equation solver? If the answer is "yes", then use the tools available to create one (such as std::vector) and move on with solving "easy" bugs, instead of fighting with using pointers and putting in a lot of effort in maintaining dynamic arrays correctly.