带有尾递归函数c ++的堆栈溢出

Posted

技术标签:

【中文标题】带有尾递归函数c ++的堆栈溢出【英文标题】:Stack Overflow with tail recursion function c++ 【发布时间】:2018-10-29 22:53:29 【问题描述】:

代码的作用: 下面的代码应该通过限制每行代码中的字符数而不丢失任何代码来为打印机格式化 .cpp 文件。代码应该逐个字符地遍历文件。它通过打开ifstream 然后使用get 方法获取每个字符直到找到EOF 来实现。当每个字符被抓取时,它被推送到std::string (vector),当每个字符被推送时,行字符数会增加。如果在行字符计数大于其最大大小之前找到新行字符,则将其重置并继续下一行。如果在找到 '\n' 字符之前,行字符数比最大字符数少 2,则将 \\n' ''s(x tab count) 附加到 string 的末尾被写入。然后该行的其余部分附加到string。但是,如果添加到string 的最后一个字符不是' ' 字符,那么string 会将其末尾和最靠近左侧' ' 字符之间的所有字符都删除并放置在新字符的开头行,在新行上的 5 个 ' ' 字符之后。最后一点是我认为错误所在。 示例行:(假装大于最大字符数)

LongLong123 longLineOfCode;\n;

样本输出:

不好:

LongLong123 longLineOfCo\\n
      de;\n

好:

LongLong123\\n
      longLineOfCode;\n

错误:

SubmitFormater.exe 中 0x55CC1949 (ucrtbased.dll) 处未处理的异常:0xC00000FD:堆栈溢出(参数: 0x00000000, 0x00F02000)。发生了

示例命令行:

110 5 "C:/Users/holycatcrusher/Documents/OC 2018 Fall/222/Labs/COSC_222_Data_Structures_Cpp/SubmitFormater.cpp"

110是一行可以有多少个字符,5是制表符的字符个数(5个空格),长字符串是要格式化的文件的地址(复制一份文件的错误,输入的文件将被销毁)。我正在使用构成该项目的代码作为示例输入(它的副本)。

代码:

/* SubmitFormater.cpp
    This file was created by Bradley Honeyman
    Oct 25, 2018

    This file is used to format submit files for COSC 222 asignments

*/

#include "pch.h"
#include <iostream>
#include <fstream>
#include <string>
#include <stdbool.h>
#include <stddef.h>

#define _CRTDBG_MAP_ALLOC  
#include <stdlib.h>  
#include <crtdbg.h>

std::string *processFile(std::ifstream *input, int maxCharCount, int tabSpaceSize, char delimiter, std::string *code, int lineCharCount);
int toInt(char *input);

int main(int argc, char **argv) 

    std::cout << "Running 222 formatter" << std::endl << "by: Bradley Honeyman" << std::endl;

    _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);

    //make sure there is the min number of commands
    if (argc < 3) 
        std::cout << "Ensure there is a specified max line length and at least one file to modify!" << std::endl;
        return 0;
    

    for (int i = 3; i < argc; i++) 

        //open file and make sure it opens
        std::ifstream input;
        input.open(argv[i]);
        if (!input.is_open()) 
            std::cout << "Could not open the input file: " << argv[i] << std::endl;
            return EXIT_FAILURE;
        

        //process code
        std::string *code = new std::string();
        processFile(&input, toInt(argv[1]), toInt(argv[2]), ' ', code, 0);

        //check for error 
        if (code == NULL) 
            return EXIT_FAILURE;
        

        //output
        input.close();
        std::ofstream out(argv[i]);
        if (!out.is_open()) 
            std::cout << "could not write to output file" << std::endl;
            return EXIT_FAILURE;
        

        //print to file
        std::cout << code->c_str() << std::endl;
        out << code->c_str();

        //close out delete code pointer
        out.close();
        delete code;

    

    return 0;


/*
    formats a file by placing \\\n the custom tab whereever the length of a line is bigger than the max
    also a delimiter is used, so words aren't cut in half
*/
#define FORMAT_CHAR_COUNT 2
std::string *processFile(std::ifstream *input, int maxCharCount, int tabSpaceSize, char delimiter, std::string *code, int lineCharCount) 

    //std::cout << "Processing" << std::endl;

    //get char and check if is end of file
    char current = input->get();
    //std::cout << "\'" << current << "\'" << std::endl;
    if (input->eof()) 
        return code;
    

    //incerment char count then check if are at furthest possible position
    lineCharCount++;
    //std::cout << lineCharCount << std::endl;
    if (current == '\n') 
        lineCharCount = 0;

        //check if are at max char count, split code and put to new line
     else if (lineCharCount >= maxCharCount && current != '\\') 

        //if not at the end of the line go back to the closest delimiter to break
        std::string *pre = new std::string("");
        bool fail = false;
        if (current != '\n' && code->at(code->size() - 1)) 
            code->push_back(current);
            int i = code->size() - 1;
            int back = 0;
            for (i; i >= 0; i--) 
                pre->push_back(code->at(i));
                back++;
                if (back > maxCharCount - tabSpaceSize - FORMAT_CHAR_COUNT) 
                    std::cout << "Can not format file because there isn't a place close enough to break at a delimiter!" << std::endl;
                    fail = true;
                    break;
                
            

            //check for fail
            if (!fail) 

                //add delimiter if it needs to be
                if (pre->size() > 0 && pre->at(pre->size() - 1) != delimiter) 
                    pre->push_back(delimiter);
                

                //reverse prepending string, and remove code that is to be prepended
                std::reverse(pre->begin(), pre->end());
                code->assign(code->substr(0, code->size() - back - 1));

            
        

        //insert \ then new line then tab then set char count to tab size + pre size + 1 for char to be added
        code->push_back('\\');
        code->push_back('\n');
        for (int i = 0; i < tabSpaceSize; i++)  code->push_back(' ');  //create tab
        code->append(pre->c_str());
        lineCharCount = tabSpaceSize + 1 + pre->size();
        pre->clear();
        delete pre;

    

    //add char to code and run again
    code->push_back(current);

    return processFile(input, maxCharCount, tabSpaceSize, delimiter, code, lineCharCount);


