I just write a simple program to learn c programming
1 #include<stdio.h>
2
3 int main()
4 {
5 int a = 5;
6 switch(a)
7 {
8 case 0:
9 {
10 ;
11 int a = 10;
12 printf("%d\n",a);
13 break;
14 }
15 default :
16 printf("%d",a);
17
18 }
19 return 0;
20 }
Output: 5
When I forgot the brace it turns to be:
8 case 0:
9
10 ;
11 int a = 10;
12 printf("%d\n",a);
13 break;
14
Output: 0
I'm little confused about this and try to compile and debug:
- 1 0x000000000040051c <+0>: push %rbp |- 1 0x000000000040051c <+0>: push %rbp
| 2 0x000000000040051d <+1>: mov %rsp,%rbp || 2 0x000000000040051d <+1>: mov %rsp,%rbp
| 3 0x0000000000400520 <+4>: sub $0x10,%rsp || 3 0x0000000000400520 <+4>: sub $0x10,%rsp
| 4 => 0x0000000000400524 <+8>: movl $0x5,-0x8(%rbp) || 4 => 0x0000000000400524 <+8>: movl $0x5,-0x8(%rbp)
| 5 0x000000000040052b <+15>: mov -0x8(%rbp),%eax || 5 0x000000000040052b <+15>: mov -0x8(%rbp),%eax
| 6 0x000000000040052e <+18>: test %eax,%eax || 6 0x000000000040052e <+18>: test %eax,%eax
| 7 0x0000000000400530 <+20>: jne 0x40054f <main+51> || 7 0x0000000000400530 <+20>: jne 0x40054f <main+51>
| 8 0x0000000000400532 <+22>: movl $0xa,-0x4(%rbp) || 8 0x0000000000400532 <+22>: movl $0xa,-0x4(%rbp)
9 0x0000000000400539 <+29>: mov -0x4(%rbp),%eax | 9 0x0000000000400539 <+29>: mov -0x4(%rbp),%eax
10 0x000000000040053c <+32>: mov %eax,%esi | 10 0x000000000040053c <+32>: mov %eax,%esi
11 0x000000000040053e <+34>: mov $0x400614,%edi | 11 0x000000000040053e <+34>: mov $0x400614,%edi
12 0x0000000000400543 <+39>: mov $0x0,%eax | 12 0x0000000000400543 <+39>: mov $0x0,%eax
13 0x0000000000400548 <+44>: callq 0x4003f0 <printf#plt> | 13 0x0000000000400548 <+44>: callq 0x4003f0 <printf#plt>
14 0x000000000040054d <+49>: jmp 0x400563 <main+71> | 14 0x000000000040054d <+49>: jmp 0x400563 <main+71>
15 0x000000000040054f <+51>: mov -0x8(%rbp),%eax | 15 0x000000000040054f <+51>: mov -0x4(%rbp),%eax
16 0x0000000000400552 <+54>: mov %eax,%esi | 16 0x0000000000400552 <+54>: mov %eax,%esi
17 0x0000000000400554 <+56>: mov $0x400618,%edi | 17 0x0000000000400554 <+56>: mov $0x400618,%edi
18 0x0000000000400559 <+61>: mov $0x0,%eax | 18 0x0000000000400559 <+61>: mov $0x0,%eax
19 0x000000000040055e <+66>: callq 0x4003f0 <printf#plt> | 19 0x000000000040055e <+66>: callq 0x4003f0 <printf#plt>
20 0x0000000000400563 <+71>: mov $0x0,%eax | 20 0x0000000000400563 <+71>: mov $0x0,%eax
21 0x0000000000400568 <+76>: leaveq | 21 0x0000000000400568 <+76>: leaveq
+ 22 +-- 2 lines: 0x0000000000400569 <+77>: retq ----------------------|+ 22 +-- 2 lines: 0x0000000000400569 <+77>: retq ---------------------
A little diffrent but vital:
$ diff with.txt without.txt
15c15
< 0x000000000040054f <+51>: mov -0x8(%rbp),%eax
---
> 0x000000000040054f <+51>: mov -0x4(%rbp),%eax
UPDATE:
I learn a lesson that gcc -Wall is always a good practice.
So you have to really work at getting to this point, as it won't compile without the extra semicolon in your code. Which makes me think this is contrived example made up for an interview or some such.
In your second example, the variable a (inner) is created at the switch braces { }, but it is not initialized at that level, since the initialization is only in the case 0: code. So the value of a is completely random (happens to be zero)
Either way, bad coding style! Don't forget to use braces in case-statements if you introduce variables. [In g++ you actually get an error "jump to case-label crosses initialization of 'int a']
So, first of all, gcc -Wall will give a warning for "uninitalized variable".
If we consider this example:
int a = 111;
int main()
{
int a = 2;
printf("a=%d\n", a);
}
I don't think anyone would argue the case of "which a do we mean", right?
Or if we have:
int main()
{
int x = 12;
int a = 11;
if(x == 12)
{
int a = 2;
printf("a=%d\n", a);
}
}
Again, it's pretty obvious what's going on here, right?
If we rewrite your code to show what actually happens:
int main()
{
int a = 5;
switch(a)
{
int a;
case 0:
a=10 ;
printf("%d\n",a);
break;
default :
printf("%d",a);
}
return 0;
}
Now, that's semantically the same thing as your code in the second variant. It just looks a bit different!
Without '{}', you just jump over the initialization of the inner variable 'a' (it still defined, though), so you get an uninitialized 'a', in this case, it is '0'.
BTW, with '{}', it should output '5'.
I appreciate the usage of assembly here in the post as
it helps to understand the code in a better way.
Now what happened here.
Lets discuss this issue in cases
case 1 :
#include<stdio.h>
int main()
{
int a=5;
switch(a)
{
case 0:
{
;
int a = 10;
printf("%d in case 0\n",a);
break;
;
}
default:
printf("%d in default case\n",a);
break;
}
return 0;
}
Case 1 Assembly:
.file "test1.c"
.section .rodata
.LC0:
.string "%d in case 0\n"
.LC1:
.string "%d in default case\n"
.text
.globl main
.type main, #function
main:
pushl %ebp
movl %esp, %ebp
andl $-16, %esp
subl $32, %esp
movl $5, 28(%esp)
movl 28(%esp), %eax
testl %eax, %eax
jne .L6
.L3:
movl $10, 24(%esp)
movl $.LC0, %eax
movl 24(%esp), %edx
movl %edx, 4(%esp)
movl %eax, (%esp)
call printf
jmp .L4
.L6:
movl $.LC1, %eax
movl 28(%esp), %edx
movl %edx, 4(%esp)
movl %eax, (%esp)
call printf
.L4:
movl $0, %eax
leave
ret
case 1 o/p:
root#local-host#./a.out
5 in default case
Explanation :
This is as expected because as you can see the statement in assembly
movl $5, 28(%esp)
we are copying / moving 5 to 28(%esp) location
movl 28(%esp), %eax
testl %eax, %eax its a switch equivalent
in main tag
here 28(%esp) is nothing but the value of a (int a=5;)
And at L3 all 24(%esp) is used to store the value 10
Please note that ebx register is used hereas new a.
And L6 is the default case now look at the following statement
movl 28(%esp), %edx
here 28(%esp) (which is nothing but value of a) is copied to o/p hence our o/p is as expected.
Case 2:
include
int main()
{
int a=5;
switch(a)
{
case 0:
;
int a = 10;
printf("%d in case 0\n",a);
break;
default:
printf("%d in default case\n",a);
break;
}
return 0;
}
Case 2 Assembly:
.file "test1.c"
.section .rodata
.LC0:
.string "%d in case 0\n"
.LC1:
.string "%d in default case\n"
.text
.globl main
.type main, #function
main:
pushl %ebp
movl %esp, %ebp
andl $-16, %esp
subl $32, %esp
movl $5, 28(%esp)
movl 28(%esp), %eax
testl %eax, %eax
jne .L6
.L3:
movl $10, 24(%esp)
movl $.LC0, %eax
movl 24(%esp), %edx
movl %edx, 4(%esp)
movl %eax, (%esp)
call printf
jmp .L4
.L6:
movl $.LC1, %eax
movl 24(%esp), %edx
movl %edx, 4(%esp)
movl %eax, (%esp)
call printf
.L4:
movl $0, %eax
leave
ret
Case 2 o/p:
root#local-host#./a.out
134513723 in default case
A garbage value
Let me explain here also the 28(%esp) is the value of a
Now take a close look at L3 and L6 labels
Here is what you find during the code at 24%esp again a is created only if the case 0 satisfies.
As the case of 0 is not satisfied the 24%esp will not get initialised i.e int a = 10;
And we are going to default which is L6 and we are trying to get 24%esp value which is not yet
initilised (but it is a valid location) as we havent went to case 0.
Thats why we are getting garbage value.
Case 3:
include
int main()
{
int a=5;
switch(a)
{
case 5:
;
int a = 10;
printf("%d in case 5\n",a);
default:
printf("%d in default case\n",a);
break;
}
return 0;
}
Case 3 assembly:
.file "test1.c"
.section .rodata
.LC0:
.string "%d in case 5\n"
.LC1:
.string "%d in default case\n"
.text
.globl main
.type main, #function
main:
pushl %ebp
movl %esp, %ebp
andl $-16, %esp
subl $32, %esp
movl $5, 28(%esp)
movl 28(%esp), %eax
cmpl $5, %eax
jne .L2
.L3:
movl $10, 24(%esp)
movl $.LC0, %eax
movl 24(%esp), %edx
movl %edx, 4(%esp)
movl %eax, (%esp)
call printf
.L2:
movl $.LC1, %eax
movl 24(%esp), %edx
movl %edx, 4(%esp)
movl %eax, (%esp)
call printf
movl $0, %eax
leave
ret
Case 3 o/p:
root#local-host#./a.out
10 in case 0
10 in default case
Here as you can see that the new a is created at 24(%esp) and initailised at case 5 so the value will be same
at default location.
In L3 label
movl 24(%esp), %edx
In L2 Label
movl 24(%esp), %edx
So same location 24(%esp) is used which is created and initialised at cse 5 and flow through the default.
Case 4:
include
int main()
{
int a=5;
switch(a)
{
case 0:
;
static int a = 10;
printf("%d in case 0\n",a);
break;
default:
printf("%d in default\n",a);
break;
}
return 0;
}
Case 4 Assembley:
.file "test1.c"
.section .rodata
.LC0:
.string "%d in case 0\n"
.LC1:
.string "%d in default\n"
.text
.globl main
.type main, #function
main:
pushl %ebp
movl %esp, %ebp
andl $-16, %esp
subl $32, %esp
movl $5, 28(%esp)
movl 28(%esp), %eax
testl %eax, %eax
jne .L6
.L3:
movl a.1706, %edx
movl $.LC0, %eax
movl %edx, 4(%esp)
movl %eax, (%esp)
call printf
jmp .L4
.L6:
movl a.1706, %edx
movl $.LC1, %eax
movl %edx, 4(%esp)
movl %eax, (%esp)
call printf
.L4:
movl $0, %eax
leave
ret
.size main, .-main
.data
.align 4
.type a.1706, #object
.size a.1706, 4
a.1706:
.long 10
.ident "GCC: (Ubuntu/Linaro 4.4.4-14ubuntu5.1) 4.4.5"
.section .note.GNU-stack,"",#progbits
Case 4 O/p:
root#local-host#./a.out
10 in default
This makes sense as the static type is a global and stored in the data section so
During execution the global value i.e the static int a is in data section and initialized as 10.
Case 5:
include
int main()
{
int a=5;
switch(a)
{
case 0:
{
;
static int a = 10;
printf("%d in case 0\n",a);
break;
}
default:
printf("%d in default\n",a);
break;
}
return 0;
}
Case 5 Assembly:
.file "test1.c"
.section .rodata
.LC0:
.string "%d in case 0\n"
.LC1:
.string "%d in default\n"
.text
.globl main
.type main, #function
main:
pushl %ebp
movl %esp, %ebp
andl $-16, %esp
subl $32, %esp
movl $5, 28(%esp)
movl 28(%esp), %eax
testl %eax, %eax
jne .L6
.L3:
movl a.1706, %edx
movl $.LC0, %eax
movl %edx, 4(%esp)
movl %eax, (%esp)
call printf
jmp .L4
.L6:
movl $.LC1, %eax
movl 28(%esp), %edx
movl %edx, 4(%esp)
movl %eax, (%esp)
call printf
.L4:
movl $0, %eax
leave
ret
.size main, .-main
.data
.align 4
.type a.1706, #object
.size a.1706, 4
a.1706:
.long 10
.ident "GCC: (Ubuntu/Linaro 4.4.4-14ubuntu5.1) 4.4.5"
.section .note.GNU-stack,"",#progbits
Case 5 O/p:
root#local-host#./a.out
5 in default
Here due to the '{' delimiters the static value is confined to case 0 at assembler stage
the default case is still getting the value from 28(%esp) which is as expected as at assemble time case 0 has static and is out of scope for default.
You can try different formats by removing breaks and applying '{' '}' delimiters
Never forget to look it in terms of scope and wrto assembly code.
Related
So I'm learning how to convert the assembly into readable C code. The assembly is as follows...
Consider the compiler places the C variables: a at -4(%rbp), b at -8(%rbp), and c at -12(rbp).
file "main.c"
.text
.globl main
.type main, #function
main:
endbr64
pushq %rbp
movq %rsp, %rbp
movl $10, -12(%rbp)
movl $20, -4(%rbp)
movl $1, -8(%rbp)
.L4:
cmpl $1, -12(%rbp)
je .L7
movl -8(%rbp), %eax
imull -12(%rbp), %eax
movl %eax, -8(%rbp)
subl $1, -12(%rbp)
jmp .L4
.L7:
nop
movl -4(%rbp), %eax
imull -8(%rbp), %eax
movl %eax, -4(%rbp)
movl $0, %eax
popq %rbp
ret
This is what I have so far.
int c = 10;
int a = 20;
int b = 1;
for(c = 10; c > 1; c--)
{
int x = b;
x = c * x;
b = x;
}
Not completely sure how correct that is. The part that confuses me the most is the appearance (from what seems like out of nowhere) of eax. When eax appears, should I just assume that it is some other random variable? (hence the integer x I introduced)
I've written some code (main in c, subprogram in assembly x86) to calculate all the binomial coefficients recursively and print out all the binomial coefficients with n=10, restricted by m<=n.
So basically I'm trying to output a pascals triangle for n=10. (without the whole format of a triangle)
My problem is that I'm getting a segfault on compile and I'm having trouble figuring out how to print the individual values generated by the recursive function.
Segmentation fault (core dumped)
Here's the main program:
#include <stdio.h>
unsigned int result,m,n,i;
unsigned int binom(int,int);
int main(){
n=10;
for (i=0; i<n+1;i++){
printf("i=%d | %d \n", i, binom(n,i) );
}
return;
}
And the recursive sub program:
.text
.globl binom
binom:
mov $0x00, %edx #for difference calculation
cmp %edi, %esi #m=n?
je equalorzero #jump to equalorzero for returning of value 1
cmp $0x00, %esi #m=0?
je equalorzero
cmp $0x01, %esi #m=1?
mov %esi,%edx
sub %edi, %edx
cmp $0x01, %edx # n-m = 1 ?
je oneoronedifference
jmp otherwise
equalorzero:
add $1, %eax #return 1
ret
oneoronedifference:
add %edi, %eax #return n
ret
otherwise:
sub $1, %edi #binom(n-1,m)
call binom
sub $1, %esi #binom(n-1,m-1)
call binom
This is what gcc is giving me
./runtimes
i=0 | 12
Segmentation fault (core dumped)
The two major issues with your assembly code are: 1) you niether add nor return the sum of the two recursive calls; 2) you don't save your locals on the stack so they are wiped out by the recursive calls -- you're using the wrong values once you return from the calls. Here's my rework of your code, some of the changes are due to my writing this under OSX:
The recursive sub program:
.text
.globl _binom
_binom:
pushq %rbp # allocate space on stack for locals
movq %rsp, %rbp
subq $24, %rsp
cmpl %edi, %esi # m == n ?
je equalorzero # jump to equalorzero for returning of value 1
cmpl $0, %esi # m == 0 ?
je equalorzero
movl %esi, %edx
subl %edi, %edx
cmpl $1, %edx # n - m == 1 ?
je oneoronedifference
subl $1, %edi # binom(n - 1, m)
movl %edi, -4(%rbp)
movl %esi, -8(%rbp)
callq _binom
movl %eax, -12(%rbp) # save result to stack
movl -4(%rbp), %edi
movl -8(%rbp), %esi
subl $1, %esi # binom(n - 1, m - 1)
callq _binom
addl -12(%rbp), %eax # add results of the two recursive calls
addq $24, %rsp # release locals space on stack
popq %rbp
retq
equalorzero:
movl $1, %eax # return 1
addq $24, %rsp # release locals space on stack
popq %rbp
retq
oneoronedifference:
movl %edi, %eax # return n
addq $24, %rsp # release locals space on stack
popq %rbp
retq
The main program:
#include <stdio.h>
extern unsigned int binom(int, int);
int main() {
int n = 10;
for (int i = 0; i <= n; i++) {
printf("i=%d | %d\n", i, binom(n, i));
}
return 0;
}
And the results:
i=0 | 1
i=1 | 10
i=2 | 45
i=3 | 120
i=4 | 210
i=5 | 252
i=6 | 210
i=7 | 120
i=8 | 45
i=9 | 10
i=10 | 1
I'm trying to understand the assembly code during a recursive function call.
#include<stdio.h>
int recursive(int no){
if(no > 1){
no--;
recursive(no);
printf("\n %d \n",no);
}
else if(no == 1){
return 1;
}
}
int main(){
int a = 10;
recursive(a);
return 0;
}
disassembly :
.file "sample2.c"
.section .rodata
.LC0:
.string "\n %d \n"
.text
.globl recursive
.type recursive, #function
recursive:
pushl %ebp
movl %esp, %ebp
subl $24, %esp
cmpl $1, 8(%ebp)
jle .L2
subl $1, 8(%ebp)
movl 8(%ebp), %eax
movl %eax, (%esp)
call recursive
movl $.LC0, %eax
movl 8(%ebp), %edx
movl %edx, 4(%esp)
movl %eax, (%esp)
call printf
jmp .L5
.L2:
cmpl $1, 8(%ebp)
jne .L5
movl $1, %eax
movl %eax, %edx
movl %edx, %eax
jmp .L4
.L5:
.L4:
leave
ret
.size recursive, .-recursive
.globl main
.type main, #function
main:
pushl %ebp
movl %esp, %ebp
andl $-16, %esp
subl $32, %esp
movl $10, 28(%esp)
movl 28(%esp), %eax
movl %eax, (%esp)
call recursive
movl $0, %eax
leave
ret
.size main, .-main
.ident "GCC: (Ubuntu/Linaro 4.4.4-14ubuntu5) 4.4.5"
.section .note.GNU-stack,"",#progbits
I could understand .LC0 always holds the string literals. But I dont know what it really means. Would like to understand the code during the function call recursion was made.
I could not understand what this piece of assembly code does,
subl $24, %esp
cmpl $1, 8(%ebp)
jle .L2
subl $1, 8(%ebp)
movl 8(%ebp), %eax
movl %eax, (%esp)
call recursive
movl $.LC0, %eax
movl 8(%ebp), %edx
movl %edx, 4(%esp)
movl %eax, (%esp)
call printf
jmp .L5
.L2:
cmpl $1, 8(%ebp)
jne .L5
movl $1, %eax
movl %eax, %edx
movl %edx, %eax
jmp .L4
Q1:
The recursive function contains 1 parameter. so after the padding alignment, it has to be 8. why is it 24.
Also in .L2 ,
movl $1, %eax
movl %eax, %edx
movl %edx, %eax
jmp .L4
Q2:
we have moved '1' to the accumulater, why are we moving again to data register and then back to the accumulator.
Q3:
Are we popping out of stack. If leave is used for popping out of stack, are we not popping the rest of the 8 stack frames ?
To answer the only thing in your post that matches your title:
Why are we not popping out from the stack and only push instruction in the assembly.
Because leave is equivalent to:
movl %ebp, %esp
popl %ebp
With this program, I am intending to find the GCD of two numbers. But the result I get is "Floating point exception(core dumped)". What is the problem?
The code I am trying to generate is
int main() {
int sml, lrg, rem;
read %d sml
read %d lrg
while (sml > 0){
rem = lrg % sml;
lrg = sml;
sml = rem;
}
print %d lrg;
return 0;
}
The assembly file generated by me is:
.file "gcd.c"
.section .rodata
.LC0:
.string "%d"
.LC1:
.string "%d\n"
.text
.globl main
.type main, #function
main:
pushl %ebp
movl %esp, %ebp
andl $-16, %esp
subl $32, %esp
leal -8(%ebp), %eax #scan a value
movl %eax, 4(%esp)
movl $.LC0, (%esp)
call scanf
leal -12(%ebp), %eax #scan a value
movl %eax, 4(%esp)
movl $.LC0, (%esp)
call scanf
.L2:
movl $0, %eax
cmpl -8(%ebp),%eax
jle .L0
jmp .L1
.L0:
movl -12(%ebp),%eax
movl -8(%ebp),%ecx
movl %eax,%edx
sarl $31, %edx
idivl %ecx
movl %edx,%eax
movl %eax, -16(%ebp)
movl -8(%ebp),%edx
movl %edx, -12(%ebp)
movl -16(%ebp),%edx
movl %edx, -8(%ebp)
jmp .L2
.L1:
movl -12(%ebp), %eax
movl %eax, 4(%esp)
movl $.LC0, (%esp)
call printf
movl $0, %edx
movl $0, %eax #end of program
leave
ret
.LFE2:
.size main, .-main
.ident "GCC: (GNU) 4.2.3 (4.2.3-6mnb1)"
.section .note.GNU-stack,"",#progbits
On the other hand, this assembly code works
.file "check.c"
.section .rodata
.LC0:
.string "%d"
.text
.globl main
.type main, #function
main:
.LFB0:
.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
leal 20(%esp), %eax
movl %eax, 4(%esp)
movl $.LC0, (%esp)
call scanf
leal 24(%esp), %eax
movl %eax, 4(%esp)
movl $.LC0, (%esp)
call scanf
jmp .L2
.L3:
movl 24(%esp), %eax
movl 20(%esp), %ecx
movl %eax, %edx
sarl $31, %edx
idivl %ecx
movl %edx, 28(%esp)
movl 20(%esp), %eax
movl %eax, 24(%esp)
movl 28(%esp), %eax
movl %eax, 20(%esp)
.L2:
movl 20(%esp), %eax
testl %eax, %eax
jg .L3
movl 24(%esp), %eax
movl %eax, 4(%esp)
movl $.LC0, (%esp)
call printf
movl $0, %eax
leave
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
.cfi_endproc
.LFE0:
.size main, .-main
.ident "GCC: (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3"
.section .note.GNU-stack,"",#progbits
You probably have a division by zero in this line:
idivl %ecx
With a 0 value in ecx register.
Your comparison and jump are wrong. The working code has:
testl %eax, %eax
jg .L3
The broken code has:
movl $0, %eax
cmpl -8(%ebp),%eax
jle .L0
jmp .L1
The former compares %eax (which contains the most recently computed residue) to zero, and it continues the loop (jumps to .L3) if the residue is positive (greater than zero).
The latter compares 0 to -8(%ebp) (the most recently computed residue). Note that the order is different; it compares 0 to -8(%ebp), not -8(%ebp) to 0. The testl instruction compares the value (after performing an AND) with zero. If the value is positive, it is “greater than” zero. The cmpl instruction compares its second operand to its first operand; if the second operand exceeds the first, the result is “greater than”. This is because, in Intel’s manuals and assembly language, instructions are written with their operands in the reverse order. E.g, moving 3 into %eax would be “mov %eax, $3”. However, the assembler you are using reverses all the operands from Intel’s order (due to legacy reasons).
So, the broken code continues the loop (jumps to .L0) if 0 is less than or equal to the residue. Thus, if the residue is zero, the loop continues. You can change the jle to jl:
jl .L0
Alternately, you could eliminate the redundant unconditional jump:
movl $0, %eax
cmpl -8(%ebp),%eax
jge .L1
Also, you probably want to change:
movl $.LC0, (%esp)
call _printf
to:
movl $.LC1, (%esp)
call _printf
so that you pass "%d\n" to printf instead of passing "%d".
Incidentally, the idivl instruction generates a divide error, not a floating-point exception. Your system is misreporting the error.
I have the following recursive function to count all the nodes having value 20, in a circular doubly linked list. I need to convert this to tail recursive function to prevent safety issues. Please help me with the same. Thanks
int count(node *start)
{
return count_helper(start, start);
}
int count_helper(node *current, node *start)
{
int c;
c = 0;
if(current == NULL)
return 0;
if((current->roll_no) == 20)
c = 1;
if(current->next == start) return c;
return (c + count_helper(current->next, start));
}
In order to take advantage of tail recursion, the recursive call simply has to be the last thing performed. Currently, the only thing standing in the way of this goal is an addition. So, to transform the function, that addition has to be moved around. A common way to accomplish this is by passing the variable c as a parameter to the recursive helper function, as so:
int count(node *start)
{
return count_helper(start,start,0);
}
int count_helper(node *current, node *start, int c)
{
if(current == NULL)
return c;
if((current->roll_no) == 20)
c+=1;
if(current->next == start)
return c;
return count_helper(current->next, start,c);
}
This unrolls as follows (using gcc 4.6.1, as produced by gcc -S -O2):
count_helper:
.LFB23:
.cfi_startproc
pushl %ebx
.cfi_def_cfa_offset 8
.cfi_offset 3, -8
movl 8(%esp), %edx
movl 12(%esp), %ebx
movl 16(%esp), %eax
testl %edx, %edx
jne .L15
jmp .L10
.p2align 4,,7
.p2align 3
.L14:
testl %edx, %edx
je .L10
.L15:
xorl %ecx, %ecx
cmpl $20, 4(%edx)
movl (%edx), %edx
sete %cl
addl %ecx, %eax
cmpl %ebx, %edx
jne .L14 # <-- this is the key line right here
.L10:
popl %ebx
.cfi_def_cfa_offset 4
.cfi_restore 3
ret
.cfi_endproc
Compare this to your original (done without -O2, as apparently the compiler finds a way to make your original tail recursive as well, although in the process it mucks it up so much that I can barely read it):
count_helper:
.LFB1:
.cfi_startproc
pushl %ebp
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, %ebp
.cfi_def_cfa_register 5
subl $40, %esp
movl $0, -12(%ebp)
cmpl $0, 8(%ebp)
jne .L3
movl $0, %eax
jmp .L4
.L3:
movl 8(%ebp), %eax
movl 4(%eax), %eax
cmpl $20, %eax
jne .L5
movl $1, -12(%ebp)
.L5:
movl 8(%ebp), %eax
movl (%eax), %eax
cmpl 12(%ebp), %eax
jne .L6
movl -12(%ebp), %eax
jmp .L4
.L6:
movl 8(%ebp), %eax
movl (%eax), %eax
movl 12(%ebp), %edx
movl %edx, 4(%esp)
movl %eax, (%esp)
call count_helper # <-- this is the key line right here
addl -12(%ebp), %eax
.L4:
leave
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
.cfi_endproc