Mod 3

Download as pdf or txt
Download as pdf or txt
You are on page 1of 72

Module-3

A function in C is a set of statements that when called perform some specific task. It is the
basic building block of a C program that provides modularity and code reusability. The programming
statements of a function are enclosed within { } braces, having certain meanings and performing
certain operations. They are also called subroutines or procedures in other languages.

In this article, we will learn about functions, function definition. declaration, arguments and
parameters, return values, and many more.

Syntax of Functions in C

The syntax of function can be divided into 3 aspects:

1. Function Declaration

2. Function Definition

3. Function Calls

Function Declarations

In a function declaration, we must provide the function name, its return type, and the number and
type of its parameters. A function declaration tells the compiler that there is a function with the given
name defined somewhere else in the program.

Syntax

return_type name_of_the_function (parameter_1, parameter_2);

The parameter name is not mandatory while declaring functions. We can also declare the function
without using the name of the data variables.

Example

int sum(int a, int b);

int sum(int , int);


Function Declaration

Note: A function in C must always be declared globally before calling it.

Function Definition

The function definition consists of actual statements which are executed when the function is called
(i.e. when the program control comes to the function).

A C function is generally defined and declared in a single step because the function definition always
starts with the function declaration so we do not need to declare it explicitly. The below example serves
as both a function definition and a declaration.

return_type function_name (para1_type para1_name, para2_type para2_name)

// body of the function

Function Definition in C

Function Call

A function call is a statement that instructs the compiler to execute the function. We use the function
name and parameters in the function call.

In the below example, the first sum function is called and 10,30 are passed to the sum function. After
the function call sum of a and b is returned and control is also returned back to the main function of
the program.
Working of function in C

Note: Function call is neccessary to bring the program control to the function definition. If not called,
the function statements will not be executed.

Example of C Function

• C

// C program to show function

// call and definition

#include <stdio.h>

// Function that takes two parameters

// a and b as inputs and returns

// their sum

int sum(int a, int b)

return a + b;

// Driver code

int main()

// Calling sum function and

// storing its value in add variable

int add = sum(10, 30);


printf("Sum is: %d", add);

return 0;

Output

Sum is: 40

As we noticed, we have not used explicit function declaration. We simply defined and called the
function.

Function Return Type

Function return type tells what type of value is returned after all function is executed. When we don’t
want to return a value, we can use the void data type.

Example:

int func(parameter_1,parameter_2);

The above function will return an integer value after running statements inside the function.

Note: Only one value can be returned from a C function. To return multiple values, we have to use
pointers or structures.

Function Arguments

Function Arguments (also known as Function Parameters) are the data that is passed to a function.

Example:

int function_name(int var1, int var2);

Conditions of Return Types and Arguments

In C programming language, functions can be called either with or without arguments and might return
values. They may or might not return values to the calling functions.

1. Function with no arguments and no return value

2. Function with no arguments and with return value

3. Function with argument and with no return value

4. Function with arguments and with return value

To know more about function Arguments and Return values refer to the article – Function Arguments
& Return Values in C.

How Does C Function Work?

Working of the C function can be broken into the following steps as mentioned below:
1. Declaring a function: Declaring a function is a step where we declare a function. Here we
define the return types and parameters of the function.

2. Defining a function:

3. Calling the function: Calling the function is a step where we call the function by passing the
arguments in the function.

4. Executing the function: Executing the function is a step where we can run all the statements
inside the function to get the final result.

5. Returning a value: Returning a value is the step where the calculated value after the execution
of the function is returned. Exiting the function is the final step where all the allocated memory
to the variables, functions, etc is destroyed before giving full control to the main function.

Types of Functions

There are two types of functions in C:

1. Library Functions

2. User Defined Functions

Types of Functions in C

1. Library Function

A library function is also referred to as a “built-in function”. A compiler package already exists that
contains these functions, each of which has a specific meaning and is included in the package. Built-in
functions have the advantage of being directly usable without being defined, whereas user-defined
functions must be declared and defined before being used.

For Example:

pow(), sqrt(), strcmp(), strcpy() etc.

Advantages of C library functions

• C Library functions are easy to use and optimized for better performance.

• C library functions save a lot of time i.e, function development time.

• C library functions are convenient as they always work.

Example:

• C
// C program to implement

// the above approach

#include <math.h>

#include <stdio.h>

// Driver code

int main()

double Number;

Number = 49;

// Computing the square root with

// the help of predefined C

// library function

double squareRoot = sqrt(Number);

printf("The Square root of %.2lf = %.2lf",

Number, squareRoot);

return 0;

Output

The Square root of 49.00 = 7.00

2. User Defined Function

Functions that the programmer creates are known as User-Defined functions or “tailor-made
functions”. User-defined functions can be improved and modified according to the need of the
programmer. Whenever we write a function that is case-specific and is not defined in any header file,
we need to declare and define our own functions according to the syntax.

Advantages of User-Defined Functions

• Changeable functions can be modified as per need.

• The Code of these functions is reusable in other programs.

• These functions are easy to understand, debug and maintain.


Example:

• C

// C program to show

// user-defined functions

#include <stdio.h>

int sum(int a, int b)

return a + b;

// Driver code

int main()

int a = 30, b = 40;

// function call

int res = sum(a, b);

printf("Sum is: %d", res);

return 0;

Output

Sum is: 70

Passing Parameters to Functions

The data passed when the function is being invoked is known as the Actual parameters. In the below
program, 10 and 30 are known as actual parameters. Formal Parameters are the variable and the data
type as mentioned in the function declaration. In the below program, a and b are known as formal
parameters.
Passing Parameters to Functions

We can pass arguments to the C function in two ways:

1. Pass by Value

2. Pass by Reference

1. Pass by Value

Parameter passing in this method copies values from actual parameters into formal function
parameters. As a result, any changes made inside the functions do not reflect in the caller’s
parameters.

Example:

• C

// C program to show use

// of call by value

#include <stdio.h>

void swap(int var1, int var2)

int temp = var1;

var1 = var2;

var2 = temp;

// Driver code

