1、There are several techniques available for memory management in C. Many of them are used in NetHack 3.4.3; and even more are used somewhere in NetHack 4. In this blog post, Id like to look at them and discuss their advantages and disadvantages. Im mostly concerned about correctness, rather than effi
2、ciency, here; that means that unless the performance difference is very large, I care more about clean code than I do about fast code.Techniques for allocating fixed amounts of memoryThere are basically two different issues here: tracking the lifetime of memory allocations, and ensuring that theyre
3、the right size. As such, Ill start by looking at allocations whose size is statically known at compile time, and then move on to techniques that can handle the size being unknown.Fixed-size buffers on the stackProbably the simplest way to allocate memory in C is to use a stack allocation; if a varia
4、ble is declared inside a function without specifying static or extern, enough memory for that variable will be allocated when that variables scope starts, and freed again when that variables scope ends (sometimes known as an automatic variable). Because scopes in C are well-nested, a typical C imple
5、mentation will implement this via using a stack of memory; newly allocated variables will be given space from the top of the stack, and that space will be released from the top of the stack when they leave scope. (Top is in quotes because its far from rare for a stack to be upside-down in C, with th
6、e top being the end with the lowest memory address.)There are some major advantages (and some minor advantages) of doing things this way: It does not require any extra state, beyond that provided by the implementation, to work. Quite often, memory allocation schemes will themselves need memory to wo
7、rk, leading to something of an infinite regress. Stack allocations are often useful to provide that memory. Unlike the vast majority of memory allocation schemes, it is exception-safe with no extra effort: longjmp is aware of stack allocations, and will automatically free them from any scope it jump
8、s out of. (Some care is needed in the function containing setjmp itself; any memory that needs to persist past a longjmp must be marked as volatile, a requirement set by the standard in order to allow compilers to optimize around longjmp calls more easily. Technically speaking, the memory does not n
9、eed to be marked as volatile if its value does not change bewtween setjmp and the matching longjmp; in practice, although compilers can handle this case, they tend to spew warnings, so it makes sense to mark the memory as volatile if it is read after the longjmp at all.)This mostly does not matter i
10、n NetHack 3.4.3, which does not use any exception-like behaviour (if something goes wrong, it calls panic which attempts to produce a save file, and then exits the entire process, freeing all the memory that way). However, NetHack 4 uses exception-like behaviour internally (implemented using longjmp
11、 and setjmp, because C does not have true exceptions); NitroHack (and thus NetHack 4) uses exceptions in order to exit a game without exiting the process. From NetHack 4.3 (the current development version, which is unreleased but available using the savebreak branch of the repository) onwards, they
12、are used even more heavily, handling situations like rewinding the game mid-turn. It works correctly in the presence of multithreading, recursion, and asynchronous signals. This technique does not require any global state apart from that managed by the C compiler; thus, it is impossible to, for inst
13、ance, forget to save a variable stored in a stack allocation (unless the save code is running inside a scope containing it), or to have it accidentally persist from one game into the next. If your code does end up overflowing a fixed-sized buffer, your program is going to be incorrect no matter what
14、, but if that buffer is allocated on the stack, it maximizes the chance that your compiler will be able to generate code to catch the buffer overflow (especially if the overflow happens in a call to a standard string manipulation function like sprintf or strcat). It is incredibly unwise to rely on this to protect your code from exploits, but its a rather helpful feature for debugging. (Statically allocated memory is almost as good in this respect, but dynamic allocations are hopeless.)It is thus no surprise that stack alloc
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1