Recursion and tail recursion (C language)

Source: Internet
Author: User

Original: Recursion and tail recursion (C language) "Turn"

Archimedes

Source: http://www.cnblogs.com/archimedes/This article is copyright to the author and the blog Park is shared, welcome reprint, but without the consent of the author must retain this paragraph, and in the article page obvious location to the original link, otherwise reserves the right to pursue legal responsibility.

In the field of computer science, recursion is implemented by recursive functions. The programming technique called by the program itself is called recursion (recursion).

A procedure or function has a method of directly or indirectly invoking itself in its definition or description, which usually transforms a large and complex problem layer into a smaller problem similar to the original problem, and the recursive strategy can describe the repeated computations needed in the process of solving the problems by a small number of programs. Greatly reduces the code volume of the program. The ability to recursion is to define an infinite set of objects with limited statements.

In general, recursion is required: boundary conditions, recursive forward segments, and recursive return segments.

Recursion advances when boundary conditions are not met, and returns recursively when the boundary conditions are met.

Attention:

(1) Recursion is the invocation of itself in a process or function;

(2) When using a recursive strategy, there must be a definite recursive end condition called a recursive exit.

This article address: http://www.cnblogs.com/archimedes/p/rescuvie-tailrescuvie.html, reprint please indicate source address.

C programs in the virtual memory address from the low address to the high address order is. Text segment (code area),. Rodata segment (constant area),. Data segment (the initialized global variable area),. BSS segment (uninitialized global variable area), heap, dynamic library mapping area, stack, Kernel area (user-state code not accessible)

Basic recursion

Problem: Calculating n!

The mathematical formula is: n! =nx (n-1) x (n-2) ... 2x1

Using recursion, you can define:

Calculate 4 in a recursive way!

F (4) =4XF (3) Recursion phase

F (3) =3XF (2)

F (2) =2xf (1)

F (1) =1 termination conditions

F (2) = (2) x (1) Regression phase

F (3) = (3) x (2)

F (4) = (4) x (6)

24 Recursive completion

Implement the factorial function recursively:

int fact (int n) {    if (n < 0)        return 0;    else if (n = = 0 | | n = = 1)        return 1;    else        return n * fact (n-1);}

Here's a detailed analysis of how recursion works

Take a look at how functions are executed in C, and learn about how the C program is organized in-memory:

BSS:(BSS segment) usually refers to an area of memory that is used to store uninitialized global variables in the program. BSS is the abbreviation for English block Started by symbol. BSS segments belong to static memory allocations.

Data Segment : Data segment usually refers to an area of memory that is used to hold the initialized global variables in the program. The data segment belongs to static memory allocation.

Code Snippets: code segment/text segment usually refers to an area of memory that is used to store program execution code. The size of this area is determined before the program runs, and the memory area is usually read-only, and some schemas allow the code snippet to be writable, which allows the program to be modified. In a code snippet, it is also possible to include some read-only constant variables, such as String constants. The program segment is a mapping of program code in memory. A program can have more than one copy in memory.

Heap : A heap is used to store a dynamically allocated memory segment in a process run, which is not fixed in size and can be dynamically expanded or scaled down. When a process calls a function such as malloc/free to allocate memory, the newly allocated memory is dynamically added to the heap (heap is expanded)/freed memory is removed from the heap (heap is reduced)

Stack: Stacks are also called stacks, which store local variables of the program (but do not include static declared variables, static means that variables are stored in the data segment). In addition, the stack is used to pass parameters and return values when a function is called. Because of the last-in-first-out feature of the stack, the stack is particularly handy for saving/recovering call sites. In this sense, we can think of the stack as a memory area where temporary data is stored and exchanged.

The growth direction of the heap grows from low address to high address, while the stack is growing in the opposite direction (the actual situation is related to CPU architecture)

When a function is called in a C program, a space is allocated in the stack to hold the information associated with the call, and each invocation is considered active. The storage space on the stack is called an active record or a stack frame.

The stack frame consists of 5 regions: Input parameters, return value space, temporary storage space used to evaluate expressions, state information saved at function calls, and output parameters, see:

You can use the following procedure to verify:

