32

Like we do with macros:

#undef SOMEMACRO 

Can we also undeclare or delete the variables in C, so that we can save a lot of memory?

I know about malloc() and free(), but I want to delete the variables completely so that if I use printf("%d", a); I should get error

test.c:4:14: error: ‘a’ undeclared (first use in this function)
Kara
  • 6,115
  • 16
  • 50
  • 57
  • 19
    Use a tight scope around the variables (that's a pair of braces enclosing a sequence of statements). The variables are destroyed when the scope they're defined in is exited (and aren't created until the scope is entered). Otherwise, the answer's "No". Global variables can't be destroyed at all. – Jonathan Leffler Jul 29 '17 at 09:08
  • Any local variable when you leave it's scope stops to exist. In the global scope there is no way except the program termination – 0___________ Jul 29 '17 at 09:13
  • Related: https://stackoverflow.com/questions/2759371/in-c-do-braces-act-as-a-stack-frame – alk Jul 29 '17 at 10:32

5 Answers5

57

No, but you can create small minimum scopes to achieve this since all scope local variables are destroyed when the scope is exit. Something like this:

void foo() {
    // some codes
    // ...
    {    // create an extra minimum scope where a is needed
        int a;
    }
    // a doesn't exist here
}
taskinoor
  • 45,586
  • 12
  • 116
  • 142
  • you don't save any memory this way. All compilers that I know of pre-allocate the memory for all stack variables once in the beginning of the function. Scopes simply control the visibility of variables during compilation. – Mike Nakis Jul 29 '17 at 09:40
  • 9
    Actually this could allow the compiler to re-use stack variable space if you define variables that don't overlap in scope this way. Though it's quite possible for the compiler to do such an optimization even if you don't do that. – immortal Jul 29 '17 at 09:41
  • 1
    @immortal yes it could allow the compiler to re-use stack space, but there is a reason why compilers never did that, don't do that, and in all likelihood never will: the amount of memory saved would be very small, and the cost of adjusting the stack mid-function would be very real. – Mike Nakis Jul 29 '17 at 10:00
  • 18
    @MikeNakis - Have you ever worked with embedded systems? I once had a controller with only 128 byte stack and a compiler that quite seriously overlapped stack variables between functions. It caused runtime errors when it failed to properly figure out overlapping stack frames due to function pointer uses. Good times :) – immortal Jul 29 '17 at 10:10
  • 2
    @Mike Nakis Yes they usually allocate stack at the entry to the function. But showing the exact scope of the variable may help the compiler to optimize the use of registers. So if the variable is visible only in the limited scope, and it is not volatile, compiler will try to keep it in the register instead of allocating stack for it. – 0___________ Jul 29 '17 at 10:11
  • @immortal I personally do almost only embedded systems. What do you mean by `stack overlapping between functions`? `failed to properly figure out overlapping stack frames due to function pointer uses` ???????? – 0___________ Jul 29 '17 at 10:14
  • @PeterJ Maybe bad phrasing to a situation that functions had constant stack locations, and the compiler figured out which function calls can't overlap (sequentially) in runtime to assign constant locations for their variables in the same location. – immortal Jul 29 '17 at 10:16
  • @immortal Some examples please? I do not think that is a real situation. What is the `constant stack location`? Have you seen any hardware systems with the dynamic ones? (except different stacks for different execution contexts like privileged and unprivileged or hardware mechanisms for the thread switching) – 0___________ Jul 29 '17 at 10:22
  • @immortal interesting. But we are still not talking about saving "a lot of memory" as the OP imagines. – Mike Nakis Jul 29 '17 at 10:37
  • 12
    @MikeNakis: "*compilers never did that [re-use stack space], don't do that, and in all likelihood never will*" My GCC does reuse the stack like described here: https://stackoverflow.com/a/2759834/694576 Just tested this: `void foo(void) { { char a[1024 * 1024 * 4] = {0}; } { char b[1024 * 1024 * 4] = {0}; } }` works whereas `void foo(void) { char a[1024 * 1024 * 4] = {0}; char b[1024 * 1024 * 4] = {0}; }` doesn't. – alk Jul 29 '17 at 10:38
  • @PeterJ The compiler assigned a constant location on the 128 bytes stack area to each stack variable. A smaller section of the stack area was used only to store return addresses, while most of the stack area was fragmented into "stack" variables in constant locations. The "stack" variables weren't calculated as offsets from a stack pointer, but had hard-coded constant addresses. We had to introduce a link-map file to resolve conflicts the compiler missed due to function-pointer calls. – immortal Jul 29 '17 at 10:46
  • 10
    @MikeNakis Reusing stack space for variables whose scopes don't overlap doesn't require adjusting the stack pointer. It essentially treats that part of the stack frame like a `union`. – Barmar Jul 29 '17 at 15:33
  • 12
    The compiler doesn't need limited scope for reuse memory registers. It simply use the flow graph to reason about non overlapping lifetimes. It can even put the same variable in different locations/registers at different times (at least when the address isn't taken). Since compilers often use [SSA form](https://en.wikipedia.org/wiki/Static_single_assignment_form) during optimization passes, decoupling storage locations and variables comes very natural to a compiler. – CodesInChaos Jul 29 '17 at 17:25
  • 2
    @MikeNakis: To supplement alk's answer, gcc 4.7 is somewhat famous for introducing a stricter reuse policy of the stack space used for temporaries; broke a lot of programs relying on undefined behavior at the time of its release. I do agree the savings are not necessarily "big", but it does reduce the frame size. – Matthieu M. Jul 30 '17 at 11:14
  • @PeterJ Old FORTRAN compilers did similar things so it's not that surprising. Basically if you disallow recursion and do static linking you can assign all local variables in your whole program a static location. immortal's compiler seems to do something similar just a bit cleverer. – Voo Jul 30 '17 at 11:32
  • @MikeNakis It's not just gcc either. Java HotSpot does the exact same thing. Why waste valuable l1 cache if you can easily avoid it? And since basically every compiler uses SSA at some point the whole optimisation is basically free. – Voo Jul 30 '17 at 11:38
  • 2
    @MikeNakis: GCC, Clang and Visual Studio all do this. I've dug out coding bugs caused by this on all of them. They have been trying harder to do it in recent years, because of the increasing importance of saving memory on mobile platforms. None of them adjust the stack pointer: they just use the same space for different variables, depending on where you are in the function. – John Dallman Jul 30 '17 at 15:56
27

It's not a direct answer to the question, but it might bring some order and understanding on why this question has no proper answer and why "deleting" variables is impossible in C.

Point #1 What are variables?

Variables are a way for a programmer to assign a name to a memory space. This is important, because this means that a variable doesn't have to occupy any actual space! As long as the compiler has a way to keep track of the memory in question, a defined variable could be translated in many ways to occupy no space at all. Consider: const int i = 10; A compiler could easily choose to substitute all instances of i into an immediate value. i would occupy 0 data memory in this case (depending on architecture it could increase code size). Alternatively, the compiler could store the value in a register and again, no stack nor heap space will be used. There's no point in "undefining" a label that exists mostly in the code and not necessarily in runtime.

Point #2 Where are variables stored?

After point #1 you already understand that this is not an easy question to answer as the compiler could do anything it wants without breaking your logic, but generally speaking, variables are stored on the stack. How the stack works is quite important for your question. When a function is being called the machine takes the current location of the CPU's instruction pointer and the current stack pointer and pushes them into the stack, replacing the stack pointer to the next location on stack. It then jumps into the code of the function being called.

That function knows how many variables it has and how much space they need, so it moves the frame pointer to capture a frame that could occupy all the function's variables and then just uses stack. To simplify things, the function captures enough space for all it's variables right from the start and each variable has a well defined offset from the beginning of the function's stack frame*. The variables are also stored one after the other. While you could manipulate the frame pointer after this action, it'll be too costly and mostly pointless - The running code only uses the last stack frame and could occupy all remaining stack if needed (stack is allocated at thread start) so "releasing" variables gives little benefit. Releasing a variable from the middle of the stack frame would require a defrag operation which would be very CPU costly and pointless to recover few bytes of memory.

Point #3: Let the compiler do its job

The last issue here is the simple fact that a compiler could do a much better job at optimizing your program than you probably could. Given the need, the compiler could detect variable scopes and overlap memory which can't be accessed simultaneously to reduce the programs memory consumption (-O3 compile flag). There's no need for you to "release" variables since the compiler could do that without your knowledge anyway.

This is to complement all said before me about the variables being too small to matter and the fact that there's no mechanism to achieve what you asked.


* Languages that support dynamic-sized arrays could alter the stack frame to allocate space for that array only after the size of the array was calculated.

Matteo Italia
  • 123,740
  • 17
  • 206
  • 299
immortal
  • 3,118
  • 20
  • 38
  • 3
    Point #3 is particularly relevant; compilers routinely recycle the stack space for other variables once one is no longer needed, exactly as they do for registers; it is actually a thing you must take care when you do reverse engineering - even after you understand to what local variable a given position on the stack refers to, once it is overwritten it may as well be a completely different variable. – Matteo Italia Jul 29 '17 at 15:00
  • 1
    this should be the #1 answer here – Display Name Jul 29 '17 at 15:31
  • Many compilers, particularly for embedded applications, (XC8 for example) do not allow the higher levels of optimization unless you pay for a premium version. That being said, your point of "Let the compiler do its job" is still probably the best answer. – Charlie Jul 29 '17 at 21:18
17

There is no way to do that in C nor in the vast majority of programming languages, certainly in all programming languages that I know.

And you would not save "a lot of memory". The amount of memory you would save if you did such a thing would be minuscule. Tiny. Not worth talking about.

The mechanism that would facilitate the purging of variables in such a way would probably occupy more memory than the variables you would purge.

The invocation of the code that would reclaim the code of individual variables would also occupy more space than the variables themselves.

So if there was a magic method purge() that purges variables, not only the implementation of purge() would be larger than any amount of memory you would ever hope to reclaim by purging variables in your program, but also, in int a; purge(a); the call to purge() would occupy more space than a itself.

That's because the variables that you are talking about are very small. The printf("%d", a); example that you provided shows that you are thinking of somehow reclaiming the memory occupied by individual int variables. Even if there was a way to do that, you would be saving something of the order of 4 bytes. The total amount of memory occupied by such variables is extremely small, because it is a direct function of how many variables you, as a programmer, declare by hand-typing their declarations. It would take years of typing on a keyboard doing nothing but mindlessly declaring variables before you would declare a number of int variables occupying an amount of memory worth speaking of.

Mike Nakis
  • 56,297
  • 11
  • 110
  • 142
  • There's R, in which deleting a variable including name is possible. It's a scripting language though. – stefan Jul 29 '17 at 15:57
  • 3
    Also possible in Python, Matlab, etc... otherwise, this answer is quite far in not even wrong territory. – ojs Jul 30 '17 at 09:52
6

Well, you can use blocks ({ }) and defining a variable as late as possible to limit the scope where it exists.

But unless the variable's address is taken, doing so has no influence on the generated code at all, as the compiler's determination of the scope where it has to keep the variable's value is not significantly impacted.

If the variable's address is taken, failure of escape-analysis, mostly due to inlining-barriers like separate compilation or allowing semantic interpositioning, can make the compiler assume it has to keep it alive till later in the block than strictly neccessary. That's rarely significant (don't worry about a handful of ints, and most often a few lines of code longer keeping it alive are insignificant), but best to keep it in mind for the rare case where it might matter.

Deduplicator
  • 44,692
  • 7
  • 66
  • 118
2

If you are that concerned about the tiny amount of memory that is on the stack, then you're probably going to be interested in understanding the specifics of your compiler as well. You'll need to find out what it does when it compiles. The actual shape of the stack-frame is not specified by the C language. It is left to the compiler to figure out. To take an example from the currently accepted answer:

void foo() {
    // some codes
    // ...
    {    // create an extra minimum scope where a is needed
        int a;
    }
    // a doesn't exist here
}

This may or may not affect the memory usage of the function. If you were to do this in a mainstream compiler like gcc or Visual Studio, you would find that they optimize for speed rather than stack size, so they pre-allocate all of the stack space they need at the start of the function. They will do analysis to figure out the minimum pre-allocation needed, using your scoping and variable-usage analysis, but those algorithms literally wont' be affected by extra scoping. They're already smarter than that.

Other compilers, especially those for embedded platforms, may allocate the stack frame differently. On these platforms, such scoping may be the trick you needed. How do you tell the difference? The only options are:

  • Read the documentation
  • Try it, and see what works

Also, make sure you understand the exact nature of your problem. I worked on a particular embedded project which eschewed the stack for everything except return values and a few ints. When I pressed the senior developers about this silliness, they explained that on this particular application, stack space was at more of a premium than space for globally allocated variables. They had a process they had to go through to prove that the system would operate as intended, and this process was much easier for them if they allocated everything up front and avoided recursion. I guarantee you would never arrive at such a convoluted solution unless you first knew the exact nature of what you were solving.

As another solution you could look at, you could always build your own stack frames. Make a union of structs, where each struct contains the variables for one stack frame. Then keep track of them yourself. You could also look at functions like alloca, which can allow for growing the stack frame during the function call, if your compiler supports it.

Would a union of structs work? Try it. The answer is compiler dependent. If all variables are stored in memory on your particular device, then this approach will likely minimize stack usage. However, it could also substantially confuse register coloring algorithms, and result in an increase in stack usage! Try and see how it goes for you!

Cort Ammon
  • 10,221
  • 31
  • 45