Переполнение стека при отладке, но не в выпуске - PullRequest
4 голосов
/ 15 апреля 2011

Ниже приведен следующий код, который анализирует текстовый файл и индексирует слова и строки:

bool Database::addFromFileToListAndIndex(string path, BSTIndex* & index, list<Line *> & myList)
{
    bool result = false;
    ifstream txtFile;
    txtFile.open(path, ifstream::in);
    char line[200];
    Line * ln;
    //if path is valid AND is not already in the list then add it
    if(txtFile.is_open() && (find(textFilePaths.begin(), textFilePaths.end(), path) == textFilePaths.end())) //the path is valid
    {
        //Add the path to the list of file paths
        textFilePaths.push_back(path);
        int lineNumber = 1;
        while(!txtFile.eof())
        {
            txtFile.getline(line, 200);
            ln = new Line(line, path, lineNumber);
            if(ln->getLine() != "")
            {
                lineNumber++;
                myList.push_back(ln);
                vector<string> words = lineParser(ln);
                for(unsigned int i = 0; i < words.size(); i++)
                {
                    index->addWord(words[i], ln);
                }
            }
        }
        result = true;
    }
    return result;
}

Мой код работает без нареканий и довольно быстро, пока я не передам ему ОГРОМНЫЙ текстовый файл. Затем я получаю ошибку переполнения стека из Visual Studio. Когда я переключаюсь в конфигурацию «Release», код запускается без проблем. Что-то не так с моим кодом или есть какие-то ограничения при запуске конфигурации «Отладка»? Я пытаюсь сделать слишком много в одной функции? Если так, как я могу разбить его, чтобы он не зависал при отладке?

EDIT По запросу моя реализация addWord;

void BSTIndex::addWord(BSTIndexNode *& pCurrentRoot, string word, Line * pLine)
    {
        if(pCurrentRoot == NULL)  //BST is empty
        {
            BSTIndexNode * nodeToAdd = new BSTIndexNode();
            nodeToAdd->word = word;
            nodeToAdd->pData = pLine;
            pCurrentRoot = nodeToAdd;
            return;
        }
        //BST not empty
        if (word < (pCurrentRoot->word)) //Go left
        {
            addWord(pCurrentRoot->pLeft, word, pLine);
        }
        else //Go right
        {
            addWord(pCurrentRoot->pRight, word, pLine);
        }
    }

И lineParser:

vector<string> Database::lineParser(Line * ln) //Parses a line and returns a vector of the words it contains
{
    vector<string> result;
    string word;
    string line = ln->getLine();
    //Regular Expression, matches anything that is not a letter, number, whitespace, or apostrophe
    tr1::regex regEx("[^A-Za-z0-9\\s\\']");
    //Using regEx above, replaces all non matching characters with nothing, essentially removing them.
    line = tr1::regex_replace(line, regEx, std::string(""));

    istringstream iss(line);
    while(iss >> word)
    {
        word = getLowercaseWord(word);
        result.push_back(word);
    }
    return result;
}

Ответы [ 4 ]

5 голосов
/ 15 апреля 2011

Переполнение стека означает, что вы исчерпали пространство стека (вероятно, очевидно, но на всякий случай).Типичными причинами являются не прекращающаяся или чрезмерная рекурсия, или дублирование очень большого стека объектов.Как ни странно, это может быть и в этом случае.

Вполне вероятно, что в Release ваш компилятор выполняет оптимизацию хвостовых вызовов, которая предотвращает переполнение стека из-за чрезмерной рекурсии.

Также вероятно, что в Release вашего компилятораоптимизирует возвращаемую копию вектора из lineParser.

Так что вам нужно выяснить, какое условие переполняется в Debug, я бы начал с рекурсии как наиболее вероятного виновника, пытаясь изменить тип строкового параметра нассылка, т. е.

void BSTIndex::addWord(BSTIndexNode *& pCurrentRoot, string & word, Line * pLine)

Это должно помешать вам дублировать объект слова при каждом вложенном вызове addWord.

Также рассмотрите возможность добавления std :: cout << "recursing addWord" <<станд :: епсИ;введите оператор addWord, чтобы вы могли видеть, насколько глубоко он идет и правильно ли он завершается. </p>

3 голосов
/ 15 апреля 2011

Проблема почти наверняка заключается в рекурсивном вызове в addWord - в неоптимизированной сборке это будет занимать много места в стеке, а в оптимизированной сборке компилятор превратит его в хвостовой вызов, который повторно использует тот же стек рама.

Вы можете довольно легко вручную преобразовать рекурсивный вызов в цикл:

void BSTIndex::addWord(BSTIndexNode ** pCurrentRoot, string word, Line * pLine)
{
    while (*pCurrentRoot != NULL) {
        //BST not empty
        if (word < (*pCurrentRoot)->word) //Go left
        {
            pCurrentRoot = &(*pCurrentRoot)->pLeft;
        }
        else //Go right
        {
            pCurrentRoot = &(*pCurrentRoot)->pRight;

        }
    }
    //BST is empty
    BSTIndexNode * nodeToAdd = new BSTIndexNode();
    nodeToAdd->word = word;
    nodeToAdd->pData = pLine;
    *pCurrentRoot = nodeToAdd;
}
1 голос
/ 15 апреля 2011

Вы должны также опубликовать свой стек, который бы фактически показал, что вызвало переполнение. Кажется довольно очевидным, что рекурсия в addWord значительно потребляет стековую память.

Если вы просто хотите, чтобы это работало, зайдите в настройки компилятора / компоновщика и увеличьте размер, зарезервированный для вашего стека. По умолчанию это всего 1 МБ, увеличьте его до 32 МБ или около того, и вы будете уверены, что у любого дополнительного счетчика или зонда, который есть в отладочной сборке, у вас будет достаточно стека для его обработки.

0 голосов
/ 11 мая 2012

Вы можете увеличить размер стека до соответствующего количества байтов.

#pragma comment(linker, "/STACK:1000000000")  
...