Detecting that the stack is full

2019-04-07 21:58发布

问题:

When writing C++ code I've learned that using the stack to store memory is a good idea.

But recently I ran into a problem:

I had an experiment that had code that looked like this:

void fun(const unsigned int N) {
    float data_1[N*N];
    float data_2[N*N];

    /* Do magic */
}

The code exploted with a seqmentation fault at random, and I had no idea why.

It turned out that problem was that I was trying to store things that were to big on my stack, is there a way of detecting this? Or at least detecting that it has gone wrong?

回答1:

float data_1[N*N];
float data_2[N*N];

These are variable length arrays (VLA), as N is not a constant expression. The const-ness in the parameter only ensures that N is read-only. It doesn't tell the compiler that N is constant expression.

VLAs are allowed in C99 only; in other version of C, and all versions of C++ they're not allowed. However, some compilers provides VLA as compiler-extension feature. If you're compiling with GCC, then try using -pedantic option, it will tell you it is not allowed.

Now why your program gives segfault, probably because of stack-overflow due to large value of N * N:


Consider using std::vector as:

#include <vector> 

void fun(const unsigned int N) 
{
  std::vector<float> data_1(N*N);
  std::vector<float> data_2(N*N);

  //your code
}


回答2:

It's extremely difficult to detect that the stack is full, and not at all portable. One of the biggest problems is that stack frames are of variable size (especially when using variable-length arrays, which are really just a more standard way of doing what people were doing before with alloca()) so you can't use simple proxies like the number of stack frames.

One of the simplest methods that is mostly portable is to put a variable (probably of type char so that a pointer to it is a char*) at a known depth on the stack and to then measure the distance from that point to a variable (of the same type) in the current stack frame by simple pointer arithmetic. Add in an estimate of how much space you're about to allocate, and you can have a good guess as to wether the stack is about to blow up on you. The problems with this are that you don't know the direction that the stack is growing in (no, they don't all grow in the same direction!) and working out the size of the stack space is itself rather messy (you can try things like system limits, but they're really quite awkward). Plus the hack factor is very high.

The other trick I've seen used on 32-bit Windows only was to try to alloca() sufficient space and handle the system exception that would occur if there was insufficient room.

int have_enough_stack_space(void) {
    int enough_space = 0;

    __try {           /* Yes, that's got a double-underscore. */
        alloca(SOME_VALUE_THAT_MEANS_ENOUGH_SPACE);
        enough_space = 1;
    }  __except (EXCEPTION_EXECUTE_HANDLER) {}

    return enough_space;
}

This code is very non-portable (e.g., don't count on it working on 64-bit Windows) and building with older gcc requires some nasty inline assembler instead! Structured exception handling (which this is a use of) is amongst the blackest of black arts on Windows. (And don't return from inside the __try construct.)



回答3:

Try using instead functions like malloc. It will return null explicitly, if it failed to find a block of memory of the size you requested.

Of course, in that case don't forget to free this memory in the end of function, after you are done.

Also, you can check the settings of your compiler, with what stack memory limit it generates the binaries.



回答4:

One of the reasons people say it is better to use stack instead of heap memory can be because of the fact that variables allocated on top of the stack will be popped out automatically when you leave the body of the function. For storing big blocks of information it is usual to use heap memory and other data structures like linked lists or trees. Also memories allocated on the stack is limited and much more less than you can allocate in the heap space. I think it is better to manage the memory allocation and releasing more carefully instead of trying to use stack for storing big data.

You can use framework which manage your memory allocations. As well you can use VDL to check your memory leaks and memories which is not released.



回答5:

is there a way of detecting this?

No, in general.

Stack size is platform depedent. Typically, Operating System decides the size of the stack. So you can check your OS (ulimit -s on linux) to see how much stack memory it allocates for your program.

If your compiler supports stackavail() then you can check it. It's better to go heap-allocated memory in situations where you are unsure whether you'd exceed the stack limit.