79

While coding in C, I came across the below situation.

int function ()
{
  if (!somecondition) return false;

  internalStructure  *str1;
  internalStructure *str2;
  char *dataPointer;
  float xyz;

  /* do something here with the above local variables */    
}

Considering the if statement in the above code can return from the function, I can declare the variables in two places.

  1. Before the if statement.
  2. After the if statement.

As a programmer, I would think to keep the variable declaration after if Statement.

Does the declaration place cost something? Or is there some other reason to prefer one way over the other?

Deduplicator
  • 44,692
  • 7
  • 66
  • 118
Whoami
  • 13,930
  • 19
  • 84
  • 140
  • 6
    As far as I understand, allocating stack space is just incrementing the stack pointer. How much stack space a function needs is determined at compile time, by looking at all variable declarations. Initializing a variable is something that happens at runtime, so that will affect performance. – Pieter Witvoet Jan 01 '15 at 10:54
  • 3
    Look at assembler code generated for both options - it should give You a clear answer. Just make sure You're using the same compiler optimization settings as for release build. – kestasx Jan 01 '15 at 15:31
  • 69
    If you care about performance then you should have some way to measure the performance of your application. If you have the ability to measure performance then you can answer your own question: try it both ways and you will soon know. If you don't have any method for measuring performance but you care about performance then your question should be "how do I set up tools to measure my performance?" – Eric Lippert Jan 01 '15 at 16:50
  • 1
    Can you do that in **C**? – Salman A Jan 02 '15 at 20:06
  • @SalmanA If we're talking C99 yes, if C90 or older it needs an extra scoping block. (or compiler extensions ...) – johannes Apr 16 '15 at 10:57
  • @EricLippert in practice every performance oriented question can be answered as such. I think he is mostly interested in the theory behind more than the practice. On the other hand this does seem to be a practical issue more than a theoretical. – g24l Dec 05 '15 at 00:00
  • 2
    @g24l: The fact that most questions can be answered the same way indicates why such questions are a bad fit for this site. Right up there with "what does this code do?" questions; you wrote the code, run it and see what it does, and then you'll know. – Eric Lippert Dec 05 '15 at 00:35
  • @EricLippert true one can do that, but let me stress the point here that is what one would expect in theory, and that is what differentiates from evaluating it in practice, which would make it compiler/system/architecture/compilation options specific. That is if there is an answer? – g24l Dec 05 '15 at 00:54

12 Answers12

99

In C99 and later (or with the common conforming extension to C89), you are free to mix statements and declarations.

Just as in earlier versions (only more so as compilers got smarter and more aggressive), the compiler decides how to allocate registers and stack, or do any number of other optimizations conforming to the as-if-rule.
That means performance-wise, there's no expectation of any difference.

Anyway, that was not the reason such was allowed:

It was for restricting scope, and thus reducing the context a human must keep in mind when interpreting and verifying your code.

Deduplicator
  • 44,692
  • 7
  • 66
  • 118
  • 1
    thanks for your reply, and to all. If i understand correctly your statement, it does not cost any performance issue, but brings out the clarity. Is that right ? – Whoami Jan 01 '15 at 10:37
  • 5
    If you want to see the optimizer in action, write a function with declarations which initialize like `char *foo = "something"`, compile your code with optimizer and debugging flags (`gcc -O3 -g` for example) and step through the function in the debugger. The step point will bounce around and delay initializing the variable until it needs it. – Schwern Jan 02 '15 at 04:08
  • 6
    `+1` for noting the importance of reducing brain-power overhead here... many people forget about it. –  Jan 03 '15 at 13:35
46

Do whatever makes sense, but current coding style recommends putting variable declarations as close to their usage as possible

In reality, variable declarations are free on virtually every compiler after the first one. This is because virtually all processors manage their stack with a stack pointer (and possibly a frame pointer). For example, consider two functions:

int foo() {
    int x;
    return 5; // aren't we a silly little function now
}

int bar() {
    int x;
    int y;
    return 5; // still wasting our time...
}

If I were to compile these on a modern compiler (and tell it not to be smart and optimize out my unused local variables), I'd see this (x64 assembly example.. others are similar):

