6

I am just about finished reading K&R, and that is all the C that I know. All my compilation is done from Windows command line using MinGW, and I have no knowledge of advanced debugging methods (hence the "ghetto debug" comment in my 2nd program below).

I am trying to make a few small test programs to help me better understand how memory allocation works. These first couple programs do not use malloc or free, I just wanted to see how memory is allocated and de-allocated for standard arrays local to a function. The idea is that I watch my running processes RAM usage to see if it corresponds with what I understand. For this first program below, it does work as I expected. The alloc_one_meg() function allocates and initializes 250,000 4-byte integers, but that MB is de-allocated as soon as the function returns. So if I call that function 1000000 times in a row, I should never see my RAM usage go much above 1MB. And, it works.

#include <stdio.h>
#include <stdlib.h>

void alloc_one_meg() {
    int megabyte[250000];
    int i;
    for (i=0; i<250000; i++) {
        megabyte[i] = rand();
    }
}

main()
{
    int i;
    for (i=0; i<1000000; i++) {
        alloc_one_meg();
    }
}

For this second program below, the idea was to not allow the function to exit, to have 1000 copies of the same function running at once, which I accomplished with recursion. My theory was that the program would consume 1GB of RAM before it de-allocated it all after the recursion finished. However, it doesn't get past the 2nd loop through the recursion (see my ghetto debug comment). The program crashes with a pretty non-informative (to me) message (a Windows pop-up saying ____.exe has encountered a problem). Usually I can always get to the bottom of things with my ghetto debug method... but it's not working here. I'm stumped. What is the problem with this code? Thanks!

#include <stdio.h>
#include <stdlib.h>

int j=0;

void alloc_one_meg() {
    int megabyte[250000];
    int i;
    for (i=0; i<250000; i++) {
        megabyte[i] = rand();
    }
    j++;
    printf("Loop %d\n", j); // ghetto debug
    if (j<1000) {
        alloc_one_meg();
    }
}

main()
{
    alloc_one_meg();
}

Followup question posted here.

Community
  • 1
  • 1
The111
  • 5,757
  • 4
  • 39
  • 55
  • 2
    And now that you have a stack overflow crash, you have an even better understanding of memory. Funny how things work out like that. – Thomas Eding Dec 23 '11 at 01:27
  • 2
    Yup, I'm new to programming, but not new to the idea of testing. I'm an engineer and a heavy software USER, and I know very well that breaking things is the best way to understand them! :-) – The111 Dec 23 '11 at 01:32

4 Answers4

3

You're running into a stack overflow.

Local automatic storage variables (such as megabyte) are allocated on the stack, which has limited amount of space. malloc allocates on the heap, which allows much larger allocations.

You can read more here:

http://en.wikipedia.org/wiki/Stack_overflow

(I should note that the C language does not specify where memory is allocated - stack and heap are implementation details)

Pubby
  • 51,882
  • 13
  • 139
  • 180
  • 4
    How cool, I learned about stack overflow on stackoverflow.com. I should go post this on meta! Thanks for the explanation and the link. – The111 Dec 23 '11 at 00:31
  • malloc() doesn't have larger amount of space -- see the output from dumpbin and amount reserved for "heap." – Heath Hunnicutt Dec 23 '11 at 00:42
2

