UNIT - IV: Function and Dynamic Memory Allocation: Functions: Declaring a function, Signature of a function, Parameters and return type of a function, passing parameters to functions, call by value, call by reference, Passing arrays to functions, passing pointers to functions. Recursion: Simple programs, such as Finding Factorial, Fibonacci series etc., Limitations of Recursive functions. Dynamic memory allocation: Allocating and freeing memory, Allocating memory for arrays of different data types, Storage classes (auto, extern, static and register) Function: A function is a block of code which only runs when it is called. We can pass data, known as parameters, into a function. Functions are used to perform certain actions, and they are important for reusing code: Define the code once, and use it many times. (or) In c, we can divide a large program into the basic building blocks known as function. The function contains the set of programming statements enclosed by {}. A function can be called multiple times to provide reusability and modularity to the C program. In other words, we can say that the collection of functions creates a program. Advantage of functions There are the following advantages of C functions. By using functions, we can avoid rewriting same logic/code again and again in a program. We can call C functions any number of times in a program and from any place in a program. We can track a large C program easily when it is divided into multiple functions. Reusability is the main achievement of C functions. Function Aspects: There are three aspects of a C function. Function declaration: A function must be declared globally in a c program to tell the compiler about the function name, function parameters, and return type. Syntax: return_type function_name (argument list); Function call: Function can be called from anywhere in the program. The parameter list must not differ in function calling and function declaration. We must pass the same number of functions as it is declared in the function declaration. Syntax: function_name (argument_list) Function definition: It contains the actual statements which are to be executed. It is the most important aspect to which the control comes when the function is called. Here, we must notice that only one value can be returned from the function. Syntax: return_type function_name (argument list) { function body; //can be local var, stmts, expressions } Types of Functions: There are two types of functions in C programming: Library Functions: are the functions which are declared in the C header files such as scanf(), printf(), gets(), puts(), ceil(), floor() etc. User-defined functions: are the functions which are created by the C programmer, so that he/she can use it many times. It reduces the complexity of a big program and optimizes the code. Declaring a function: To create (often referred to as declare) your own function, specify the name of the function, followed by parentheses () and curly brackets {}: Syntax void myFunction() { // code to be executed } myFunction() is the name of the function void means that the function does not have a return value. Inside the function (the body), add code that defines what the function should do Call a Function Declared functions are not executed immediately. They are "saved for later use", and will be executed when they are called. To call a function, write the function's name followed by two parentheses () and a semicolon ; and function call should be inside main() Syntax: Void main() { Function_name(); } Function signature: A function signature (or type signature, or method signature) defines input and output of functions or methods. A signature can include: parameters and their types. ... information about the availability of the method in an object-oriented program (such as the keywords public , static , or prototype ). Examples on Functions: // Create a function void myFunction() { printf("I just got executed!"); } int main() { myFunction(); // call the function return 0; } Output: I just got executed! Note a function can be called multiple times: Example: void myFunction() { printf("I just got executed!"); } int main() { myFunction(); myFunction(); myFunction(); return 0; } Output: I just got executed! I just got executed! I just got executed! Parameters and return type of a function: A function may or may not accept any argument. It may or may not return any value. Based on these facts, There are four different aspects of function calls. 1. 2. 3. 4. function without arguments and without return value function without arguments and with return value function with arguments and without return value function with arguments and with return value Syntax: returnType functionName(parameter1, parameter2, parameter3) { // code to be executed } When a parameter is passed to the function, it is called an argument. Example: #include <stdio.h> void myFunction(int age) { printf("Hello %d\n", age); } int main() { myFunction(1); myFunction(2); myFunction(3); return 0; } Output: 1 2 3 A function consist of two parts: Declaration: the function's name, return type, and parameters (if any) Definition: the body of the function (code to be executed) Syntax: return_type func-name(args list); //function declaration void main() //main method { func-name(); //function call } return_type func-name(args list) { Var; Stmts; Expressions; } Return Values The void keyword, used in the previous examples, indicates that the function should not return a value. If you want the function to return a value, you can use a data type (such as int or float, etc.) instead of void, and use the return keyword inside the function: Example int myFunction(int x) { return 5 + x; } int main() { printf("Result is: %d", myFunction(3)); return 0; } Output: Result is: 8 passing parameters to functions: Example for Function without argument and without return value #include<stdio.h> void printName(); void main () { printf("Hello "); printName(); } void printName() { printf("C Program"); } Output: Hello C Program Example 2: calculate the sum of two numbers: #include<stdio.h> void sum(); void main() { printf("\nGoing to calculate the sum of two numbers:"); sum(); } void sum() { int a,b; printf("\nEnter two numbers"); scanf("%d %d",&a,&b); printf("The sum is %d",a+b); } output: Going to calculate the sum of two numbers: Enter two numbers 10 24 The sum is 34 Example for Function without argument and with return value Example1 #include<stdio.h> int sum(); void main() { int result; printf("\nGoing to calculate the sum of two numbers:"); result = sum(); printf("%d",result); } int sum() { int a,b; printf("\nEnter two numbers"); scanf("%d %d",&a,&b); return a+b; } output: Going to calculate the sum of two numbers: Enter two numbers 10 24 The sum is 34 Example2: program to calculate the area of the square #include<stdio.h> int sum(); void main() { printf("Going to calculate the area of the square\n"); float area = square(); printf("The area of the square: %f\n",area); } int square() { float side; printf("Enter the length of the side in meters: "); scanf("%f",&side); return side * side; } Output: Going to calculate the area of the square Enter the length of the side in meters: 10 The area of the square: 100.000000 Example for Function with argument and without return value Example1 #include<stdio.h> void sum(int, int); void main() { int a,b,result; printf("\nGoing to calculate the sum of two numbers:"); printf("\nEnter two numbers:"); scanf("%d %d",&a,&b); sum(a,b); } void sum(int a, int b) { printf("\nThe sum is %d",a+b); } Output Going to calculate the sum of two numbers: Enter two numbers 10 24 The sum is 34 Example2: program to calculate the average of five numbers. #include<stdio.h> void average(int, int, int, int, int); void main() { int a,b,c,d,e; printf("\nGoing to calculate the average of five numbers:"); printf("\nEnter five numbers:"); scanf("%d %d %d %d %d",&a,&b,&c,&d,&e); average(a,b,c,d,e); } void average(int a, int b, int c, int d, int e) { float avg; avg = (a+b+c+d+e)/5; printf("The average of given five numbers : %f",avg); } Output Going to calculate the average of five numbers: Enter five numbers:10 20 30 40 50 The average of given five numbers: 30.000000 Example for Function with argument and with return value Example1 #include<stdio.h> int sum(int, int); void main() { int a,b,result; printf("\nGoing to calculate the sum of two numbers:"); printf("\nEnter two numbers:"); scanf("%d %d",&a,&b); result = sum(a,b); printf("\nThe sum is : %d",result); } int sum(int a, int b) { return a+b; } Output Going to calculate the sum of two numbers: Enter two numbers:10 20 The sum is : 30 Example 2: Program to check whether a number is even or odd #include<stdio.h> int even_odd(int); void main() { int n,flag=0; printf("\nGoing to check whether a number is even or odd"); printf("\nEnter the number: "); scanf("%d",&n); flag = even_odd(n); if(flag == 0) { printf("\nThe number is odd"); } else { printf("\nThe number is even"); } } int even_odd(int n) { if(n%2 == 0) { return 1; } else { return 0; } } Output Going to check whether a number is even or odd Enter the number: 100 The number is even Call by Value: In this particular parameter passing method, the values of the actual parameters copy into the function’s formal parameters. It stores both types of parameters in different memory locations. Thus, if one makes any changes inside the function- it does not show on the caller’s actual parameters. This method passes a copy of an actual argument to the formal argument of any called function. In the case of a Call by Value method, any changes or alteration made to the formal arguments in a called function does not affect the overall values of an actual argument. Thus, all the actual arguments stay safe, and no accidental modification occurs to them. Call by Reference: In this case, both the formal and actual parameters refer to a similar location. It means that if one makes any changes inside the function, it gets reflected in the caller’s actual parameters. This method passes the address or location of the actual arguments to the formal arguments of any called function. It means that by accessing the actual argument’s addresses, one can easily alter them from within the called function. Thus, in Call by Reference, it is possible to make alterations to the actual arguments. Thus, the code needs to handle the arguments very carefully. Or else, there might be unexpected results and accidental errors. Call by Value vs. Call by Reference: Parameters Call by value Call by reference Definition While calling a function, in programming language While calling a function, when you pass instead of copying the values of variables, the address values by copying variables, it is known of the variables is used it is known as “Call By as “Call By Values.” References. Arguments In this method, a copy of the variable is In this method, a variable itself is passed. passed. Effect Changes made in a copy of variable never modify the value of variable outside the function. Change in the variable also affects the value of the variable outside the function. Parameters Call by value Call by reference Alteration of value Does not allow you to make any changes in the actual variables. Allows you to make changes in the values of variables by using function calls. Passing of variable Values of variables are passed using a Pointer variables are required to store the address of straightforward method. variables. Value modification Original value not modified. The original value is modified. Memory Location Actual and formal arguments will be created in different memory location Actual and formal arguments will be created in the same memory location Safety Actual arguments remain safe as they cannot be modified accidentally. Actual arguments are not Safe. They can be accidentally modified, so you need to handle arguments operations carefully. Default Default in many programming languages like C++.PHP. Visual Basic NET, and C#. It is supported by most programming languages like JAVA, but not as default. Program to implement call by value #include<stdio.h> void swap(int ,int); int main() { int a,b; printf(“Enter the a, b values\n”); scanf(“%d%d”,&a,&b); printf(“Before swapping a, b values are %d, %d\n”,a,b); swap(a,b); printf(“After swapping a, b values are %d, %d\n”,a,b); return 0; } void swap(int x,int y) { int z; z=x; x=y; y=z; } Output: Enter the a, b values: 2 3 Before swapping a, b values are 2, 3 After swapping a, b values are 2 3 A program to implement call by refers #include<stdio.h> main() { int a=10,b=20,c; clrscr(); c=add(&a,&b); printf("%d",c); getch(); } add(int *x,int *y) { int z; z=*x+*y; return(z); } Output: 30 Passing arrays to functions: Consider the following syntax to pass an array to the function. functionname(arrayname);//passing array Methods to declare a function that receives an array as an argument There are 3 ways to declare the function which is intended to receive an array as an argument. First way: return_type function(type arrayname[]) Declaring blank subscript notation [] is the widely used technique. Second way: return_type function(type arrayname[SIZE]) Optionally, we can define size in subscript notation []. Third way: return_type function(type *arrayname) Example: #include <stdio.h> void myFunction(int myNumbers[5]) { for (int i = 0; i < 5; i++) { printf("%d\n", myNumbers[i]); } } int main() { int myNumbers[5] = {10, 20, 30, 40, 50}; myFunction(myNumbers); return 0; } // Program to calculate the sum of array elements by passing to a function #include <stdio.h> float calculateSum(float num[]); int main() { float result, num[] = {23.4, 55, 22.6, 3, 40.5, 18}; // num array is passed to calculateSum() result = calculateSum(num); printf("Result = %.2f", result); return 0; } float calculateSum(float num[]) { float sum = 0.0; for (int i = 0; i < 6; ++i) { sum += num[i]; } return sum; } Output Result = 162.50 passing pointers to functions: C programming allows passing a pointer to a function. To do so, simply declare the function parameter as a pointer type. #include<stdio.h> void Swap(int *x, int *y) { int Temp; Temp = *x; *x = *y; *y = Temp; } int main() { int a, b; printf ("Please Enter 2 Integer Values : "); scanf("%d %d", &a, &b); printf("\nBefore Swapping A = %d and B = %d", a, b); Swap(&a, &b); printf(" \nAfter Swapping A = %d and B = %d \n", a, b); } OUTPUT: Please Enter 2 Integer Values : 20 50 Before Swapping A = 20 and B = 50 After Swapping A = 50 and B = 20 In this Pass Pointers to Functions program, we created a function that accepts the array pointer and its size. Please refer to the C program to find the Sum of All Elements in an Array article to know the logic. Within the main Pass Pointers to Functions program, we used for loop to iterate the array. Next, pass the user given value to an array. Next, we pass an array to a function #include<stdio.h> int SumofArrNumbers(int *arr, int Size) { int sum = 0; for(int i = 0; i < Size; i++) { sum = sum + arr[i]; } return sum; } int main() { int i, Addition, Size, a[10]; printf("Please Enter the Size of an Array : "); scanf("%d", &Size); printf("\nPlease Enter Array Elements : "); for(i = 0; i < Size; i++) { scanf("%d", &a[i]); } Addition = SumofArrNumbers(a, Size); printf("Sum of Elements in this Array = %d \n", Addition); return 0; } OUTPUT: Please Enter the Size of an Array : 7 Please Enter Array Elements : 10 20 200 30 500 40 50 Sum of Elements in this Array = 850 Recursion: Recursion is the technique of making a function call itself. This technique provides a way to break complicated problems down into simple problems which are easier to solve, may be a bit difficult to understand. Example: int factorial(int num) { if(num != 0) { num=num*factorial(num-1); } return num; } Examples: Sum of Natural Numbers Using Recursion #include <stdio.h> int sum(int n); int main() { int number, result; printf("Enter a positive integer: "); scanf("%d", &number); result = sum(number); printf("sum = %d", result); return 0; } int sum(int n) { if (n != 0) // sum() function calls itself return n + sum(n-1); else return n; } OUTPUT: Enter a positive integer:3 sum = 6 The factorial of a given number using a recursive function #include <stdio.h> unsigned long long int factorial(unsigned int i) { if(i <= 1) { return 1; } return i * factorial(i - 1); } int main() { int i = 12; printf("Factorial of %d is %d\n", i, factorial(i)); return 0; } OUTPUT: Factorial of 12 is 479001600 The Fibonacci series for a given number using a recursive function #include <stdio.h> int fibonacci(int i) { if(i == 0) { return 0; } if(i == 1) { return 1; } return fibonacci(i-1) + fibonacci(i-2); } int main() { int i; for (i = 0; i < 10; i++) { printf("%d\t\n", fibonacci(i)); } return 0; } OUTPUT: When the above code is compiled and executed, it produces the following result − 0 1 1 2 3 5 8 13 21 34 Write a program in C to find GCD of two numbers using recursion. #include<stdio.h> int findGCD(int num1,int num2); int main() { int num1,num2,gcd; printf("\n\n Recursion : Find GCD of two numbers :\n"); printf("------------------------------------------\n"); printf(" Input 1st number: "); scanf("%d",&num1); printf(" Input 2nd number: "); scanf("%d",&num2); gcd = findGCD(num1,num2); printf("\n The GCD of %d and %d is: %d\n\n",num1,num2,gcd); return 0; } int findGCD(int a,int b) { while(a!=b) { if(a>b) return findGCD(a-b,b); else return findGCD(a,b-a); } return a; } Output: Recursion : Find GCD of two numbers : -----------------------------------------Input 1st number: 10 Input 2nd number: 50 The GCD of 10 and 50 is: 10 Limitations of Recursive functions: 1. Recursive solutions may involve extensive overhead because they use function calls. Each function call requires push of return memory address, parameters, returned result,etc. and every function return requires that many pops. 2. Each time you call a function you use up some of your memory allocation may be in stack or heap. If there are large number of recursive calls – then you may run out of memory. When to Use: For large number of iterations, use loops (iterative approach). For small number of iterations, use recursion. Dynamic memory allocation: Allocating and freeing memory Dynamic memory allocation in c language enables the C programmer to allocate memory at runtime. Dynamic memory allocation in c language is possible by 4 functions of stdlib.h header file. 1. malloc() 2. calloc() 3. realloc() 4. free() Difference between static memory allocation and dynamic memory allocation. static memory allocation dynamic memory allocation memory is allocated at compile time. memory is allocated at run time. memory can't be increased while executing program. memory can be increased while executing program. used in array. used in linked list. Methods used for dynamic memory allocation. 1. malloc() allocates single block of requested memory. calloc() allocates multiple block of requested memory. realloc() reallocates the memory occupied by malloc() or calloc() functions. free() frees the dynamically allocated memory. malloc() function in C The malloc() function allocates single block of requested memory. It doesn't initialize memory at execution time, so it has garbage value initially. It returns NULL if memory is not sufficient. The syntax of malloc() function: ptr=(cast-type*)malloc(byte-size) Program for sum of array element using malloc function: #include <stdio.h> #include <stdlib.h> int main() { int n, i, *ptr, sum = 0; printf("Enter number of elements: "); scanf("%d", &n); ptr = (int*) malloc(n * sizeof(int)); if(ptr == NULL) { printf("Error! memory not allocated."); exit(0); } printf("Enter elements: "); for(i = 0; i < n; ++i) { scanf("%d", ptr + i); sum += *(ptr + i); } printf("Sum = %d", sum); free(ptr); return 0; } Output: Enter number of elements:5 Enter elements: 1 3 2 3 5 Sum =14 2. calloc() function in C The calloc() function allocates multiple block of requested memory. It initially initialize all bytes to zero. It returns NULL if memory is not sufficient. The syntax of calloc() function: ptr=(cast-type*)calloc(number, byte-size) Program for smallest number in an array using calloc function #include <stdio.h> #include <stdlib.h> int main() { int i, n; int *element; printf("Enter total number of elements: "); scanf("%d", &n); /* returns a void pointer(which is type-casted to int*) pointing to the first block of the allocated space */ element = (int*) calloc(n,sizeof(int)); /* If it fails to allocate enough space as specified, it returns a NULL pointer.*/ if(element == NULL) { printf("Error.Not enough space available"); exit(0); } for(i = 0; i < n; i++) { /* storing elements from the user in the allocated space */ scanf("%d", element+i); } for(i = 1; i < n; i++) { if(*element > *(element+i)) { *element = *(element+i); } } printf("Smallest element is %d", *element); return 0; } Output: Enter total number of elements: 5 21 5 6 3 8 20 Smallest element is 3 3. realloc() function in C If memory is not sufficient for malloc() or calloc(), you can reallocate the memory by realloc() function. In short, it changes the memory size. syntax of realloc() function: ptr=realloc(ptr, new-size) Example Program for realloc #include <stdio.h> #include <stdlib.h> int main() { int *ptr = (int *)malloc(sizeof(int)*2); int i; int *ptr_new; *ptr = 10; *(ptr + 1) = 20; ptr_new = (int *)realloc(ptr, sizeof(int)*3); *(ptr_new + 2) = 30; for(i = 0; i < 3; i++) printf("%d ", *(ptr_new + i)); return 0; } Output: 10 20 30 4. free() function in C The memory occupied by malloc() or calloc() functions must be released by calling free() function. Otherwise, it will consume memory until program exit. syntax of free() function: free(ptr) Allocating memory for arrays of different data types: C program to create memory for int, char and float variable at run time. #include <stdio.h> #include <stdlib.h> int main() { int *iVar; char *cVar; float *fVar; /*allocating memory dynamically*/ iVar=(int*)malloc(1*sizeof(int)); cVar=(char*)malloc(1*sizeof(char)); fVar=(float*)malloc(1*sizeof(float)); printf("Enter integer value: "); scanf("%d",iVar); printf("Enter character value: "); scanf(" %c",cVar); printf("Enter float value: "); scanf("%f",fVar); printf("Inputted value are: %d, %c, %.2f\n",*iVar,*cVar,*fVar); /*free allocated memory*/ free(iVar); free(cVar); free(fVar); return 0; } Output: Enter integer value:5 Enter character value:c Enter float value:5.8 Inputted value are 5,c,5.8 Storage classes (auto, extern, static and register): Storage Classes are used to describe the features of a variable/function. These features basically include the scope, visibility and life-time 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. 2. 3. 4. 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. 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. 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. 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 registration 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 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. To specify the storage class for a variable, the following syntax is to be followed : Syntax: storage_class var_data_type var_name; //Write a program in C to print Automatic storage class #include <stdio.h> void main() { int a = 10,i; printf("%d ",++a); { int a = 20; for (i=0;i<3;i++) { printf("%d ",a); // 20 will be printed 3 times since it is the local value of a } } printf("%d ",a); // 11 will be printed since the scope of a = 20 is ended. } Output: 11 20 20 20 11 //Write a program in C to print Static storage class #include<stdio.h> void sum() { static int a = 10; static int b = 24; printf("%d %d \n",a,b); a++; b++; //printf("%d %d \n",a,b); } void main() { int i; for(i = 0; i< 3; i++) { sum(); // The static variables holds their value between multiple function calls. } } Output: 10 24 11 25 12 26 //Write a program in C to print Register storage class #include <stdio.h> void main() { register int b; // variable a is allocated memory in the CPU register. The initial default value of a is 1. printf("%d",b); } /* int b; Printf(“%d”,b) --- (o/p: 0-garbage value) Register int b=20; printf("%d",b); --- (o/p: 20) */ //Register variables were supposed to be faster than local variables. Output: 1 //Write a program in C to print External storage class #include <stdio.h> void main() { extern int a; // Compiler will search here for a variable a defined and initialized somewhere in the pogram or not. printf("%d",a); } int a = 20; Output: 20