foo:
push ebp
mov  ebp, esp
sub  esp, 8    ; 1. this is the first line which is different between the two
mov  eax, 5    ; this is how we return the value
add  esp, 8    ; 2. this is the second line which is different between the two
ret

bar:
push ebp
mov  ebp, esp
sub  esp, 16    ; 1. this is the first line which is different between the two
mov  eax, 5     ; this is how we return the value
add  esp, 16    ; 2. this is the second line which is different between the two
ret

Note: both functions have the same number of opcodes!

This is because virtually all compilers will allocate all of the space they need up front (barring fancy things like alloca which are handled separately). In fact, on x64, it is mandatory that they do so in this efficient manner.

(Edit: As Forss pointed out, the compiler may optimize some of the local variables into registers. More technically, I should be arguing that the first varaible to "spill over" into the stack costs 2 opcodes, and the rest are free)

For the same reasons, compilers will collect all of the local variable declarations, and allocate space for them right up front. C89 requires all declarations to be up-front because it was designed to be a 1 pass compiler. For the C89 compiler to know how much space to allocate, it needed to know all of the variables before emitting the rest of the code. In modern languages, like C99 and C++, compilers are expected to be much smarter than they were back in 1972, so this restriction is relaxed for developer convenience.

Modern coding practices suggest putting the variables close to their usage

This has nothing to do with compilers (which obviously could not care one way or another). It has been found that most human programmers read code better if the variables are put close to where they are used. This is just a style guide, so feel free to disagree with it, but there is a remarkable consensus amongst developers that this is the "right way."

Now for a few corner cases:

  • If you are using C++ with constructors, the compiler will allocate the space up front (since it's faster to do it that way, and doesn't hurt). However, the variable will not be constructed in that space until the correct location in the flow of the code. In some cases, this means putting the variables close to their use can even be faster than putting them up front... flow control might direct us around the variable declaration, in which case the constructor doesn't even need to be called.
  • alloca is handled on a layer above this. For those who are curious, alloca implementations tend to have the effect of moving the stack pointer down some arbitrary amount. Functions using alloca are required to keep track of this space in one way or another, and make sure the stack pointer gets re-adjusted upwards before leaving.
  • There may be a case where you usually need 16-bytes of stack space, but on one condition you need to allocate a local array of 50kB. No matter where you put your variables in the code, virtually all compilers will allocate 50kB+16B of stack space every time the function gets called. This rarely matters, but in obsessively recursive code this could overflow the stack. You either have to move the code working with the 50kB array into its own function, or use alloca.
  • Some platforms (ex: Windows) need a special function call in the prologue if you allocate more than a page worth of stack space. This should not change analysis very much at all (in implementation, it is a very fast leaf function that just pokes 1 word per page).
Cort Ammon
  • 10,221
  • 31
  • 45
  • Please be explicit and say C89, ISO C90, K&R C, etc. " requires all declarations to be up-front because it was designed to be a 1 pass compiler." You obviously know the difference because you call out C99 as an exception, but C!=C89. Arguably, right now, C=C11. – Jeff Hammond Jan 01 '15 at 21:56
  • Note also that one can use {} to mix declarations and code in C89, provided the scoping works. – Jeff Hammond Jan 01 '15 at 22:00
  • @Jeff: Ahh, I did not know that you could use braces in such a way. Guess that's what I get for being a C++ person trying to answer using C terminology! – Cort Ammon Jan 01 '15 at 22:03
  • C++ is a superset of C89, so you should be okay most of the time. C99 will get you though, because it's not a strict subset of C++03. – Jeff Hammond Jan 01 '15 at 22:06
  • "In reality, variable declarations are free on virtually every compiler after the first one." Is this true if you consider using more variables than the compiler can optimize into registers? – Forss Jan 02 '15 at 23:39
  • @Forss: Good point, I failed to account for compilers optimizing variables into registers. In that case, the first variable *which does not optimize into a register* costs 2 opcodes (one in prologue, one in epilog), and the rest are free thereafter. I'll update the wording to account for this. There's also a hit on some OSs where you have to call a function if you want to use more than 1 page of stack space in a single function (1 function call plus generally 3 opcodes per page used), but that's in the noise. – Cort Ammon Jan 03 '15 at 00:01
  • Wouldn't both function compile to `mov eax, 5` after optimizations? At least that was gcc/clang does. @Forss at the end most compilers use some form of SSA and CSE so the number of variables in code does not matter (except for debug builds) - only the number of subexpressions and how long they are live. – Maciej Piechotka Jan 03 '15 at 19:12
  • @MaciejPiechotka: Yes, they would, if we were optimizing them. I pointed out that the examples were unoptimized. It would be trivial to write a longer version which *did* require stack-frame allocations for local variables, but the example would be longer, harder to read, and no more informative, so I'm hand-waving away such optimizations – Cort Ammon Jan 03 '15 at 21:27
  • This is the only answer here that addresses the OP's question. I am puzzled by all the FUD comments. – user3344003 Jan 04 '15 at 16:54
  • @user3344003: I think the "FUD" comments are because my examples refer to a very specific aspect of modern computing, and so precision of language is actually quite important. Otherwise, while answering the OP's question, I could confuse some poor soul who got here from Google because I claimed something in my answer that was incorrect, but Google picked it up and indexed it. – Cort Ammon Jan 04 '15 at 19:01
  • @user3344003: The only one addressing the OPs question? I beg to differ. It's a good writeup of lots of the gritty details going into "no code-difference expected". Though putting a bit more emphasis on the one difference there is according to studies, and why it probably is there, would not hurt. Certainly worth an upvote too. – Deduplicator Jan 06 '15 at 22:08
  • 1
    @Jeff C++ is not a superset of C89. – L. F. Sep 14 '19 at 02:54