int main()
{

int var1 = 3, var2 = 2;

printf("Before swap Value of var1 and var2 is: %d, %d\n",

var1, var2);

swap(var1, var2);

printf("After swap Value of var1 and var2 is: %d, %d",

var1, var2);

return 0;

Output

Before swap Value of var1 and var2 is: 3, 2

After swap Value of var1 and var2 is: 3, 2

2. Pass by Reference

The caller’s actual parameters and the function’s actual parameters refer to the same locations, so any
changes made inside the function are reflected in the caller’s actual parameters.

Example:

• C

// C program to show use of

// call by Reference

#include <stdio.h>

void swap(int *var1, int *var2)

int temp = *var1;

*var1 = *var2;

*var2 = temp;

}
// Driver code

int main()

int var1 = 3, var2 = 2;

printf("Before swap Value of var1 and var2 is: %d, %d\n",

var1, var2);

swap(&var1, &var2);

printf("After swap Value of var1 and var2 is: %d, %d",

var1, var2);

return 0;

Output

Before swap Value of var1 and var2 is: 3, 2

After swap Value of var1 and var2 is: 2, 3

Advantages of Functions in C

Functions in C is a highly useful feature of C with many advantages as mentioned below:

1. The function can reduce the repetition of the same statements in the program.

2. The function makes code readable by providing modularity to our program.

3. There is no fixed number of calling functions it can be called as many times as you want.

4. The function reduces the size of the program.

5. Once the function is declared you can just use it without thinking about the internal working
of the function.

Disadvantages of Functions in C

The following are the major disadvantages of functions in C:

1. Cannot return multiple values.

2. Memory and time overhead due to stack frame allocation and transfer of program control.

Conclusion

We discussed the following points about the function as mentioned below:

1. The function is the block of code that can be reused as many times as we want inside a
program.

2. To use a function, we need to call a function.


3. Function declaration includes function_name, return type, and parameters.

4. Function definition includes the body of the function.

5. The function is of two types user-defined function and library function.

6. In function, we can according to two types call by value and call by reference according to the
values passed.

Return Statement in C
C return statement ends the execution of a function and returns the control to the function from
where it was called. The return statement may or may not return a value depending upon the return
type of the function. For example, int returns an integer value, void returns nothing, etc.

In C, we can only return a single value from the function using the return statement and we have to
declare the data_type of the return value in the function definition/declaration.

Syntax:

return return_value;

Working of Return Statement

There are various ways to use return statements. A few are mentioned below:

1. Methods not returning a value


In C, one cannot skip the return statement when the return type of the function is non-void type. The
return statement can be skipped only for void types.

A. Not using a return statement in void return type function:

While using the void function, it is not necessary to use return as the void itself means nothing(an
empty value).

Syntax:

void func()

Example:

• C

// C code to show not using return statement in void return type function

#include <stdio.h>

// void method without return statement

void Print()

printf("Welcome to GeeksforGeeks");

// Driver method

int main()

// Calling print

Print();
return 0;

Output:

Welcome to GeeksforGeeks

B. Using the return statement in the void return type function

As void means empty, we don’t need to return anything, but we can use the return statement inside
void functions as shown below. Although, we still cannot return any value.

Syntax:

void func()

return;

This syntax is used in function as a jump statement in order to break the flow of the function and jump
out of it. One can think of it as an alternative to “break statement” to use in functions.

Example:

• C

// C code to show using return

// statement in void return type function

#include <stdio.h>

// void method with return statement

void Print()

printf("Welcome to GeeksforGeeks");

// void method using the return statement

return;
}

// Driver method

int main()

// Calling print

Print();

return 0;

Output:

Welcome to GeeksforGeeks

But if the return statement tries to return a value in a void return type function, that will lead to errors.

Incorrect Syntax:

void func()

return value;

Example:

• C

// C code to show using return statement

// with a value in void return type function

#include <stdio.h>

// void method

void Print()

