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

时间:2021-07-18 17:02:20

I have a class library that mainly logs the string to text file. One of its method is LogString(string str) so I'm just wondering based on the function below if I call it for many times like over 600 times, does it cause *?

我有一个类库,主要将字符串记录到文本文件。它的一个方法是LogString(字符串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;
        }

    }

13 个解决方案

#1


That doesn't even appear to be a recursive function. I don't see how that would cause a stack overflow.

这似乎不是一个递归函数。我不知道这会如何导致堆栈溢出。

In order to overflow a stack you have to continually call functions from within functions. Every time you do this, more stack space is used to resume the calling function when the called function returns. Recursive functions run into this problem because they need to store multiple copies of the state of the same function, one for each level of recursion. This can also happen in mutually recursive functions (A calls B, B calls A) and it can be harder to detect, but I don't think that's the case here.

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

#2


No, it won't cause stack overflow but it can cause other exceptions if two different threads will try to write into the same file. consider using lock to make it thread-safe if necessary.

不,它不会导致堆栈溢出,但如果两个不同的线程尝试写入同一个文件,它可能会导致其他异常。考虑使用锁来在必要时使其成为线程安全的。

#3


@Franci Penov offers good advice.
Please read this post by Raymond Chen.
It neatly explains why the function that the stack overflow exception is pointing to is not the culprit.

@Franci Penov提供了很好的建议。请阅读Raymond Chen的这篇文章。它巧妙地解释了为什么堆栈溢出异常指向的函数不是罪魁祸首。

#4


If you realy want to find your culprit, you need to look a little bit deeper in the call stack and look what other functions are on it. More precisely, look for repetetive traces.

如果你真的想找到你的罪魁祸首,你需要在调用堆栈中看一点,看看其他功能是什么。更准确地说,寻找重复的痕迹。

Another way to get the stack overflow exception is if your code allocates big chunk of memory on the stack. Do you happen to deal with big structures somewhere in your code?

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

#5


Are you asking if it will it cause a * or why does it cause a *?

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

*s (usually) happen when recursion goes slightly wrong.

当递归稍微错误时,会发生*s(通常)。

#6


In response to OP's update: The problem is probably with the caller method, and not LogString() itself. If the caller method is indeed a recursive method, then there is certainly a risk of *ing if the allocated stack size is not big enough.

响应OP的更新:问题可能在于调用方法,而不是LogString()本身。如果调用方法确实是递归方法,那么如果分配的堆栈大小不够大,则肯定存在堆栈溢出的风险。

#7


Calling this function will not cause a "stack overflow". You can only cause a stack overflow by a function calling itself recursively. I'll explain briefly:

调用此函数不会导致“堆栈溢出”。您只能通过递归调用自身的函数导致堆栈溢出。我将简要解释一下:

When you call a function in a program, it creates a new entry on the "call stack". This contains a little information about the current function, the place it was called from, and the local variables of the function. This entry, as I said, is created when you call the function, but when you return from the function, throw an exception, or simply let the function reach its end, the system uses that information to return to the previous point of execution, then removes that entry from the call stack.

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

So, before you call your function, you might have on the stack:

因此,在调用函数之前,您可能已经在堆栈中:

.                           .
.                           .
.                           .
|                           |
+---------------------------+
| performComplexOperation() |
| return address            |
| local variables           |
| ...                       |
+---------------------------+

then, you call the LogString function:

然后,你调用LogString函数:

.                           .
.                           .
.                           .
|                           |
+---------------------------+
| performComplexOperation() |
| return address            |
| local variables           |
| ...                       |
+---------------------------+
| LogString()               |
| return address            |
| local variables           |
| ...                       |
+---------------------------+

But after LogString completes, the code will return to performComplexOperation using the return address. The entry for LogString will be removed:

但是在LogString完成之后,代码将使用返回地址返回performComplexOperation。将删除LogString的条目:

.                           .
.                           .
.                           .
|                           |
+---------------------------+
| performComplexOperation() |
| return address            |
| local variables           |
| ...                       |
+---------------------------+
       (this is where
    LogString used to be)

In a stack overflow, the space taken by the entries on the stack will exceed the space alloted for the stack by the language you're using*, and the program won't be able to create the entry for the function you're about to call.

在堆栈溢出中,堆栈上的条目占用的空间将超过您正在使用的语言分配给堆栈的空间*,并且程序将无法为您所关注的函数创建条目打电话。

* Not exactly, but close enough.

*不完全,但足够接近。

#8


Only problem I could think of is if you have something crazy going on in this function which you do not list code for filePathFilenameExists()

我能想到的唯一问题是,如果你在这个函数中有一些疯狂的东西,你没有列出filePathFilenameExists()的代码

#9


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

#10


Something else is funny here because there's basically no way the code you've posted can cause a stack overflow.

还有一件事很有趣,因为你发布的代码基本上没有办法导致堆栈溢出。

  • What's the rest of your call stack look like when the stack overflow exception is thrown?
  • 当抛出堆栈溢出异常时,调用堆栈的其余部分是什么样的?

  • Is filePathFileName a field or a property? If it's a property, are you doing anything in your getter? Perhaps calling LogString(String) again?
  • filePathFileName是字段还是属性?如果它是一个属性,你在吸气器中做什么吗?也许再次调用LogString(String)?

#11


I think Paul Fisher is right but this is my first post and I don't have the rep to comment on his answer.

我认为保罗费舍尔是对的,但这是我的第一篇文章,我没有代表评论他的回答。

The same theory in my words; your calling function is causing the stack overflow. It pushes copies of itself onto the stack until it's a relatively short distance from the end. On some "N-1"th recursive iteration of the calling function, the stack depth of your LogString is enough to cause the overflow, since you'd then be within a pretty short distance from the end of the stack. filePathFilenameExists() likely has a deeper maximum stack than most of the other methods in the calling function and is enough to single LogString out as the lucky guy who catches the exception.

用我的话说同样的理论;你的调用函数导致堆栈溢出。它将自身的副本推送到堆栈,直到距离末端相对较短的距离。在调用函数的某些“N-1”递归迭代中,LogString的堆栈深度足以导致溢出,因为您距离堆栈末尾的距离非常短。 filePathFilenameExists()可能比调用函数中的大多数其他方法具有更深的最大堆栈,并且足以将单个LogString单独作为捕获异常的幸运者。

Theory aside, your problem should be apparent by the output of LogString, assuming it's being called repetitively from somewhere. That and viewing your stack in the IDE debugger.

除了理论之外,你的问题应该通过LogString的输出显而易见,假设它是从某个地方重复调用的。那并在IDE调试器中查看您的堆栈。

#12


This is the function filePathFileNameExists that Sean thought was recursive.

这是Sean认为是递归的函数filePathFileNameExists。

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

#13


Thanks you everyone for the inputs and sorry for the hustle. It was my fault because my program has recursion which I didn't know. The reason I didn't find that out because I didn't continue to trace the calls until the program stopped. I always stopped the debug when the last call was made to that last case scenario(last function) until last night I continued to debug until the program stopped itself then I saw there were many calls were made back and forth so based on my little programming experience I conclude that is recursion.

感谢大家的投入,并为喧嚣感到抱歉。这是我的错,因为我的程序有递归,我不知道。之所以我没有发现这一点,是因为在程序停止之前我没有继续跟踪调用。我总是在最后一次调用最后一个案例(最后一个函数)时停止调试,直到昨晚我继续调试,直到程序停止自身,然后我看到有很多调用来自于我的小编程经验我得出的结论是递归。

#1


That doesn't even appear to be a recursive function. I don't see how that would cause a stack overflow.

这似乎不是一个递归函数。我不知道这会如何导致堆栈溢出。

In order to overflow a stack you have to continually call functions from within functions. Every time you do this, more stack space is used to resume the calling function when the called function returns. Recursive functions run into this problem because they need to store multiple copies of the state of the same function, one for each level of recursion. This can also happen in mutually recursive functions (A calls B, B calls A) and it can be harder to detect, but I don't think that's the case here.

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

#2


No, it won't cause stack overflow but it can cause other exceptions if two different threads will try to write into the same file. consider using lock to make it thread-safe if necessary.

不,它不会导致堆栈溢出,但如果两个不同的线程尝试写入同一个文件,它可能会导致其他异常。考虑使用锁来在必要时使其成为线程安全的。

#3


@Franci Penov offers good advice.
Please read this post by Raymond Chen.
It neatly explains why the function that the stack overflow exception is pointing to is not the culprit.

@Franci Penov提供了很好的建议。请阅读Raymond Chen的这篇文章。它巧妙地解释了为什么堆栈溢出异常指向的函数不是罪魁祸首。

#4


If you realy want to find your culprit, you need to look a little bit deeper in the call stack and look what other functions are on it. More precisely, look for repetetive traces.

如果你真的想找到你的罪魁祸首,你需要在调用堆栈中看一点,看看其他功能是什么。更准确地说,寻找重复的痕迹。

Another way to get the stack overflow exception is if your code allocates big chunk of memory on the stack. Do you happen to deal with big structures somewhere in your code?

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

#5


Are you asking if it will it cause a * or why does it cause a *?

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

*s (usually) happen when recursion goes slightly wrong.

当递归稍微错误时,会发生*s(通常)。

#6


In response to OP's update: The problem is probably with the caller method, and not LogString() itself. If the caller method is indeed a recursive method, then there is certainly a risk of *ing if the allocated stack size is not big enough.

响应OP的更新:问题可能在于调用方法,而不是LogString()本身。如果调用方法确实是递归方法,那么如果分配的堆栈大小不够大,则肯定存在堆栈溢出的风险。

#7


Calling this function will not cause a "stack overflow". You can only cause a stack overflow by a function calling itself recursively. I'll explain briefly:

调用此函数不会导致“堆栈溢出”。您只能通过递归调用自身的函数导致堆栈溢出。我将简要解释一下:

When you call a function in a program, it creates a new entry on the "call stack". This contains a little information about the current function, the place it was called from, and the local variables of the function. This entry, as I said, is created when you call the function, but when you return from the function, throw an exception, or simply let the function reach its end, the system uses that information to return to the previous point of execution, then removes that entry from the call stack.

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

So, before you call your function, you might have on the stack:

因此,在调用函数之前,您可能已经在堆栈中:

.                           .
.                           .
.                           .
|                           |
+---------------------------+
| performComplexOperation() |
| return address            |
| local variables           |
| ...                       |
+---------------------------+

then, you call the LogString function:

然后,你调用LogString函数:

.                           .
.                           .
.                           .
|                           |
+---------------------------+
| performComplexOperation() |
| return address            |
| local variables           |
| ...                       |
+---------------------------+
| LogString()               |
| return address            |
| local variables           |
| ...                       |
+---------------------------+

But after LogString completes, the code will return to performComplexOperation using the return address. The entry for LogString will be removed:

但是在LogString完成之后,代码将使用返回地址返回performComplexOperation。将删除LogString的条目:

.                           .
.                           .
.                           .
|                           |
+---------------------------+
| performComplexOperation() |
| return address            |
| local variables           |
| ...                       |
+---------------------------+
       (this is where
    LogString used to be)

In a stack overflow, the space taken by the entries on the stack will exceed the space alloted for the stack by the language you're using*, and the program won't be able to create the entry for the function you're about to call.

在堆栈溢出中,堆栈上的条目占用的空间将超过您正在使用的语言分配给堆栈的空间*,并且程序将无法为您所关注的函数创建条目打电话。

* Not exactly, but close enough.

*不完全,但足够接近。

#8


Only problem I could think of is if you have something crazy going on in this function which you do not list code for filePathFilenameExists()

我能想到的唯一问题是,如果你在这个函数中有一些疯狂的东西,你没有列出filePathFilenameExists()的代码

#9


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

#10


Something else is funny here because there's basically no way the code you've posted can cause a stack overflow.

还有一件事很有趣,因为你发布的代码基本上没有办法导致堆栈溢出。

  • What's the rest of your call stack look like when the stack overflow exception is thrown?
  • 当抛出堆栈溢出异常时,调用堆栈的其余部分是什么样的?

  • Is filePathFileName a field or a property? If it's a property, are you doing anything in your getter? Perhaps calling LogString(String) again?
  • filePathFileName是字段还是属性?如果它是一个属性,你在吸气器中做什么吗?也许再次调用LogString(String)?

#11


I think Paul Fisher is right but this is my first post and I don't have the rep to comment on his answer.

我认为保罗费舍尔是对的,但这是我的第一篇文章,我没有代表评论他的回答。

The same theory in my words; your calling function is causing the stack overflow. It pushes copies of itself onto the stack until it's a relatively short distance from the end. On some "N-1"th recursive iteration of the calling function, the stack depth of your LogString is enough to cause the overflow, since you'd then be within a pretty short distance from the end of the stack. filePathFilenameExists() likely has a deeper maximum stack than most of the other methods in the calling function and is enough to single LogString out as the lucky guy who catches the exception.

用我的话说同样的理论;你的调用函数导致堆栈溢出。它将自身的副本推送到堆栈,直到距离末端相对较短的距离。在调用函数的某些“N-1”递归迭代中,LogString的堆栈深度足以导致溢出,因为您距离堆栈末尾的距离非常短。 filePathFilenameExists()可能比调用函数中的大多数其他方法具有更深的最大堆栈,并且足以将单个LogString单独作为捕获异常的幸运者。

Theory aside, your problem should be apparent by the output of LogString, assuming it's being called repetitively from somewhere. That and viewing your stack in the IDE debugger.

除了理论之外,你的问题应该通过LogString的输出显而易见,假设它是从某个地方重复调用的。那并在IDE调试器中查看您的堆栈。

#12


This is the function filePathFileNameExists that Sean thought was recursive.

这是Sean认为是递归的函数filePathFileNameExists。

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

#13


Thanks you everyone for the inputs and sorry for the hustle. It was my fault because my program has recursion which I didn't know. The reason I didn't find that out because I didn't continue to trace the calls until the program stopped. I always stopped the debug when the last call was made to that last case scenario(last function) until last night I continued to debug until the program stopped itself then I saw there were many calls were made back and forth so based on my little programming experience I conclude that is recursion.

感谢大家的投入,并为喧嚣感到抱歉。这是我的错,因为我的程序有递归,我不知道。之所以我没有发现这一点,是因为在程序停止之前我没有继续跟踪调用。我总是在最后一次调用最后一个案例(最后一个函数)时停止调试,直到昨晚我继续调试,直到程序停止自身,然后我看到有很多调用来自于我的小编程经验我得出的结论是递归。