21

In C, I believe all variable declarations are applied as if they were at the top of the function declaration; if you declare them in a block, I think it's just a scoping thing (I don't think it's the same in C++). The compiler will perform all optimizations on the variables, and some may even effectively disappear in the machine code in higher optimizations. The compiler will then decide how much space is needed by the variables, and then later, during execution, create a space known as the stack where the variables live.

When a function is called, all of the variables that are used by your function are put on the stack, along with information about the function that is called (i.e. the return address, parameters, etc.). It doesn't matter where the variable was declared, just that it was declared - and it will be allocated onto the stack, regardless.

Declaring variables isn't "expensive," per se; if it's easy enough to be not used as a variable, the compiler will probably remove it as a variable.

Check this out:

Da stack

Wikipedia on call stacks, Some other place on the stack

Of course, all of this is implementation-dependent and system-dependent.

user12205
  • 2,684
  • 1
  • 20
  • 40
Jeremy Rodi
  • 2,485
  • 2
  • 21
  • 40
  • How would that work with VLAs? Or completely eliminating the variable / it's stack space? Or aliasing slots? – Deduplicator Jan 01 '15 at 11:15
  • @Deduplicator Apparently, it increases the [size of the stack frame](http://image.slidesharecdn.com/tlpi-7-memory-allocation-120715001411-phpapp02/95/tlpi-7-memory-allocation-16-728.jpg?cb=1342350863) (according to [this](http://www.slideshare.net/shuyufu/tlpi-7memoryallocation) presentation). It says `alloca`, but the two [are linked](https://gcc.gnu.org/onlinedocs/gcc-4.9.2/gcc/Variable-Length.html#Variable-Length). `alloca` allocates space from the stack. Reminder: those are implementation defined. – Jeremy Rodi Jan 01 '15 at 11:23
  • Just thought to prod you with some constructs your explanation would preclude just now, so you can refine it. – Deduplicator Jan 01 '15 at 11:26
  • @Deduplicator I'm sorry? I don't think I understand what you're trying to say. – Jeremy Rodi Jan 01 '15 at 11:28
  • Please note that the stack traditionally grows DOWNWARD, not upward as in the Wikipedia drawing! The reason is that the heap grows from the bottom upward (above the global variables) so the stack and heap meet in the middle, giving each as much memory as possible. I am amazed by this error in Wikipedia. (Just check a call instruction on Intel: SP is decremented, not incremented.) – Paul Ogilvie Jan 01 '15 at 13:17
  • @PaulOgilvie Isn't that also implementation-defined? – Jeremy Rodi Jan 01 '15 at 13:18
  • 6
    @Paul It's not unusual to draw the stack "reversed" if you so wish (the top of the stack being at the top of the paper has obvious advantages if you draw by hand). And in any case you might want to tell HP that they've been doing it wrong all those years (PA-RISC traditionally has the stack growing upwards ;) Heck if you don't have `push` and `pop` operations (do any RISC ISAs actually?) it's just a convention - MULTICS had upward growing stacks. – Voo Jan 01 '15 at 13:20
12

Yes, it can cost clarity. If there is a case where the function must do nothing at all on some condition, (as when finding the global false, in your case), then placing the check at the top, where you show it above, is surely easier to understand - something that is essential while debugging and/or documenting.

Martin James
  • 24,453
  • 3
  • 36
  • 60
11

It ultimately depends on the compiler but usually all locals are allocated at the beginning of the function.

However, the cost of allocating local variables is very small as they are put on the stack (or are put in a register after optimization).

Brainstorm
  • 1,110
  • 8
  • 13
  • "all locals are allocated at the beginning of the function". do you mean if i use lazy approach also, does it get allocated ? – Whoami Jan 01 '15 at 10:40
  • Probably yes. It's up to the compiler how to do it but the easiest way is to allocate all at the beginning and deallocate all att the end. – Brainstorm Jan 01 '15 at 10:43
  • 2
    However the allocation is just a single add instruction so it is incredably cheep. Not at all like using malloc. – Brainstorm Jan 01 '15 at 10:45
  • But, looking at all other engineers reply, it seems to be no performance or any other expenses in any manner except readability? :) – Whoami Jan 01 '15 at 10:45