{
printf("Welcome to GeeksforGeeks");

// void method using the return

// statement to return a value

return 10;

// Driver method

int main()

// Calling print

Print();

return 0;

Warnings:

prog.c: In function 'Print':

prog.c:12:9: warning: 'return' with a value, in function returning void

return 10;

2. Methods returning a value

For functions that define a non-void return type in the definition and declaration, the return statement
must be immediately followed by the return value of that specified return type.

Syntax:

return-type func()

return value;

Example:

• C
// C code to illustrate Methods returning

// a value using return statement

#include <stdio.h>

// non-void return type

// function to calculate sum

int SUM(int a, int b)

int s1 = a + b;

// method using the return

// statement to return a value

return s1;

// Driver method

int main()

int num1 = 10;

int num2 = 10;

int sum_of = SUM(num1, num2);

printf("The sum is %d", sum_of);

return 0;

Output:

The sum is 20

Note: A function can only return a single value using return statement. To return multiple values, we
use pointers or structures
The scope of a variable in C is the block or the region in the program where a
variable is declared, defined, and used. Outside this region, we cannot access the variable and it is
treated as an undeclared identifier.

• The scope is the area under which a variable is visible.

• The scope of an identifier is the part of the program where the identifier may directly be
accessible.

• We can only refer to a variable in its scope.

• In C, all identifiers are lexically(or statically) scoped.

Example

• C

// C program to illustrate the scope of a variable

#include <stdio.h>

int main()

// Scope of this variable is within main() function


// only.

int var = 34;

printf("%d", var);

return 0;

// function where we try to access the var defined in main()

void func() { printf("%d", var); }

Output

solution.c: In function 'func':

solution.c:15:28: error: 'var' undeclared (first use in this function)

void func() { printf("%d", var); }

Here, we tried to access variable names var As we can see that if we try to refer to the variable outside
its scope, we get the above error.

Types of Scope Rules in C

C scope rules can be covered under the following two categories:

1. Global Scope

2. Local Scope

Let’s discuss each scope rule with examples.

1. Global Scope in C

The global scope refers to the region outside any block or function.

• The variables declared in the global scope are called global variables.

• Global variables are visible in every part of the program.

• Global is also called File Scope as the scope of an identifier starts at the beginning of the file
and ends at the end of the file.

Example

• C

// C program to illustrate the global scope


#include <stdio.h>

// variable declared in global scope

int global = 5;

// global variable accessed from

// within a function

void display()

printf("%d\n", global);

// main function

int main()

printf("Before change within main: ");

display();

// changing value of global

// variable from main function

printf("After change within main: ");

global = 10;

display();

Output

Before change within main: 5

After change within main: 10

Linkage of Variables in Global Scope

Global variables have external linkage by default. It means that the variables declared in the global
scope can be accessed in another C source file. We have to use the extern keyword for that purpose.
Example of External Linkage

file1.c

• C

// filename: file1.c

int a;

int main(void)

a = 2;

file2.c

• C

// filename: file2.c

// When this file is linked with file1.c, functions

// of this file can access a

extern int a;

int myfun()

printf("%d", a);

Output

Note: To restrict access to the current file only, global variables can be marked as static.

2. Local Scope in C

The local scope refers to the region inside a block or a function. It is the space enclosed between the {
} braces.
• The variables declared within the local scope are called local variables.

• Local variables are visible in the block they are declared in and other blocks nested inside that
block.

• Local scope is also called Block scope.

• Local variables have internal linkage.

Example

• C

// C program to illustrate the local scope

#include <stdio.h>

// Driver Code

int main()

int x = 10, y = 20;

// The outer block contains

// declaration of x and

// y, so following statement

// is valid and prints

// 10 and 20

printf("x = %d, y = %d\n", x, y);

// y is declared again,

// so outer block y is

// not accessible in this block

int y = 40;

// Changes the outer block

// variable x to 11
x++;

// Changes this block's

// variable y to 41

y++;

printf("x = %d, y = %d\n", x, y);

// This statement accesses

// only outer block's

// variables

printf("x = %d, y = %d\n", x, y);

return 0;

Output

x = 10, y = 20

x = 11, y = 41

x = 11, y = 20

Storage Classes in C

C Storage Classes are used to describe the features of a variable/function. These features basically
include the scope, visibility, and lifetime which help us to trace the existence of a particular variable
during the runtime of a program.

C language uses 4 storage classes, namely:


1. auto

This is the default storage class for all the variables declared inside a function or a block. Hence, the
keyword auto is rarely used while writing programs in C language. Auto variables can be only accessed
within the block/function they have been declared and not outside them (which defines their scope).
Of course, these can be accessed within nested blocks within the parent block/function in which the
auto variable was declared.

However, they can be accessed outside their scope as well using the concept of pointers given here by
pointing to the very exact memory location where the variables reside. They are assigned a garbage
value by default whenever they are declared.

2. extern

Extern storage class simply tells us that the variable is defined elsewhere and not within the same
block where it is used. Basically, the value is assigned to it in a different block and this can be
overwritten/changed in a different block as well. So an extern variable is nothing but a global variable
initialized with a legal value where it is declared in order to be used elsewhere. It can be accessed
within any function/block.

Also, a normal global variable can be made extern as well by placing the ‘extern’ keyword before its
declaration/definition in any function/block. This basically signifies that we are not initializing a new
variable but instead, we are using/accessing the global variable only. The main purpose of using extern
variables is that they can be accessed between two different files which are part of a large program.

3. static

This storage class is used to declare static variables which are popularly used while writing programs
in C language. Static variables have the property of preserving their value even after they are out of
their scope! Hence, static variables preserve the value of their last use in their scope. So we can say
that they are initialized only once and exist till the termination of the program. Thus, no new memory
is allocated because they are not re-declared.

Their scope is local to the function to which they were defined. Global static variables can be accessed
anywhere in the program. By default, they are assigned the value 0 by the compiler.

4. register
This storage class declares register variables that have the same functionality as that of the auto
variables. The only difference is that the compiler tries to store these variables in the register of the
microprocessor if a free register is available. This makes the use of register variables to be much faster
than that of the variables stored in the memory during the runtime of the program.

If a free registration is not available, these are then stored in the memory only. Usually, a few variables
which are to be accessed very frequently in a program are declared with the register keyword which
improves the running time of the program. An important and interesting point to be noted here is that
we cannot obtain the address of a register variable using pointers.

Syntax

To specify the storage class for a variable, the following syntax is to be followed:

storage_class var_data_type var_name;

Functions follow the same syntax as given above for variables. Have a
look at the following C example for further clarification:

• C

// A C program to demonstrate different storage


// classes
#include <stdio.h>

// declaring the variable which is to be made extern


// an initial value can also be initialized to x
int x;

void autoStorageClass()
{

printf("\nDemonstrating auto class\n\n");

// declaring an auto variable (simply


// writing "int a=32;" works as well)
auto int a = 32;
// printing the auto variable 'a'
printf("Value of the variable 'a'"
" declared as auto: %d\n",
a);

printf("--------------------------------");
}

void registerStorageClass()
{

printf("\nDemonstrating register class\n\n");

// declaring a register variable


register char b = 'G';

// printing the register variable 'b'


printf("Value of the variable 'b'"
" declared as register: %d\n",
b);

printf("--------------------------------");
}

void externStorageClass()
{

printf("\nDemonstrating extern class\n\n");

// telling the compiler that the variable


// x is an extern variable and has been
// defined elsewhere (above the main
// function)
extern int x;

// printing the extern variables 'x'


printf("Value of the variable 'x'"
" declared as extern: %d\n",
x);

// value of extern variable x modified


x = 2;

// printing the modified values of


// extern variables 'x'
printf("Modified value of the variable 'x'"
" declared as extern: %d\n",
x);

printf("--------------------------------");
}

void staticStorageClass()
{
int i = 0;

printf("\nDemonstrating static class\n\n");

// using a static variable 'y'


printf("Declaring 'y' as static inside the loop.\n"
"But this declaration will occur only"
" once as 'y' is static.\n"
"If not, then every time the value of 'y' "
"will be the declared value 5"
" as in the case of variable 'p'\n");

printf("\nLoop started:\n");

for (i = 1; i < 5; i++) {

// Declaring the static variable 'y'


static int y = 5;

// Declare a non-static variable 'p'


int p = 10;

// Incrementing the value of y and p by 1


y++;
p++;

// printing value of y at each iteration


printf("\nThe value of 'y', "
"declared as static, in %d "
"iteration is %d\n",
i, y);

// printing value of p at each iteration


printf("The value of non-static variable 'p', "
"in %d iteration is %d\n",
i, p);
}
printf("\nLoop ended:\n");

printf("--------------------------------");
}

int main()
{

printf("A program to demonstrate"


" Storage Classes in C\n\n");

// To demonstrate auto Storage Class


autoStorageClass();

// To demonstrate register Storage Class


registerStorageClass();

// To demonstrate extern Storage Class


externStorageClass();

// To demonstrate static Storage Class


staticStorageClass();

// exiting
printf("\n\nStorage Classes demonstrated");

return 0;
}

// This code is improved by RishabhPrabhu

Output
A program to demonstrate Storage Classes in C

Demonstrating auto class

Value of the variable 'a' declared as auto: 32


--------------------------------
Demonstrating register class

Value of the variable 'b' declared as register: 71


--------------------------------
Demonstrating extern class

Value of the variable 'x' declared as extern: 0


Modified value of the variable 'x' declared as extern: 2
--------------------------------
Demonstrating static class

Declaring 'y' as static inside the loop.


But this declaration will occur only once as 'y' is static.
If not, then every time the value of 'y' will be the declared value
5 as in the case of variable 'p'

Loop started:

The value of 'y', declared as static, in 1 iteration is 6


The value of non-static variable 'p', in 1 iteration is 11

The value of 'y', declared as static, in 2 iteration is 7


The value of non-static variable 'p', in 2 iteration is 11

The value of 'y', declared as static, in 3 iteration is 8


The value of non-static variable 'p', in 3 iteration is 11

The value of 'y', declared as static, in 4 iteration is 9


The value of non-static variable 'p', in 4 iteration is 11

Loop ended:
--------------------------------

Recursive functions
What is Recursion in C?
First, let’s start with the recursion definition,

Recursion is the process of a function calling itself repeatedly till the given condition is satisfied. A
function that calls itself directly or indirectly is called a recursive function and such kind of function calls
are called recursive calls.

In C, recursion is used to solve complex problems by breaking them down into simpler sub-problems.
We can solve large numbers of problems using recursion in C. For example, factorial of a number,
generating Fibonacci series, generating subsets, etc.

Let’s discuss some basic terminologies and fundamentals of recursion before going into working and
implementation.

Recursive Functions in C

In C, a function that calls itself is called Recursive Function. The recursive functions contain a call to
themselves somewhere in the function body. Moreover, such functions can contain multiple recursive
calls.

Basic Structure of Recursive Functions

The basic syntax structure of the recursive functions is:

type function_name (args) {

// function statements

// base condition

// recursion case (recursive call)

Basic Structure of Recursive Function in C

Example: C Program to Implement Recursion

In the below C program, recursion is used to calculate the sum of the first N natural numbers.

// C Program to calculate the sum of first N natural numbers

// using recursion

#include <stdio.h>
int nSum(int n)

// base condition to terminate the recursion when N = 0

if (n == 0) {

return 0;

// recursive case / recursive call

int res = n + nSum(n - 1);

return res;

int main()

int n = 5;

// calling the function

int sum = nSum(n);

printf("Sum of First %d Natural Numbers: %d", n, sum);

return 0;

Output

Sum of First 5 Natural Numbers: 15

We will understand the different concepts of recursion using this example.

Fundamentals of C Recursion
The fundamental of recursion consists of two objects which are essential for any recursive function.
These are:

1. Recursion Case

2. Base Condition

Base Condition and Recursion Case for nSum() Function

1. Recursion Case

The recursion case refers to the recursive call present in the recursive function. It decides what type of
recursion will occur and how the problem will be divided into smaller subproblems.

The recursion case defined in the nSum() function of the above example is:

int res = n + nSum(n - 1);

The recursion case if often represented mathematically is a recurrence relation. For the above case:

f(N) = N + f(N - 1);

This recurrence relation is used for the complexity analysis of the method.

2. Base Condition

The base condition specifies when the recursion is going to terminate. It is the condition that
determines the exit point of the recursion.

Note: It is important to define the base condition before the recursive case otherwise, the base
condition may never encountered and recursion might continue till infinity.

Considering the above example again, the base condition defined for the nSum() function:

if (n == 0) {

return 0;

}
Now that the basic terminologies and fundamentals are out of the way, let’s move on to understand
how the recursion works in C.

How Recursion works in C?

Recursion is considered difficult to understand by many people but once you understand the working
of recursion, it becomes a powerful weapon in your arsenal to battle complex problems.

To understand how C recursion works, we will again refer to the example above and trace the flow of
the program. In the nSum() function, Recursive Case is

int res = n + nSum(n - 1);

In the example, n = 5, so as nSum(5)’s recursive case, we get

int res = 5 + nSum(4);

In nSum(4), the recursion case and everything else will be the same, but n = 4. Let’s evaluate the
recursive case for n = 4,

int res = 4 + nSum(3);

Similarly, for nSum(3), nSum(2) and nSum(1)

int res = 3 + nSum(2); // nSum(3)

int res = 2 + nSum(1); // nSum(2)

int res = 1 + nSum(0); // nSum(1)

Let’s not evaluate nSum(0) and further for now.

Now recall that the return value of the nSum() function in this same integer named res. So, instead of
the function, we can put the value returned by these functions. As such, for nSum(5), we get

int res = 5 + 4 + nSum(3);

Similarly, putting return values of nSum() for every n, we get

int res = 5 + 4 + 3 + 2 + 1 + nSum(0);

In nSum() function, the base condition is

if (n == 0) {

return 0;
}

which means that when nSum(0) will return 0. Putting this value in nSum(5)’s recursive case, we get

int res = 5 + 4 + 3 + 2 + 1 + 0 = 15

At this point, we can see that there are no function calls left. So the recursion will stop here and the
final value returned by the function will be 15 which is the sum of the first 5 natural numbers.

Recursion Tree Diagram of nSum(5) Function

If you didn’t understand properly and are still confused about the recursion working, don’t worry, it is
explained again in terms of memory management and the compiler’s internal handling.

Memory Allocation for C Recursive Function

To further improve our understanding of recursion in C, we will look into how the recursion is internally
handled by the C compiler and how the memory is managed for recursive functions.

As you may know, all the function’s local variables and other stuff are stored inside the stack frame in
stack memory and once the function returns some value, its stack frame is removed from the memory.
The recursion follows a similar concept but with a little twist. In Recursion,

• A stack frame is created on top of the existing stack frames each time a recursive call is
encountered and the data of each recursive copy of the function will be stored in their
respective stack.

• Once, some value is returned by the function, its stack frame will be destroyed.

• The compiler maintains an instruction pointer to store the address of the point where the
control should return in the function after its progressive copy returns some value. This return
point is the statement just after the recursive call.
• After all the recursive copy returned some value, we come back to the base function and the
finally return the control to the caller function.

Let’s use the first example again and see how the memory is managed for the nSum() function.

Step 1:

When nSum() is called from the main() function with 5 as an argument, a stack frame for nSum(5) is
created.

Step 2:

While executing nSum(5), a recursive call is encountered as nSum(4). The compiler will now create a
new stack frame on top of the nSum(5)’s stack frame and maintain an instruction pointer at the
statement where nSum(4) was encountered.

Function Call Stack at the Execution of nSum(5)

Step 3:

In the execution of nSum(4), we encounter another recursive call as nSum(3). The compiler will again
follow the same steps and maintain another instruction pointer and stack frame for nSum(3).

Function Call Stack at the Execution of nSum(4)

Step 4:

The same thing will happen with nSum(3), nSum(2), and nSum(1)’s execution.

Function Call Stack at the Execution of nSum(3)

Step 5:

But when the control comes to nSum(0), the condition (n == 0) becomes true and the
statement return 0 is executed.
Function Call Stack at the Execution of nSum(0)

Step 6:

As the value is returned by the nSum(0), the stack frame for the nSum(0) will be destroyed. Using the
instruction pointer, the program control will return to the nSum(1) function and the nSum(0) call will
be replaced by value 0.

nSum(0) Function Returning Value

Step 7:

Now, in nSum(1), the expression int res = 1 + 0 will be evaluated and the statement return res will be
executed. The program control will move to the nSum(2).

nSum(1) Function Returning Value

Step 8:

In nSum(2), nSum(1) call will be replaced by the value it returned, which is 1. So, after evaluating int
res = 2 + 1, 3 will be returned to nSum(3). The same thing will keep happening till the control comes
to the nSum(5) again.
nSum(2), nSum(3) and nSum(4) Functions Returning Value

Step 9:

When the control reaches the nSum(5), the expression int res = 5 + nSum(4) will look like int res = 5 +
10. Finally, this value will be returned to the main() function and the execution of nSum() function will
be completed.

Final Result Returned to main()

Stack Overflow

The program’s call stack has limited memory assigned to it by the operating system and is generally
enough for program execution. But if we have a recursive function that goes on for infinite times,
sooner or later, the memory will be exhausted and no more data can be stored. This is called stack
overflow. In other words,

Stack overflow is the error that occurs occurs when the call stack of the program cannot store more
data resulting in program termination.

It is one of the most common errors that is associated with the recursion.

Types of C Recursion

In C, recursion can be classified into different types based on what kind of recursive case is present.
These types are:

1. Direct Recursion

• Head Recursion

• Tail Recursion

• Tree Recursion

2. Indirect Recursion
Types of Recursion in C

1. Direct Recursion

Direct recursion is the most common type of recursion, where a function calls itself directly within its
own body. The recursive call can occur once or multiple times within the function due to which we can
further classify the direct recursion

A. Head Recursion

The head recursion is a linear recursion where the position of its only recursive call is at the start of
the function. It is generally the first statement in the function.

B. Tail Recursion

The tail recursion is also a liner recursion like head recursion but the position of the recursive call is at
the end of the function. Due to this, the tail recursion can be optimized to minimize the stack memory
usage. This process is called Tail Call Optimization.

In the first example, the nSum() does the tail recursion.

C. Tree Recursion

In tree recursion, there are multiple recursive calls present in the body of the function. Due to this,
while tracing the program flow, it makes a tree-like structure, hence the name Tree Recursion.

2. Indirect Recursion

Indirect recursion is an interesting form of recursion where a function calls another function, which
eventually calls the first function or any other function in the chain, leading to a cycle of function calls.
In other words, the functions are mutually recursive. This type of recursion involves multiple functions
collaborating to solve a problem.

Examples of Recursion in C

Example 1: C Program to Find the Factorial of a Natural Number using Tail Recursion.
C

// C program to find the factorail using tail recursion

#include <stdio.h>

int factorialTail(int n)

// Base case

if (n == 1 || n == 0) {

return 1;

else {

// Tail recursive call

return n * factorialTail(n - 1);

int main()

int n = 5;

int fact1 = factorialTail(n);

printf("Resursive Factorial of %d: %d \n", n, fact1);

return 0;

Output

Resursive Factorial of 5: 120

Example 2: C Program to find the Fibonacci Number using Tree Recursion


C

// C Program to find the fibonacci number using tree

// recursion

#include <stdio.h>

int fibonacci(int n)

// Base case

// Fibonacci of 0 and 1 is the number itself

if (n <= 1) {

return n;

else {

// Tree recursive calls

return fibonacci(n - 1) + fibonacci(n - 2);

int main()

// function call

int n = fibonacci(3);

// print 5th fibonacci number

printf("%d", n);

return 0;

Output
2

Example 3: C Program to Illustrate the Indirect Recursion

// C Program to Illustrate the Indirect Recursion

#include <stdio.h>

void functionA(int n)

if (n < 1) {

return;

printf("%d ", n);

n = n - 1;

// Indirect recursive call to functionB

functionB(n);

void functionB(int n)

if (n < 2) {

return;

printf("%d ", n);

n = n / 2;

// Indirect recursive call to functionA

functionA(n);

}
int main()

// Function call

functionB(20);

return 0;

Output

20 10 9 4 3 1

Applications of Recursion in C

Recursion is widely used to solve different kinds of problems from simple ones like printing linked lists
to being extensively used in AI. Some of the common uses of recursion are:

• Tree-Graph Algorithms

• Mathematical Problems

• Divide and Conquer

• Dynamic Programming

• In Postfix to Infix Conversion

• Searching and Sorting Algorithms

Advantages of C Recursion

The advantages of using recursive methods over other methods are:

1. Recursion can effectively reduce the length of the code.

2. Some problems are easily solved by using recursion like the tower of Hanoi and tree traversals.

3. Data structures like linked lists, trees, etc. are recursive by nature so recursive methods are
easier to implement for these data structures.

Disadvantages of C Recursion

As with almost anything in the world, recursion also comes with certain limitations some of which are:

1. Recursive functions make our program a bit slower due to function call overhead.
2. Recursion functions always take extra space in the function call stack due to separate stack
frames.

3. Recursion methods are difficult to understand and implement.

Array in C is one of the most used data structures in C programming. It is a simple and fast way
of storing multiple values under a single name. In this article, we will study the different aspects of
array in C language such as array declaration, definition, initialization, types of arrays, array syntax,
advantages and disadvantages, and many more.

What is Array in C?

An array in C is a fixed-size collection of similar data items stored in contiguous memory locations. It
can be used to store the collection of primitive data types such as int, char, float, etc., and also derived
and user-defined data types such as pointers, structures, etc.

C Array Declaration

In C, we have to declare the array like any other variable before using it. We can declare an array by
specifying its name, the type of its elements, and the size of its dimensions. When we declare an array
in C, the compiler allocates the memory block of the specified size to the array name.

Syntax of Array Declaration

data_type array_name [size];


or
data_type array_name [size1] [size2]...[sizeN];

where N is the number of dimensions.


The C arrays are static in nature, i.e., they are allocated memory at the compile time.

Example of Array Declaration

• C

// C Program to illustrate the array declaration

#include <stdio.h>

int main()

// declaring array of integers

int arr_int[5];

// declaring array of characters

char arr_char[5];

return 0;

C Array Initialization

Initialization in C is the process to assign some initial value to the variable. When the array is declared
or allocated memory, the elements of the array contain some garbage value. So, we need to initialize
the array to some meaningful value. There are multiple ways in which we can initialize an array in C.

1. Array Initialization with Declaration


In this method, we initialize the array along with its declaration. We use an initializer list to initialize
multiple elements of the array. An initializer list is the list of values enclosed within braces { } separated
b a comma.

data_type array_name [size] = {value1, value2, ... valueN};

2. Array Initialization with Declaration without Size

If we initialize an array using an initializer list, we can skip declaring the size of the array as the compiler
can automatically deduce the size of the array in these cases. The size of the array in these cases is
equal to the number of elements present in the initializer list as the compiler can automatically deduce
the size of the array.

data_type array_name[] = {1,2,3,4,5};

The size of the above arrays is 5 which is automatically deduced by the compiler.

3. Array Initialization after Declaration (Using Loops)

We initialize the array after the declaration by assigning the initial value to each element individually.
We can use for loop, while loop, or do-while loop to assign the value to each element of the array.

for (int i = 0; i < N; i++)

{
array_name[i]=valuei;
}

Example of Array Initialization in C

• C

// C Program to demonstrate array initialization


#include <stdio.h>

int main()

// array initialization using initialier list

int arr[5] = { 10, 20, 30, 40, 50 };

// array initialization using initializer list without

// specifying size

int arr1[] = { 1, 2, 3, 4, 5 };

// array initialization using for loop

float arr2[5];

for (int i = 0; i < 5; i++) {

arr2[i] = (float)i * 2.1;

return 0;

Access Array Elements

We can access any element of an array in C using the array subscript operator [ ] and the index
value i of the element.

array_name [index];

One thing to note is that the indexing in the array always starts with 0, i.e., the first element is at
index 0 and the last element is at N – 1 where N is the number of elements in the array.
Example of Accessing Array Elements using Array Subscript Operator

• C

// C Program to illustrate element access using array

// subscript

#include <stdio.h>

int main()

// array declaration and initialization

int arr[5] = { 15, 25, 35, 45, 55 };

// accessing element at index 2 i.e 3rd element

printf("Element at arr[2]: %d\n", arr[2]);

// accessing element at index 4 i.e last element

printf("Element at arr[4]: %d\n", arr[4]);

// accessing element at index 0 i.e first element

printf("Element at arr[0]: %d", arr[0]);

return 0;

}
Output

Element at arr[2]: 35

Element at arr[4]: 55

Element at arr[0]: 15

Update Array Element

We can update the value of an element at the given index i in a similar way to accessing an element
by using the array subscript operator [ ] and assignment operator =.

array_name[i] = new_value;

C Array Traversal

Traversal is the process in which we visit every element of the data structure. For C array traversal, we
use loops to iterate through each element of the array.

Array Traversal using for Loop

for (int i = 0; i < N; i++) {


array_name[i];
}

How to use Array in C?

The following program demonstrates how to use an array in the C programming language:

• C

// C Program to demonstrate the use of array

#include <stdio.h>
int main()

// array declaration and initialization

int arr[5] = { 10, 20, 30, 40, 50 };

// modifying element at index 2

arr[2] = 100;

// traversing array using for loop

printf("Elements in Array: ");

for (int i = 0; i < 5; i++) {

printf("%d ", arr[i]);

return 0;

Output

Elements in Array: 10 20 100 40 50

Types of Array in C

There are two types of arrays based on the number of dimensions it has. They are as follows:

1. One Dimensional Arrays (1D Array)

2. Multidimensional Arrays

1. One Dimensional Array in C

The One-dimensional arrays, also known as 1-D arrays in C are those arrays that have only one
dimension.

Syntax of 1D Array in C

array_name [size];
Example of 1D Array in C

• C

// C Program to illustrate the use of 1D array

#include <stdio.h>

int main()

// 1d array declaration

int arr[5];

// 1d array initialization using for loop

for (int i = 0; i < 5; i++) {

arr[i] = i * i - 2 * i + 1;

printf("Elements of Array: ");

// printing 1d array by traversing using for loop

for (int i = 0; i < 5; i++) {

printf("%d ", arr[i]);

return 0;

Output

Elements of Array: 1 0 1 4 9
Array of Characters (Strings)

In C, we store the words, i.e., a sequence of characters in the form of an array of characters terminated
by a NULL character. These are called strings in C language.

• C

// C Program to illustrate strings

#include <stdio.h>

int main()

// creating array of character

char arr[6] = { 'G', 'e', 'e', 'k', 's', '\0' };

// printing string

int i = 0;

while (arr[i]) {

printf("%c", arr[i++]);

return 0;

Output

Geeks

2. Multidimensional Array in C

Multi-dimensional Arrays in C are those arrays that have more than one dimension. Some of the
popular multidimensional arrays are 2D arrays and 3D arrays. We can declare arrays with more
dimensions than 3d arrays but they are avoided as they get very complex and occupy a large amount
of space.

A. Two-Dimensional Array in C

A Two-Dimensional array or 2D array in C is an array that has exactly two dimensions. They can be
visualized in the form of rows and columns organized in a two-dimensional plane.
Syntax of 2D Array in C

array_name[size1] [size2];

Here,

• size1: Size of the first dimension.

• size2: Size of the second dimension.

Example of 2D Array in C

• C

// C Program to illustrate 2d array

#include <stdio.h>

int main()

// declaring and initializing 2d array

int arr[2][3] = { 10, 20, 30, 40, 50, 60 };

printf("2D Array:\n");

// printing 2d array

for (int i = 0; i < 2; i++) {

for (int j = 0; j < 3; j++) {

printf("%d ",arr[i][j]);
}

printf("\n");

return 0;

Output

2D Array:

10 20 30

40 50 60

B. Three-Dimensional Array in C

Another popular form of a multi-dimensional array is Three Dimensional Array or 3D Array. A 3D array
has exactly three dimensions. It can be visualized as a collection of 2D arrays stacked on top of each
other to create the third dimension.

Syntax of 3D Array in C

array_name [size1] [size2] [size3];

Example of 3D Array

• C

// C Program to illustrate the 3d array

#include <stdio.h>
int main()

// 3D array declaration

int arr[2][2][2] = { 10, 20, 30, 40, 50, 60 };

// printing elements

for (int i = 0; i < 2; i++) {

for (int j = 0; j < 2; j++) {

for (int k = 0; k < 2; k++) {

printf("%d ", arr[i][j][k]);

printf("\n");

printf("\n \n");

return 0;

Output

10 20

30 40

50 60

00
Relationship between Arrays and Pointers

Arrays and Pointers are closely related to each other such that we can use pointers to perform all the
possible operations of the array. The array name is a constant pointer to the first element of the array
and the array decays to the pointers when passed to the function.

• C

// C Program to demonstrate the relation between arrays and

// pointers

#include <stdio.h>

int main()

int arr[5] = { 10, 20, 30, 40, 50 };

int* ptr = &arr[0];

// comparing address of first element and address stored

// inside array name

printf("Address Stored in Array name: %p\nAddress of "

"1st Array Element: %p\n",

arr, &arr[0]);

// printing array elements using pointers

printf("Array elements using pointer: ");

for (int i = 0; i < 5; i++) {

printf("%d ", *ptr++);

return 0;

Output

Address Stored in Array name: 0x7ffce72c2660

Address of 1st Array Element: 0x7ffce72c2660

Array elements using pointer: 10 20 30 40 50


Passing an Array to a Function in C

An array is always passed as pointers to a function in C. Whenever we try to pass an array to a function,
it decays to the pointer and then passed as a pointer to the first element of an array.

We can verify this using the following C Program:

• C

// C Program to pass an array to a function

#include <stdio.h>

void printArray(int arr[])

printf("Size of Array in Functions: %d\n", sizeof(arr));

printf("Array Elements: ");

for (int i = 0; i < 5; i++) {

printf("%d ",arr[i]);

// driver code

int main()

int arr[5] = { 10, 20, 30, 40, 50 };

printf("Size of Array in main(): %d\n", sizeof(arr));

printArray(arr);

return 0;

Output

Size of Array in main(): 20


Size of Array in Functions: 8

Array Elements: 10 20 30 40 50

Return an Array from a Function in C

In C, we can only return a single value from a function. To return multiple values or elements, we have
to use pointers. We can return an array from a function using a pointer to the first element of that
array.

• C

// C Program to return array from a function

#include <stdio.h>

// function

int* func()

static int arr[5] = { 1, 2, 3, 4, 5 };

return arr;

// driver code

int main()

int* ptr = func();

printf("Array Elements: ");

for (int i = 0; i < 5; i++) {

printf("%d ", *ptr++);

return 0;

Output

Array Elements: 1 2 3 4 5
Note: You may have noticed that we declared static array using static keyword. This is due to the fact
that when a function returns a value, all the local variables and other entities declared inside that
function are deleted. So, if we create a local array instead of static, we will get segmentation fault while
trying to access the array in the main function.

Properties of Arrays in C

It is very important to understand the properties of the C array so that we can avoid bugs while using
it. The following are the main properties of an array in C:

1. Fixed Size

The array in C is a fixed-size collection of elements. The size of the array must be known at the compile
time and it cannot be changed once it is declared.

2. Homogeneous Collection

We can only store one type of element in an array. There is no restriction on the number of elements
but the type of all of these elements must be the same.

3. Indexing in Array

The array index always starts with 0 in C language. It means that the index of the first element of the
array will be 0 and the last element will be N – 1.

4. Dimensions of an Array

A dimension of an array is the number of indexes required to refer to an element in the array. It is the
number of directions in which you can grow the array size.

5. Contiguous Storage

All the elements in the array are stored continuously one after another in the memory. It is one of the
defining properties of the array in C which is also the reason why random access is possible in the
array.

6. Random Access

The array in C provides random access to its element i.e we can get to a random element at any index
of the array in constant time complexity just by using its index number.

7. No Index Out of Bounds Checking

There is no index out-of-bounds checking in C/C++, for example, the following program compiles fine
but may produce unexpected output when run.

• C

// This C program compiles fine

// as index out of bound

// is not checked in C.
#include <stdio.h>

int main()

int arr[2];

printf("%d ", arr[3]);

printf("%d ", arr[-2]);

return 0;

Output

211343841 4195777

In C, it is not a compiler error to initialize an array with more elements than the specified size. For
example, the below program compiles fine and shows just a Warning.

• C

#include <stdio.h>

int main()

// Array declaration by initializing it

// with more elements than specified size.

int arr[2] = { 10, 20, 30, 40, 50 };

return 0;

}
Warnings:

prog.c: In function 'main':


prog.c:7:25: warning: excess elements in array initializer
int arr[2] = { 10, 20, 30, 40, 50 };
^
prog.c:7:25: note: (near initialization for 'arr')
prog.c:7:29: warning: excess elements in array initializer
int arr[2] = { 10, 20, 30, 40, 50 };
^
prog.c:7:29: note: (near initialization for 'arr')
prog.c:7:33: warning: excess elements in array initializer
int arr[2] = { 10, 20, 30, 40, 50 };
^
prog.c:7:33: note: (near initialization for 'arr')

Examples of Array in C

Example 1: C Program to perform array input and output.

In this program, we will use scanf() and print() function to take input and print output for the array.

• C

// C Program to perform input and output on array

#include <stdio.h>

int main()

// declaring an integer array

int arr[5];

// taking input to array elements one by one

for (int i = 0; i < 5; i++) {

scanf("%d", &arr[i]);

}
// printing array elements

printf("Array Elements: ");

for (int i = 0; i < 5; i++) {

printf("%d ", arr[i]);

return 0;

Input

57914

Output

Array Elements: 5 7 9 1 4

Example 2: C Program to print the average of the given list of numbers

In this program, we will store the numbers in an array and traverse it to calculate the average of the
number stored.

• C

// C Program to the average to two numbers

#include <stdio.h>

// function to calculate average of the function

float getAverage(float* arr, int size)

int sum = 0;

// calculating cumulative sum of all the array elements

for (int i = 0; i < size; i++) {

sum += arr[i];

// returning average
return sum / size;

// driver code

int main()

// declaring and initializing array

float arr[5] = { 10, 20, 30, 40, 50 };

// size of array using sizeof operator

int n = sizeof(arr) / sizeof(float);

// printing array elements

printf("Array Elements: ");

for (int i = 0; i < n; i++) {

printf("%.0f ", arr[i]);

// calling getAverage function and printing average

printf("\nAverage: %.2f", getAverage(arr, n));

return 0;

Output

Array Elements: 10 20 30 40 50

Average: 30.00
Example 3: C Program to find the largest number in the array.

• C

// C Program to find the largest number in the array.

#include <stdio.h>

// function to return max value

int getMax(int* arr, int size)

int max = arr[0];

for (int i = 1; i < size; i++) {

if (max < arr[i]) {

max = arr[i];

return max;

// Driver code

int main()

int arr[10]

= { 135, 165, 1, 16, 511, 65, 654, 654, 169, 4 };

printf("Largest Number in the Array: %d",

getMax(arr, 10));

return 0;

}
Output

Largest Number in the Array: 654

Operations on arrays
Search Operation:

In an unsorted array, the search operation can be performed by linear traversal from the first element
to the last element.

// C program to implement linear

// search in unsorted array

#include <stdio.h>

// Function to implement search operation

int findElement(int arr[], int n, int key)

int i;

for (i = 0; i < n; i++)

if (arr[i] == key)

return i;

// If the key is not found

return -1;

}
// Driver's Code

int main()

int arr[] = { 12, 34, 10, 6, 40 };

int n = sizeof(arr) / sizeof(arr[0]);

// Using a last element as search element

int key = 40;

// Function call

int position = findElement(arr, n, key);

if (position == -1)

printf("Element not found");

else

printf("Element Found at Position: %d",

position + 1);

return 0;

Output

Element Found at Position: 5

Insert Operation:

1. Insert at the end:

In an unsorted array, the insert operation is faster as compared to a sorted array because we don’t
have to care about the position at which the element is to be placed.

// C program to implement insert

// operation in an unsorted array.

#include <stdio.h>

// Inserts a key in arr[] of given capacity.

// n is current size of arr[]. This

// function returns n + 1 if insertion

// is successful, else n.

int insertSorted(int arr[], int n, int key, int capacity)

// Cannot insert more elements if n is

// already more than or equal to capacity

if (n >= capacity)

return n;

arr[n] = key;

return (n + 1);

}
// Driver Code

int main()

int arr[20] = { 12, 16, 20, 40, 50, 70 };

int capacity = sizeof(arr) / sizeof(arr[0]);

int n = 6;

int i, key = 26;

printf("\n Before Insertion: ");

for (i = 0; i < n; i++)

printf("%d ", arr[i]);

// Inserting key

n = insertSorted(arr, n, key, capacity);

printf("\n After Insertion: ");

for (i = 0; i < n; i++)

printf("%d ", arr[i]);

return 0;

Output

Before Insertion: 12 16 20 40 50 70

After Insertion: 12 16 20 40 50 70 26

2. Insert at any position

Insert operation in an array at any position can be performed by shifting elements to the right, which
are on the right side of the required position
// C Program to Insert an element

// at a specific position in an Array

#include <stdio.h>

// Function to insert element

// at a specific position

void insertElement(int arr[], int n, int x, int pos)

// shift elements to the right

// which are on the right side of pos

for (int i = n - 1; i >= pos; i--)

arr[i + 1] = arr[i];

arr[pos] = x;

// Driver's code

int main()

{
int arr[15] = { 2, 4, 1, 8, 5 };

int n = 5;

printf("Before insertion : ");

for (int i = 0; i < n; i++)

printf("%d ", arr[i]);

printf("\n");

int x = 10, pos = 2;

// Function call

insertElement(arr, n, x, pos);

n++;

printf("After insertion : ");

for (int i = 0; i < n; i++)

printf("%d ", arr[i]);

return 0;

Output

Before insertion : 2 4 1 8 5

After insertion : 2 4 10 1 8 5
Delete Operation:

In the delete operation, the element to be deleted is searched using the linear search, and then the
delete operation is performed followed by shifting the elements.

// C program to implement delete operation in a

// unsorted array

#include <stdio.h>

// To search a key to be deleted

int findElement(int arr[], int n, int key);

// Function to delete an element

int deleteElement(int arr[], int n, int key)

// Find position of element to be deleted

int pos = findElement(arr, n, key);

if (pos == -1) {

printf("Element not found");

return n;

// Deleting element
int i;

for (i = pos; i < n - 1; i++)

arr[i] = arr[i + 1];

return n - 1;

// Function to implement search operation

int findElement(int arr[], int n, int key)

int i;

for (i = 0; i < n; i++)

if (arr[i] == key)

return i;

return -1;

// Driver's code

int main()

int i;

int arr[] = { 10, 50, 30, 40, 20 };

int n = sizeof(arr) / sizeof(arr[0]);

int key = 30;

printf("Array before deletion\n");

for (i = 0; i < n; i++)

printf("%d ", arr[i]);


// Function call

n = deleteElement(arr, n, key);

printf("\nArray after deletion\n");

for (i = 0; i < n; i++)

printf("%d ", arr[i]);

return 0;

Output

Array before deletion

10 50 30 40 20

Array after deletion

10 50 40 20

Advantages of Array in C

The following are the main advantages of an array:

1. Random and fast access of elements using the array index.

2. Use of fewer lines of code as it creates a single array of multiple elements.

3. Traversal through the array becomes easy using a single loop.

4. Sorting becomes easy as it can be accomplished by writing fewer lines of code.

Disadvantages of Array in C

1. Allows a fixed number of elements to be entered which is decided at the time of declaration.
Unlike a linked list, an array in C is not dynamic.

2. Insertion and deletion of elements can be costly since the elements are needed to be
rearranged after insertion and deletion.

from-
Manasa G
Asst. Prof,
Dept. of R&A, RRCE.

You might also like

pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy