C# 这个简单的C代码会导致堆栈溢出吗?

C# 这个简单的C代码会导致堆栈溢出吗?,c#,stack-overflow,C#,Stack Overflow,我有一个类库,它主要将字符串记录到文本文件中。它的一个方法是LogStringstring str,所以我只是想知道,根据下面的函数,如果我多次调用它,比如超过600次,它会导致堆栈溢出吗 public void LogString(string str) { try { if (filePathFilenameExists()) { using (StreamWr

我有一个类库,它主要将字符串记录到文本文件中。它的一个方法是LogStringstring str,所以我只是想知道,根据下面的函数,如果我多次调用它,比如超过600次,它会导致堆栈溢出吗

    public void LogString(string str)
    {  
        try
        {
            if (filePathFilenameExists())
            {
                using (StreamWriter strmWriter = new StreamWriter(filePathFilename, true))
                {
                    strmWriter.WriteLine(str);
                    strmWriter.Flush();
                    strmWriter.Close();
                }
            }
            else
            {
                MessageBox.Show("Unable to write to file");
            }
        }
        catch (Exception err)
        {
            string errMsg = err.Message;
        }

    }

这甚至不是一个递归函数。我看不出这会导致堆栈溢出


为了使堆栈溢出,您必须不断地从函数内部调用函数。每次执行此操作时,当被调用函数返回时,会使用更多的堆栈空间来恢复调用函数。递归函数会遇到这个问题,因为它们需要存储同一函数状态的多个副本,每个递归级别一个副本。这也可能发生在相互递归的函数A调用B,B调用A中,并且可能更难检测,但我认为这里不是这种情况。

不,这不会导致堆栈溢出,但如果两个不同的线程尝试写入同一文件,则可能会导致其他异常。如果需要,请考虑使用锁使其线程安全。

您是否询问它是否会导致堆栈溢出或为什么会导致堆栈溢出?

    public void LogString(string str)
    {  
        try
        {
            if (filePathFilenameExists())
            {
                using (StreamWriter strmWriter = new StreamWriter(filePathFilename, true))
                {
                    strmWriter.WriteLine(str);
                    strmWriter.Flush();
                    strmWriter.Close();
                }
            }
            else
            {
                MessageBox.Show("Unable to write to file");
            }
        }
        catch (Exception err)
        {
            string errMsg = err.Message;
        }

    }

堆栈溢出通常发生在递归稍微出错时。

响应:问题可能出在调用方方法上,而不是日志字符串本身。如果调用方方法确实是递归方法,那么如果分配的堆栈大小不够大,则肯定存在堆栈溢出的风险。

调用此函数不会导致堆栈溢出。函数递归调用自身只能导致堆栈溢出。我将简要解释:

在程序中调用函数时,它会在调用堆栈上创建一个新条目。其中包含有关当前函数、调用它的位置以及函数的局部变量的一些信息。正如我所说,这个条目是在调用函数时创建的,但是当您从函数返回、抛出异常或简单地让函数结束时,系统使用该信息返回到前一个执行点,然后从调用堆栈中删除该条目

因此,在调用函数之前,堆栈上可能有:

.                           .
.                           .
.                           .
|                           |
+---------------------------+
| performComplexOperation() |
| return address            |
| local variables           |
| ...                       |
+---------------------------+
然后,调用LogString函数:

.                           .
.                           .
.                           .
|                           |
+---------------------------+
| performComplexOperation() |
| return address            |
| local variables           |
| ...                       |
+---------------------------+
| LogString()               |
| return address            |
| local variables           |
| ...                       |
+---------------------------+
但在LogString完成后,代码将使用返回地址返回performComplexOperation。日志字符串的条目将被删除:

.                           .
.                           .
.                           .
|                           |
+---------------------------+
| performComplexOperation() |
| return address            |
| local variables           |
| ...                       |
+---------------------------+
       (this is where
    LogString used to be)
在堆栈溢出中,堆栈上的条目占用的空间将超过您使用的语言*为堆栈分配的空间,并且程序将无法为您将要调用的函数创建条目


*不完全是,但是足够近了

我能想到的唯一问题是,如果在这个函数中发生了一些疯狂的事情,您没有列出filePathFilenameExists的代码

这就是Sean认为是递归的函数filePathFilenameExists

    public bool filePathFilenameExists()
    {
        if (File.Exists(filePathFilename))
        {
            return true;
        }
        else
        {
           MessageBox.Show("Can not open Existing File.");
           return false;
        }
    }

如果你真的想找到你的罪魁祸首,你需要在调用堆栈中看得更深一点,看看上面还有什么其他函数。更准确地说,寻找重复的痕迹


获取堆栈溢出异常的另一种方法是,如果代码在堆栈上分配了大块内存。您是否碰巧在代码的某个地方处理过大型结构?

这里还有一些有趣的事情,因为您发布的代码基本上不可能导致堆栈溢出

抛出堆栈溢出异常时,调用堆栈的其余部分是什么样子的? filePathFileName是字段还是属性?如果它是一个属性,你在getter中做了什么?也许再次调用LogStringString?
我认为保罗·费舍尔是对的,但这是我的第一篇帖子,我没有代表对他的回答发表评论

用我的话来说,同样的理论;您的调用函数导致堆栈溢出。它将自身的副本推送到堆栈上,直到距离末端相对较短。在调用函数的第N-1次递归迭代中,日志字符串的堆栈深度足以导致溢出,因为此时距离堆栈末尾的距离很短。filePathFilenameExists可能比调用函数中的大多数其他方法具有更深的最大堆栈,并且足以作为捕获异常的幸运儿来单独记录字符串

撇开理论不谈,假设从某个地方重复调用LogString,那么通过LogString的输出,您的问题应该是显而易见的。并在IDE调试器中查看堆栈。

。 请阅读。
它清楚地解释了为什么堆栈溢出异常所指向的函数不是罪魁祸首。

感谢大家的投入,并为这段时间的忙碌表示抱歉。这是我的错,因为我的程序有我不知道的递归。我没有发现的原因是,直到程序停止,我才继续跟踪调用。我总是在最后一次调用时停止调试

最后一个场景最后一个函数直到昨晚我继续调试直到程序停止,然后我看到有很多调用来回进行,因此根据我的编程经验,我得出结论,这就是递归。

显然filePathFilenameExists是递归实现的:DYou不应该显示消息框用那种方法。返回false,你的堆栈颠倒了=D我不能向后思考,但是,也许基于每个人的响应,这个函数不会导致堆栈溢出,但是异常总是指向调用的函数;或者是先前导致的错误?错误是在LogString上导致的,因为它正好在堆栈空间耗尽的地方。但很可能是调用LogString的函数造成的。我查看了IDE上的调用堆栈,没有发现任何奇怪的东西。FunctionA调用FunctionB,FunctionA调用functionC,FunctionA调用FunctionD,依此类推。FunctionA显示了很多,因为它从数组中读取每个内容,然后传递给FunctionB、C、D。FunctionA将在它可能不会导致堆栈溢出时停止调用,但它会导致堆栈溢出。没有理由调用流上的Flush和Close,因为这已由using构造处理。只是为了记录,我想你不想一秒钟显示600个MessageBox,所以我想你应该确保只显示一次,或者使用其他一些错误报告方法。当抛出stackoverflow时,我如何确定堆栈大小?自从我编程以来,我从来没有考虑过堆栈大小,因为我的程序不是很大,但是你能告诉我如何根据你的第一个问题找到堆栈吗。filepathFileName是在类库构造函数中分配的公共成员数据。不确定您的意思是什么“您的getter”日志字符串将被多次调用,但不是无限循环调用。您使用的是Visual Studio吗?如果是这样,当抛出异常时,您可以查看调用堆栈窗口。如果您看到许多对某个函数的调用,特别是在一行中,那么这可能就是罪魁祸首。如果filePathFileName是公共成员,那么这并不重要。“getter”的意思是,如果它是一个属性,那么可以指定一个get函数。如果filePathFileName的get函数调用了LogString,而LogString又调用了filePathFileName的get函数……感谢您的响应。在我的程序中,我声明了数组类型的全局变量,然后在函数bodynot构造函数中,我分配了大小为224的数组。每个数组索引对应于每个案例场景。每个场景都是一个函数,因此每个函数都会被调用。实际上,这也是我的第一篇文章。我创建了一个大小为400的全局数组,还有三个ArrayList,但它非常小,可能最多30个。调用堆栈,我看了一下,它确实有重复的痕迹,因为从FunctionA到FunctionB的调用数量是基于数组[400]的大小的,这是程序的目的。它必须获取数组[400]中的每一项,并进行验证,然后从数组[400]中取出下一项直到没有更多,然后将停止调用。我将数组从400减少到224,仍然存在错误。当我将224减少到43时,它毫无例外地执行得很好。如果您发布调用堆栈和一些由数组控制的代码,我们可能可以帮助您确定实际问题。您是否碰巧将该数组作为参数传递给递归函数?您可以尝试将数组装箱到一个对象中并传递对它的引用。我使用两个线程,但当程序执行时,一个线程在5秒后被释放,因此只有主线程将继续运行程序的其余部分。如果是共享冲突,它应该显示消息,对吗?因为我以前犯过这种错误。