#include <stdio.h>int g1=0, g2=0, g3=0;int max (int i) {int m1 = 0, m2, m3 = 0, *p_max;    Static N1_max = 0, N2_max, N3_max = 0;    P_max = (int*) malloc (10);    printf ("Print max program address \ n");    printf ("In max:0x%08x\n\n", Max);    printf ("Print max incoming parameter address \ n");    printf ("In max:0x%08x\n\n", &i);    printf ("Print static variable address \ n" in Max function); printf ("0x%08x\n", &n1_max);    Print the memory address of each local variable printf ("0x%08x\n", &n2_max);    printf ("0x%08x\n\n", &n3_max);    printf ("Print the local variable address \ n" in the Max function); printf ("0x%08x\n", &m1);    Print the memory address of each local variable printf ("0x%08x\n", &m2);    printf ("0x%08x\n\n", &m3);    printf ("Print Max function in malloc assigned address \ n"); printf ("0x%08x\n\n", P_max);    Print the memory address of each local variable if (i) return 1; else return 0;}    int main (int argc, char **argv) {static int s1=0, S2, s3=0;    int v1=0, v2, v3=0;        int *p;    p = (int*) malloc (10);    printf ("Print the memory address of each global variable (initialized) \ n"); printf ("0x%08x\n", &AMP;G1);    Print the memory address of each global variable printf ("0x%08x\n", &AMP;G2);    printf ("0x%08x\n\n", &g3); printf ("======================\n ");    printf ("Print Program initial program main address \ n");    printf ("main:0x%08x\n\n", main);    printf ("Print main parameter address \ n");    printf ("argv:0x%08x\n\n", argv);    printf ("Print the memory address of each static variable \ n"); printf ("0x%08x\n", &AMP;S1);    Print the memory address of each static variable printf ("0x%08x\n", &AMP;S2);    printf ("0x%08x\n\n", &AMP;S3);    printf ("Print the memory address of each local variable \ n"); printf ("0x%08x\n", &AMP;V1);    Print the memory address of each local variable printf ("0x%08x\n", &v2);    printf ("0x%08x\n\n", &v3);    printf ("Print the heap address for malloc allocation \ n");    printf ("malloc:0x%08x\n\n", p);    printf ("======================\n");    Max (v1);    printf ("======================\n");    printf ("Print sub function start address \ n");    printf ("max:0x%08x\n\n", Max); return 0;}

Stacks are a great solution for storing function call information, but the stack has some drawbacks:

The stack maintains the information for each function call until the function returns, which takes a considerable amount of space, especially in cases where many recursive calls are used in the program. In addition, because there is a lot of information that needs to be saved and restored, it takes time to generate and destroy active records. We need to consider an iterative approach. Fortunately, we can use a special recursive approach called tail recursion to avoid the aforementioned drawbacks.

Tail recursion definition

If all the recursive calls in a function appear at the end of the function, we call this recursive function the tail recursion. This recursive invocation is the tail recursion when the recursive call is the last executed statement in the body of the function and its return value is not part of an expression. The feature of the tail recursive function is that it is not necessary to do anything in the regression process, which is important because most modern compilers use this feature to automatically generate optimized code.

Principle

When the compiler detects that a function call is tail-recursive, it overwrites the current activity record instead of creating a new one in the stack. The compiler can do this because the recursive call is the last statement to be executed during the current active period, so there is nothing else to do in the stack frame when the call returns, so there is no need to save the stack frame. By overwriting the current stack frame instead of adding one on top of it, the stack space used is greatly reduced, which makes the actual running efficiency even higher. Although the compiler can optimize the stack overflow problem caused by tail recursion, in programming, we should try to avoid the appearance of tail recursion, because all the tail recursion can be replaced by a simple goto loop.

Instance

To understand how the tail recursion works, let's calculate the factorial again in recursive form. First of all, it can be very easy to understand why the recursion previously defined is not a tail recursion. Recall the definition of computational n!: Calculate n times (n-1) in each active period, let n=n-1 and continue the process until n=1. This definition is not tail-recursive, because the return value for each active period relies on the return value of n multiplied by one of the following active periods, so the stack frame that is generated for each call will have to be saved on the stack until the return value of the next child call is determined. Now let's consider defining the process of calculating n! in the form of tail recursion.

This definition also needs to accept the second parameter A, which is not much different. A (initialized to 1) maintains the depth of the recursive hierarchy. This allows us to avoid having to multiply the return value by n again every time. However, in each recursive call, make A=na and n=n-1. Continue the recursive call until the n=1, which satisfies the end condition, returns a immediately.

The code example gives a C function Facttail, which takes an integer n and computes the n! in the form of a tail recursion. This function also accepts a parameter a,a with an initial value of 1. Facttail uses a to maintain the depth of the recursive hierarchy, except that it is similar to fact. Readers can look at the similarities between the implementation of the function and the definition of the tail recursion.

int facttail (int n, int a) {    if (n < 0)        return 0;    else if (n = = 0)        return 1;    else if (n = = 1)        return A;    else        return Facttail (n-1, n * a);}

The function in the example is tail-recursive, because a single recursive call to Facttail is the last statement executed before the function returns. It happens that the last statement in Facttail is also a call to Facttail, but this is not required. In other words, there are other statements that can be executed after a recursive call, except that they can only be executed if the recursive call is not executed.

The tail recursion is extremely important, without the tail recursion, the stack consumption of the function is difficult to estimate, need to save many intermediate functions of the stack. such as f (n, sum) = f (n-1) + value (n) + sum; The N function call stack is saved, and tail recursion f (n, sum) = f (n-1, Sum+value (n)) is used; This preserves only the last function stack, which can be optimized for deletion.

There may be many exceptions in C, but the programming language is not only the C language, but in the functional language Erlang (also the stack language), if you want to maintain the high concurrency of the language, you must replace the traditional recursion with the tail-recursive return.

Recursion and tail recursion (C language)

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.