While coding in C, I came across the below situation.
int function ()
{
if (!somecondition) return false;
internalStructure *str1;
internalStructure *str
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.
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;
}
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
int function(int a, int b) {
if(a == 2) { return 15; }
return a + b;
}
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.