/*
    converts string to an int
*/
int toInt(char *input) 
    int i = 0;
    int out = 0;
    while (input[i] != '\0') 
        out = (out * 10) + (input[i] - '0');
        i++;
    
    return out;

此外,您可以将上述代码用作示例 .cpp 文件。请记住使用副本,因为程序会修改文件! 调用堆栈第 1 部分和第 2 部分(单击图片即可阅读)

注意: 问题的原因已在对话中说明。

【问题讨论】:

C++ 不需要编译器执行尾调用消除。你的显然没有,至少不是你要求的优化级别。 【参考方案1】:

在这篇帖子上进行了大量讨论之后,我认为值得发布最终成为我的解决方案的内容。还有一些关于我总结的笔记。 代码:

   /* SubmitFormater.cpp
    This file was created by Bradley Honeyman
    Oct 25, 2018

    This file is used to format submit files for COSC 222 asignments

*/

#include "pch.h"
#include <iostream>
#include <fstream>
#include <string>
#include <stdbool.h>
#include <stddef.h>

#define _CRTDBG_MAP_ALLOC  
#include <stdlib.h>  
#include <crtdbg.h>

std::string *processFile(std::ifstream *input, int maxCharCount, int tabSpaceSize, char delimiter, std::string *code, int lineCharCount);
int toInt(char *input);

int main(int argc, char **argv) 

    std::cout << "Running 222 formatter" << std::endl << "by: Bradley Honeyman" << std::endl;

    _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);

    //make sure there is the min number of commands
    if (argc < 3) 
        std::cout << "Ensure there is a specified max line length and at least one file to modify!" << std::endl;
        return 0;
    

    for (int i = 3; i < argc; i++) 

        //open file and make sure it opens
        std::ifstream input;
        input.open(argv[i]);
        if (!input.is_open()) 
            std::cout << "Could not open the input file: " << argv[i] << std::endl;
            return EXIT_FAILURE;
        

        //process code
        std::string *code = new std::string();
        processFile(&input, toInt(argv[1]), toInt(argv[2]), ' ', code, 0);

        //check for error 
        if (code == NULL) 
            return EXIT_FAILURE;
        

        //output
        input.close();
        std::ofstream out(argv[i]);
        if (!out.is_open()) 
            std::cout << "could not write to output file" << std::endl;
            return EXIT_FAILURE;
        

        //print to file
        out << code->c_str();

        //close out delete code pointer
        out.close();
        delete code;

    

    return 0;


/*
    formats a file by placing \\\n the custom tab whereever the length of a line is bigger than the max
    also a delimiter is used, so words aren't cut in half
*/
#define FORMAT_CHAR_COUNT 2
std::string *processFile(std::ifstream *input, int maxCharCount, int tabSpaceSize, char delimiter, std::string *code, int lineCharCount) 

    //get char and check if is end of file
    char current = input->get();
    if (input->eof()) 
        return code;
    

    //incerment char count then check if are at furthest possible position
    lineCharCount++;
    //std::cout << lineCharCount << std::endl;
    if (current == '\n') 
        lineCharCount = 0;

        //check if are at max char count, split code and put to new line
     else if (lineCharCount >= maxCharCount && current != '\\') 

        //if not at delimiter push everything to the right of the nearest delimiter to the left to pre
        int back = 0;
        std::string pre("");
        if (current != delimiter) 
            for (int i = code->size() - 1; i >= 0; i--) 
                back++;
                if (code->at(i) == delimiter) 
                    pre.push_back(code->at(i));
                    break;

                 else 
                    pre.push_back(code->at(i));

                

            

            //remove what was added to pre from code
            std::reverse(pre.begin(), pre.end());
            code->assign(code->substr(0, code->size() - back));

        

        //insert \ then new line then tab then set char count to tab size + pre size + 1 for char to be added
        code->push_back('\\');
        code->push_back('\n');
        for (int i = 0; i < tabSpaceSize; i++)  code->push_back(' ');  //create tab
        code->append(pre);
        lineCharCount = tabSpaceSize + 1 + pre.size();

    

    //add char to code and run again
    code->push_back(current);
    return processFile(input, maxCharCount, tabSpaceSize, delimiter, code, lineCharCount);


/*
    converts string to an int
*/
int toInt(char *input) 
    int i = 0;
    int out = 0;
    while (input[i] != '\0') 
        out = (out * 10) + (input[i] - '0');
        i++;
    
    return out;

注意事项:

代码仅在 VS 中编译为发布版本时运行 while 循环中出现逻辑错误 编译器优化似乎可以解决问题

【讨论】:

以上是关于带有尾递归函数c ++的堆栈溢出的主要内容,如果未能解决你的问题,请参考以下文章

如何解决栈溢出

为啥带有 setTimeout 的函数不会导致堆栈溢出

递归函数导致堆栈溢出

C语言 尾递归

如何减少 C 中深度递归函数的堆栈帧?

递归函数