The size of the stack in a Windows program is usually around 1 MB, so on the second recursion, you're overflowing the stack. You shouldn't be allocating such large arrays on the stack, use malloc and free to allocate and deallocate the memory on the heap (there's no way to get around malloc for such sizes of arrays):

void alloc_one_meg() {
    int *megabyte = malloc(sizeof(int) * 250000); // allocate space for 250000
                                                  // ints on the heap
    int i;
    for (i=0; i<250000; i++) {
        megabyte[i] = rand();
    }
    j++;
    printf("Loop %d\n", j); // ghetto debug
    if (j<1000) {
        alloc_one_meg();
    }

    free(megabyte); // DO NOT FORGET THIS
}

That said, you can actually change the stack size of a program and make it bigger (though I'd only do so as an educational exercise, not in production code). For Visual Studio you can use the /F compiler option, and on linux you can use setrlimit(3). I'm not sure what to use with MinGW though.

Seth Carnegie
  • 73,875
  • 22
  • 181
  • 249
  • He said he didn't want to use malloc/free. – Pubby Dec 23 '11 at 00:32
  • Part of the larger purpose of this series of tests was to help me understand the purpose of malloc and free. I wanted to run a few tests first without them, then a few tests with them. I knew that local stack variables had life related to function enter/exit, while heap variables had life related to malloc/free, but I didn't realize the stack had such a small limit. So I already learned a big thing with this test. :-) – The111 Dec 23 '11 at 00:35
  • @Johnson yes, that is a good thing to learn. The stack is _very_ small compared to the heap, which is why you can't recurse very far (even with not many local variables) before you get a stack overflow – Seth Carnegie Dec 23 '11 at 00:35
  • @Seth Carnegie -- malloc() and free() are just as limited -- see the output of dumpbin on a binary compiled with defaults. He can't use them, either. What he can't not use is VirtualAlloc(). – Heath Hunnicutt Dec 23 '11 at 00:43
  • @HeathHunnicutt I think the heap is bigger than the stack – Seth Carnegie Dec 23 '11 at 01:19
  • @HeathHunnicutt, I've created a similar test using malloc and free, and sure enough I can get program memory usage (reported by Windows) up to 1GB no problem if I fail to call free. So the heap seems at least 1000x larger than the stack based on that. Or, maybe my (intentional, academic) memory leak is creating the illusion of a large heap? Or is that the same thing? – The111 Dec 23 '11 at 01:34
  • 1
    @Johnson it is the same thing, see [this thread](https://groups.google.com/group/microsoft.public.vc.language/browse_thread/thread/d4378ea01584348e) (ignore the stupid debate about the One True Meaning of the phrase "moral equivalent") which indicates that there is little if any difference between `malloc` and `VirtualAlloc` – Seth Carnegie Dec 23 '11 at 01:36
  • Thanks Seth. Followup question posted: http://stackoverflow.com/questions/8611433/memory-allocated-not-used-unless-initialized – The111 Dec 23 '11 at 01:43
1

The memory you are allocating via the recursive functional calls is allocated from the stack. All of the stack memory must be contiguous. When your process starts a thread, Windows will reserve a range of virtual memory address space for that thread's stack. The amount of memory to be reserved is specified in your EXE file's "PE header." PE stands for "Portable Executable."

Using the dumpbin utility included with Visual Studio, with itself (dumpbin.exe) as the input file:

dumpbin /headers dumpbin.exe

... there is some output, and then:

      100000 size of stack reserve
        2000 size of stack commit

The "100000" is a hexadecimal number equal to 1,048,576, so this represents around 1MB.

In other words, the operating system will only reserve a 1MB address range for the stack. When that address range is used up, Windows may or may not be able to allocate further consecutive memory ranges to increase the stack. The result depends on whether further contiguous address range is available. It is very unlikely to be available, due to the other allocations Windows made when the thread began.

To allocate a maximum amount of virtual memory under Windows, use the VirtualAlloc family of functions.

Heath Hunnicutt
  • 18,667
  • 3
  • 39
  • 62
0

StackOverflow. Is this a trick question?

jdigital
  • 11,926
  • 4
  • 34
  • 51
  • Nope, I'm just a noob. I'm learning very quickly that there is a lot about C that I need to know that I can't get from K&R (as I said, my only source of knowledge so far). Where is a good place to learn about the knowledge that would have prevented me from asking such a question? Stack/heap, that sort of thing (those concepts are not present in K&R, and I do realize why). – The111 Dec 23 '11 at 00:37