6

Keep the declaration as close to where it's used as possible. Ideally inside nested blocks. So in this case it would make no sense to declare the variables above the if statement.

bitmask
  • 32,434
  • 14
  • 99
  • 159
6

The best practice is to adapt a lazy approach, i.e., declare them only when you really need them ;) (and not before). It results in the following benefit:

Code is more readable if those variables are declared as near to the place of usage as possible.

CinCout
  • 9,486
  • 12
  • 49
  • 67
  • 5
    This is incorrect (at least for me). I find that code is much more readable (and editable - I don't have to go hunting through the code to change a declaration) if all variables are declared in a block at the top of the function, rather than scattered through the code. And as others have pointed out, compilers are smart enough to optimize allocations. – jamesqf Jan 01 '15 at 17:57
  • 2
    For you, maybe; but not for the majority of the coding community! – CinCout Jan 02 '15 at 04:50
  • 4
    This is incorrect, period. The example uses variables that are statically allocated on the stack. The compiler will generate an instruction to reserve memory on the stack for those local variables no matter where they are are declared within that function. – Mark E. Haase Jan 02 '15 at 05:51
  • @binaryBaBa Your second point "You end up not allocating memory for the variables at an earlier stage" is not related to code readability, and as others already mentioned, I don't think it's correct. – T J Jan 02 '15 at 08:26
  • Yeah it makes sense to me given the improved compilers now-a-days. Edited accordingly. – CinCout Jan 02 '15 at 08:28
5

If you have this

int function ()
{
   {
       sometype foo;
       bool somecondition;
       /* do something with foo and compute somecondition */
       if (!somecondition) return false;
   }
   internalStructure  *str1;
   internalStructure *str2;
   char *dataPointer;
   float xyz;

   /* do something here with the above local variables */    
}

then the stack space reserved for foo and somecondition can be obviously reused for str1etc., so by declaring after the if, you may save stack space. Depending on the optimization capabilities of the compiler, the saving of stack space may also take place if you flatten the fucntion by removing the inner pair of braces or if you do declare str1 etc. before the if; however, this requires the compiler/optimizer to notice that the scopes do not "really" overlap. By positining the declarations after the if you facilitate this behaviour even without optimization - not to mention the improved code readability.

Hagen von Eitzen
  • 2,109
  • 21
  • 25
5

Whenever you allocate local variables in a C scope (such as a functions), they have no default initialization code (such as C++ constructors). And since they're not dynamically allocated (they're just uninitialized pointers), no additional (and potentially expensive) functions need to be invoked (e.g. malloc) in order to prepare/allocate them.

Due to the way the stack works, allocating a stack variable simply means decrementing the stack pointer (i.e. increasing the stack size, because on most architectures, it grows downwards) in order to make room for it. From the CPU's perspective, this means executing a simple SUB instruction: SUB rsp, 4 (in case your variable is 4 bytes large--such as a regular 32-bit integer).

