Hoisting/Reordering in C, C++ and Java: Must variable declarations always be on top in a context?
The compiler may reorder/modify your code as it wishes, as long as the modified code is equivalent to the original if executed sequentially. So hoisting is allowed, but not required. This is an optimization and it is completely compiler specific.
Variable declarations in C++ can be wherever you wish. In C they used to have to be on top in a context, but when the c99 standard was introduced, the rules were relaxed and now they can be wherever you want, similarly to c++. Still, many c programmers stick to putting them on top in a context.
In your example, the compiler is free to move the if statements to the top, but I don't think it would. These variables are just pointers that are declared on stack and are un-initialized, the cost of declaring them is minimal, moreover it might be more efficient to create them at the beginning of the function, rather than after the asserts.
If your declarations would involve any side-effects, for example
struct node *a = some_function();
then compiler would be limited in what it can reorder.
Edit:
I checked GCC's loop hoisting in practice with this short program:
#include <stdio.h>
int main(int argc, char **argv) {
int dummy = 2 * argc;
int i = 1;
while (i<=10 && dummy != 4)
printf("%d\n", i++);
return 0;
}
I've compiled it with this command:
gcc -std=c99 -pedantic test.c -S -o test.asm
This is the output:
.file "test.c"
.def ___main; .scl 2; .type 32; .endef
.section .rdata,"dr"
LC0:
.ascii "%d\12\0"
.text
.globl _main
.def _main; .scl 2; .type 32; .endef
_main:
LFB7:
.cfi_startproc
pushl %ebp
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, %ebp
.cfi_def_cfa_register 5
andl $-16, %esp
subl $32, %esp
call ___main
movl 8(%ebp), %eax
addl %eax, %eax
movl %eax, 24(%esp)
movl $1, 28(%esp)
jmp L2
L4:
movl 28(%esp), %eax
leal 1(%eax), %edx
movl %edx, 28(%esp)
movl %eax, 4(%esp)
movl $LC0, (%esp)
call _printf
L2:
cmpl $10, 28(%esp)
jg L3
cmpl $4, 24(%esp)
jne L4
L3:
movl $0, %eax
leave
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
.cfi_endproc
LFE7:
.ident "GCC: (GNU) 4.8.2"
.def _printf; .scl 2; .type 32; .endef
Then I've compiled it with this command:
gcc -std=c99 -pedantic test.c -O3 -S -o test.asm
This is the output:
.file "test.c"
.def ___main; .scl 2; .type 32; .endef
.section .rdata,"dr"
LC0:
.ascii "%d\12\0"
.section .text.startup,"x"
.p2align 4,,15
.globl _main
.def _main; .scl 2; .type 32; .endef
_main:
LFB7:
.cfi_startproc
pushl %ebp
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, %ebp
.cfi_def_cfa_register 5
pushl %ebx
andl $-16, %esp
subl $16, %esp
.cfi_offset 3, -12
call ___main
movl 8(%ebp), %eax
leal (%eax,%eax), %edx
movl $1, %eax
cmpl $4, %edx
jne L8
jmp L6
.p2align 4,,7
L12:
movl %ebx, %eax
L8:
leal 1(%eax), %ebx
movl %eax, 4(%esp)
movl $LC0, (%esp)
call _printf
cmpl $11, %ebx
jne L12
L6:
xorl %eax, %eax
movl -4(%ebp), %ebx
leave
.cfi_restore 5
.cfi_restore 3
.cfi_def_cfa 4, 4
ret
.cfi_endproc
LFE7:
.ident "GCC: (GNU) 4.8.2"
.def _printf; .scl 2; .type 32; .endef
So basically, with optimization turned on the original code was transformed to something like this:
#include <stdio.h>
int main(int argc, char **argv) {
int dummy = 2 * argc;
int i = 1;
if (dummy != 4)
while (i<=10)
printf("%d\n", i++);
return 0;
}
So, as you can see, there is indeed hoisting in C.