Moreover, when you declare multiple variables, your compiler is smart enough to actually group them together into one large SUB rsp, XX instruction, where XX is the total size of a scope's local variables. In theory. In practice, something a little different happens.

In situations like these, I find GCC explorer to be an invaluable tool when it comes to finding out (with tremendous ease) what happens "under the hood" of the compiler.

So let's take a look at what happens when you actually write a function like this: GCC explorer link.

C code

int function(int a, int b) {
  int x, y, z, t;

  if(a == 2) { return 15; }

  x = 1;
  y = 2;
  z = 3;
  t = 4;

  return x + y + z + t + a + b;
}

Resulting assembly

function(int, int):
    push    rbp
    mov rbp, rsp
    mov DWORD PTR [rbp-20], edi
    mov DWORD PTR [rbp-24], esi
    cmp DWORD PTR [rbp-20], 2
    jne .L2
    mov eax, 15
    jmp .L3
.L2:
    -- snip --
.L3:
    pop rbp
    ret

As it turns out, GCC is even smarter than that. It doesn't even perform the SUB instruction at all to allocate the local variables. It just (internally) assumes that the space is "occupied", but doesn't add any instructions to update the stack pointer (e.g. SUB rsp, XX). This means that the stack pointer is not kept up to date but, since in this case no more PUSH instructions are performed (and no rsp-relative lookups) after the stack space is used, there's no issue.

Here's an example where no additional variables are declared: http://goo.gl/3TV4hE

C code

int function(int a, int b) {
  if(a == 2) { return 15; }
  return a + b;
}

Resulting assembly

function(int, int):
    push    rbp
    mov rbp, rsp
    mov DWORD PTR [rbp-4], edi
    mov DWORD PTR [rbp-8], esi
    cmp DWORD PTR [rbp-4], 2
    jne .L2
    mov eax, 15
    jmp .L3
.L2:
    mov edx, DWORD PTR [rbp-4]
    mov eax, DWORD PTR [rbp-8]
    add eax, edx
.L3:
    pop rbp
    ret

If you take a look at the code before the premature return (jmp .L3, which jumps to the cleanup and return code), no additional instructions are invoked to "prepare" the stack variables. The only difference is that the function parameters a and b, which are stored in the edi and esi registers, are loaded onto the stack at a higher address than in the first example ([rbp-4] and [rbp - 8]). This is because no additional space has been "allocated" for the local variables like in the first example. So, as you can see, the only "overhead" for adding those local variables is a change in a subtraction term (i.e. not even adding an additional subtraction operation).

So, in your case, there is virtually no cost for simply declaring stack variables.

Andrei Bârsan
  • 3,473
  • 2
  • 22
  • 46
4

I prefer keeping the "early out" condition at the top of the function, in addition to documenting why we are doing it. If we put it after a bunch of variable declarations, someone not familiar with the code could easily miss it, unless they know they have to look for it.

Documenting the "early out" condition alone is not always sufficient, it is better to make it clear in the code as well. Putting the early out condition at the top also makes it easier to keep the document in sync with the code, for instance, if we later decide to remove the early out condition, or to add more such conditions.

Masked Man
  • 1
  • 7
  • 40
  • 80
4

If it actually mattered the only way to avoid allocating the variables is likely to be:

int function_unchecked();

int function ()
{
  if (!someGlobalValue) return false;
  return function_unchecked();
}

int function_unchecked() {
  internalStructure  *str1;
  internalStructure *str2;
  char *dataPointer;
  float xyz;

  /* do something here with the above local variables */    
}

But in practice I think you'll find no performance benefit. If anything a minuscule overhead.

Of course if you were coding C++ and some of those local variables had non-trivial constructors you would probably need to place them after the check. But even then I don't think it would help to split the function.

Persixty
  • 8,165
  • 2
  • 13
  • 35
1

If you declare variables after if statement and returned from the function immediately the compiler does not commitment memory in the stack.

  • @ThomasPapamilhos: Not so. The answer is actually undefined but in my experience most compilers will allocate the full space to complete the function on entry to avoid repeated moves of the stack pointer which gain almost nothing. – Persixty Jan 14 '15 at 13:09