Download C programming data structure and algorithms and more Study notes Data Structures and Algorithms in PDF only on Docsity!
FUNCTIONS
A function is a block of code meant to perform a defined set of tasks. In many cases
these tasks are meant to be repeated several times in a program.
A function has two parts and these are:
1. A head also known as its prototype
2. A body
The head is made up of
1. The type of value (data type e.g. int) the function is to return when it completes
its task. If the function doesn’t return any data type, the word void is used as
return type.
2. The name of the function (a valid identifier)
3. List of parameters enclosed in brackets (comma-separated list of data type-valid
identifier pairs). If the function does not require a parameter, the brackets are
kept empty.
The name of the function and its parameter list put together, makes up what is called
the signature of the function.
The body (definition) of a function is a list of programming statements enclosed in
braces immediately after the head of the function
The structure of functions are as given below:
1. For a function that takes parameters
type name (type id, type id, …, type id)
//Body of function
2. For a function that doesn’t take a parameter
type name ()
//Body of function
The codes for most of the common programming tasks one can think of, have already being
provided in header files that make up the C library. As a result of this, it is not
necessary for a programmer to write his or her own codes for these tasks (reinventing
the wheel).
In order to use any of these functions, the header in which it has been defined must be
specified. This is done by using the format #include <header_name.h> , which I believe is
a familiar programming statement by now.
SOME LIBRARY FUNCTIONS
Popular mathematical functions are provided in the header math.h. Some of these functions
are given below:
S/N Function Prototype Description
Found in math.h
1 asin() double asin(double x) Returns the value of sin-^1 (x) which is an angle
in radians
2 acos() double acos(double x) Returns cos-^1 (x) which is also an angle in
radians
3 atan() double atan(double x) Returns tan-^1 (x) in radians as well
4 sin() double sin(double x) Returns the value of sine(x) which is a ratio
(no unit)
5 cos() double cos(double x) Returns the value of cosine(x) which is also a
ratio
6 tan() double tan(double x) Returns the value of the tangent of x (also a
ratio)
7 sinh() double sinh(double x) sinh(x) returns the value of the hyperbolic sine
of x
8 cosh() double cosh(double x) cosh(x) returns the value of the hyperbolic
cosine of x
9 tanh() double tanh(double x) cosh(x) returns the value of the hyperbolic
tangent of x
From stdlib.h
10 abs() int abs(int x) abs(x) returns the absolute value of x
11 ceil() double ceil(double x) ceil(x) = ⌈𝑥⌉ represents the nearest whole
number which is not less than x e.g. ceil(2.5)
= 3.0, ceil(3.0) = 3.0, ceil(-2.3) = - 2.
12 floor() double floor(double x) floor(x) = ⌊𝑥⌋ represents the nearest whole
number which is not greater than x e.g.
floor(2.5) = 2, floor(3.0) = 3.0, floor(-2.3) =
13 pow() double pow(double x, double y) Returns the value of xy
14 sqrt() double sqrt(double x) Returns the value of √𝑥
15 cbrt() double cbrt(double x) Returns the value of 3 √𝑥
Calling a function requires providing its name and arguments (representing parameters in
the prototype), if that is necessary. The number of arguments provided must match the
number of parameters, both in order and data type. For example pow(3.0, 2.0), cbrt(27.0),
e.t.c. This is normally done from the main function or another function.
Example
#include <stdio.h> #include <math.h> #include <stdlib.h> int main () { double a = cbrt ( 27.0 ); double b = ceil (- 1.0004 ); double c = floor (- 1.004 ); printf ( "cbrt(27.0) = %lf\n" , a ); printf ( "ceil(-1.0004) = %lf\n" , b ); printf ( "floor(-1.004) = %lf\n" , c ); return 0 ; } Output cbrt(27.0) = 3. ceil(-1.0004) = -1. floor(-1.004) = -2. DEVELOPING A FUNCTION
In most cases a function take some values, does some computation with these values in
the body and return the result of the computation afterwards. The ingredients needed to
develop a function in order of importance are:
1. Parameters which represent the input values needed in the function. If the function
requires none, it the brackets are kept empty.
2. The return type i.e. the type of value to be returned after the computation in the
body. In the body of the function, the return keyword is used to send back the
computed result. The format is return value;.
3. The name of the function
Example
Develop a function that computes that computes the volume of a cylinder.
Paramters : radius (r), height (h) all of type double.
POSITIONING A DEVELOPED FUNCTION IN A PROGRAM
So far the task has been developing functions in isolation. The time has come to develop and use one in
an actual program. There are two approaches to this.
1. Placing the functions before the main function in a program
2. Placing the functions to after the main function.
Using the first approach is my personal favorite, because as will be shown below, it gives a program with
fewer lines of code. This is because the second approach requires placing the prototype of the functions
before the main function, after which the whole function is the then provided under the main function.
Example
The program below is developed to use the function created above.
Method One
#include <stdio.h> #include <math.h> int main () { // input values double a = 5.0 ; double b = 4.0 ; double c = 6.0 ; // compute s double s = ( a + b + c )/ 2 ; // compute area double area = sqrt ( s ( s - a )( s - b )(* s - c )); // display area printf ( "Area = %lf\n" , area ); return 0; } Method Two #include <stdio.h> #include <math.h> // prototype of function double triangleArea ( double a , double b , double c ); int main () { double area = triangleArea ( 4.0 , 5.0 , 6.0 ); printf ( "Area = %lf\n" , area ); return 0; } // function defined under the main function double triangleArea ( double a , double b , double c ) { // compute s double s = ( a + b + c )/ 2 ; // compute area double area = sqrt ( s ( s - a )( s - b )(* s - c )); // Return computed value return area ; }
BENEFITS OF USING FUNCTIONS
1. Smaller source files: Using a function provides a way of representing several lines of code with just
a line of code, thus reducing the size of the source file. Instead of repeating these tasks at all
points where these tasks are to be performed.
- Ease of maintenance: if there is the need to make a modification to how the task of a function is
to be carried out, it will require just making the change in one spot, which is in the code implementing
the function, instead of several places if a function is not used.
EXERCISES
1. Write a function that will calculate the nth root of fractional number x i.e. √𝑥
𝑛
2. Write a program that will compute and display the roots (real or complex) of a quadratic equation.
3. Derive a function called quadRoot from the program in (2)
Hint : Make the return type void, because the result of the program may be more than one value.
Just focus on making the function display the roots.
RECURSION
This is a situation where a function invokes itself with a modification to its original argument. This recalling
and modification of argument continues until a base argument obtained, at which point the self-recalling
activity stops.
This process in which a function calls itself in trying to compute its output is called recursion. Among the
many computations available that illustrate the concept of recursion, attention will be given to two.
Factorial of a Number
It can be expressed that
6! = 6 × 5 × 4 × 3 × 2 × 1
5! = 5 × 4 × 3 × 2 × 1
4! = 4 × 3 × 2 × 1
This implies that
6! = 6 × 5!
5! = 5 × 4!
Going by this, it can therefore be said that, the general form of calculating the factorial of a number n can
be written as:
n! = n × (n-1)!
However it is important to state that, 1! and 0! will give a value of 1, thus making 1 the base argument.
This implies that the argument used in the recursive function (factorial) will continue to reduce until it gets
to 1 where recursion stops. With these facts established, the function for computing the factorial of a number
can be written as follows:
int factorial(int n)
if(n == 1 || n == 0)
return 1;
else
return n*factorial(n -1);//function calling itself
F1 = 1
F2 = 1 + 0 = 1
F3 = 1 + 1 = 2
F4 = 1 + 2 = 3
F5 = 2 + 3 = 5
The generalized expression for all terms from the third is given as:
Fn = Fn-2 + Fn-
From the general expression, the function to display the nth term of the Fibonacci series, is as given below:
int fibo(int n)
if(n == 0 || n == 1)
return n;
else
return fibo(n – 1) + fibo(n – 2);
Example
Write a program to display the first 10 terms of the Fibonacci series, using the format below:
Solution #include <stdio.h> int fib ( int n ) { if( n == 0 || n == 1 ) return n ; else return fib ( n - 1 ) + fib ( n - 2 ); } int main () { int i ; for( i = 0 ; i <= 10 ; i ++) { printf ( "F(%2d) = %d\n" , i , fib ( i )); } return 0 ; } RANDOM NUMBER GENERATION
The function rand() which is found the header stdlib.h is used for generating random numbers in C
programs. The function generates a random number from 0 to RAND_MAX, which is a constant value defined
in the header stdlib.h. The value of RAND_MAX is at least 32767.
A program using rand() to generate random numbers will always generate exactly the same set of random
numbers. This is the case even when the program is closed and reopened for execution.
Example
#include <stdio.h> #include <stdlib.h> int main () { int i ; //generate 4 random numbers for( i = 0 ; i < 4 ; i ++) printf ( "%d\n" , rand ()); return 0 ; }
1 st^ Run
2 nd^ Run
In order to prevent this from happening the function srand() is used. It takes positive number (unsigned
number which is the same as an unsigned int) as argument. This number is called a seed. When this
function is used, the random numbers generated change as the seed is changed.
Note: placeholder or format specifier for unsigned int or simply unsigned is u.
Example
#include <stdio.h> #include <stdlib.h> int main () { int i , j , seed ; srand ( time (NULL)); for( i = 0 ; i < 4 ; i ++) { printf ( "Enter a seed: " ); scanf ( "%u" , & seed ); srand ( seed ); for( j = 0 ; j < 4 ; j ++) printf ( "%d\n" , rand ()); printf ( "\n\n" ); } return 0 ; }
1 st^ Run
Enter a seed: 2
2 nd^ Run
If there is an intention to reduce the maximum number that can be generated to say n, then using
rand()%(n+1) will do the job. With that said, If this function is to be used for generating the numbers
on the face of a die, it will require getting numbers from 0 to 5 using rand()%6. In order to get the
value from 0 – 5 to 1 – 6, the formula will become 1 + rand()%6.
Example
#include <stdio.h> #include <stdlib.h> int main () { int i , j ; srand ( time (NULL)); for( j = 0 ; j < 8 ; j ++) printf ( "%d\n" , 1 + rand ()% 6 ); return 0 ; }
Output
Note: It is important to state that the function and string argument system(“cls”) can be used to clear
the output of a screen while a program is still in execution.
Example
#include <stdio.h> #include <stdlib.h> int main () { int i , j , seed ; for( i = 0 ; i < 4 ; i ++) { srand ( seed ); for( j = 0 ; j < 4 ; j ++) printf ( "Seed: %u\tNumber: %d\n" , seed , rand ()); } system ( "cls" ); printf ( "Output has been cleared\n" ); return 0 ; }
Output
Output has been cleared.
ARRAYS
It is a cluster of variables of the same type. The following are the facts to note about arrays:
1. Each variable in the cluster is called an element.
2. The number of variables in the arrays is called the size/dimension/length of the array.
3. The cluster of variables share a single name.
4. The position of a variable in the cluster is represented by a non-negative whole number called its
index.
5. The index of the first element is 0, while that of the last is size – 1.
Arrays are used when there is the need to work with a lot of variables of the same type. Using arrays
makes the process a lot simpler than declaring each variable separately.
CREATING ARRAYS
The syntax creating an array is as given below:
Method 1 :
type name[size]; e.g int scores[30];
In most cases, the size of the array is provided as a constant declared before any function. This is done
by using the format: #define NAME value e.g. #define SIZE 10
This is placed immediately after all headers have been introduced.
Method 2 (initialization method):
type name[] = {value, value, …, value}; e.g. int ages[] = {6, 15, 89, 7};.
The size of the array is equal to the number of values provided for initialization. That of the array (ages)
in the example is 4.
REFERENCING ELEMENTS IN AN ARRAY
In order to display the value of an element, or assign to it a value, or use in its value in some computational
process, it has to be referenced (get a hold of it). Every element in an array is referenced using the form:
name[index] e.g. ages[0].
In most cases, all the elements of an array are accessed together, one after the other. The most efficient
way of doing this is using a loop (since index values of successive elements differ by 1) and the people’s
favorite for this, is the for-loop. The elements are used in the body of the loop. One element per iteration
of the loop. The loop format is
for(index = 0; index < size; index++)
// array[index] is used to reference an element
DISPLAYING THE VALUES IN AN ARRAY
An array created with its size specified explicitly is uninitialized and an as such its elements contain values
that can’t be predicted. The example below illustrates this concept and also provides a means of appreciating
the use of the for-loop for accessing array elements.
Access with loop
#include <stdio.h> #include <stdlib.h> int main () { int score [ 6 ]; int i ; for( i = 0 ; i < 6 ; i ++) { printf ( "%d\n" , score [ i ]);
printf ( "%d\n" , scores [ index ]); } // display content of ages printf ( "\n\nContent of the ages-array\n" ); for( index = 0 ; index < 7 ; index ++) { printf ( "%d\n" , ages [ index ]); } return 0 ; }
Output
Content of the scores-array
Content of the ages-array
It is important to state at this juncture that if the size of an array is specified in a variable
ENTERING DATA INTO THE ELEMENTS
The pattern is not different from what is done with an ordinary variable. Doing this requires providing the
format specifier as well as the address of the element by using the Amper’s AND symbol (&). The format
for an int array is scanf(“%d”, &name[index]);
Code
#include <stdio.h> #include <stdlib.h> int main () { int scores [ 4 ] = { 45 }; // providing just a value int i ; // index variable // enter values into array printf ( "Enter 4 numbers\n" ); for( i = 0 ; i < 4 ; i ++) { scanf ( "%d" , & scores [ i ]); } // print values that have been entered printf ( "\n\nValues entered in order of index are\n" ); for( i = 0 ; i < 4 ; i ++) { printf ( "%Index %d: %d\n" , i , scores [ i ]); } return 0 ; }
Output
Enter 4 numbers
Values entered in order of index are
Index 0: 45
Index 1: 78
Index 2: 100
Index 3: 12
MULTI-DIMENSIONAL ARRAYS
All the arrays that have been discussed and used in examples thus far, are termed one-dimensional arrays.
It is possible to create multi-dimensional arrays. This is done by simply providing the sizes matching the
needed dimension of the array. The format is as shown below:
type name[size][size]; for a 2-dimensional array e.g. int scores[6][7];
type name[size][size][size]; for a 3-dimensional array e.g. int scores[7][8][4];
When accessing the elements in a multidimensional array the number for-loops nested is equal to the
dimension e.g. a 2-dimensional array requires 2 for-loops. An element in a multidimensional array is referenced
by using the name all it index values (which match the dimension of the array) e.g. score[0][3] for a 2-
dimensional array and scores[0][0][7] for a 3-dimensional array. The programs below illustrate how
multidimensional arrays are used.
Code
#include <stdio.h> int main () { int i , j , k ; // for index values // 2 - dimensional array declared and intitialized int array1 [ 4 ][ 5 ] = { 45 }; // 3 - dimensional array declared and initialized int array2 [ 3 ][ 5 ][ 4 ] = { 56 }; // display the content of array for( i = 0 ; i < 4 ; i ++) { for( j = 0 ; j < 5 ; j ++) { printf ( "%2d" , array1 [ i ][ j ]); } printf ( "\n" ); } printf ( "\n\n" ); // display the content of array for( i = 0 ; i < 3 ; i ++) { printf ( "\ni = %d\n" , i ); for( j = 0 ; j < 5 ; j ++) { for( k = 0 ; k < 4 ; k ++) { printf ( "%2d" , array2 [ i ][ j ][ k ]); } printf ( "\n" ); }
return 0 ; }
Output
Two-Dimensional Array
#include <stdio.h> void display ( int array [][ 3 ], int size ) { int i , j ; for( i = 0 ; i < size ; i ++) { for( j = 0 ; j < 3 ; j ++) printf ( "%d\t" , array [ i ][ j ]); printf ( "\n\n" ); } } int main () { int array [ 4 ][ 3 ] = { 4 , 7 , 11 }; display ( array , 4 ); return 0 ; }
Output
USING SIZEOF FUNCTION WITH ARRAYS
It is important to note that using the sizeof function with the name of an array as its argument returns
the total size (in bytes) of an array. If this value is divided by the size of the data type or the size of
one of its element, the dimension of the array will be obtained.
i.e. dimension = sizeof(array)/sizeof(array[0])
If the argument is made of just a word, then the brackets won’t be necessary, but if it is composed of
more than one word then the brackets are compulsory.
This shows that:
dimension = sizeof(array)/sizeof(array[0]) = sizeof(array)/sizeof(array[0])
POINTERS
Data is stored in the memory of a computer. Each storage location (normally in bytes) in a computer’s
memory has a unique address. Address values are numeric and they run from 0 to a maximum value
determined by the size of the memory (number of storage locations).
Since address of storage locations in memory are numeric, it is possible to store and treat them like any
other data. A variable occupies several memory locations (bytes) depending on the type of data it is to
hold. A pointer is therefore a variable which stores memory address (address of the first byte occupied) of
a variable. Since a variable contains a specific value while a pointer holds the address of a variable, it is
stated that the name of a variable references a value directly, while a pointer references the same value
indirectly.
DECLARING A POINTER
type *name; e.g. int *numbPtr;
The address of a variable is represented by &name and this address value can be assigned to a pointer.
The asterisk is dropped after declaration when the pointer is to be used.
For example
int var = 10; // variable initialized
int *varPtr; // declare pointer
varPtr = &var; // pointer assigned the address of var
Since a pointer indirectly references the value in a variable, the value in the variable can be accessed
through a pointer by placing an asterisk (indirection operator) next to the pointer i.e. *pointer. When
displaying the address value in a pointer %p is the format or conversion specifier used to get the address
in hexadecimal form, while %d will display the address in base 10.
& and * are complement operators i.e. each nullifies the effect of the other. For this reason
varPtr = &*varPtr = *&varPtr = &var
The program below illustrates all of the above points.
Code
#include <stdio.h> int main () { int var ; // variable declared var = 10 ; // variable assigned 10 int ***** varPtr ; // pointer to var declared varPtr = & var ; // pointer assigned the address of var printf ( "Value through direct access\nvar = %d\n" , var ); printf ( "\nValue through indirect access\nvarPtr = %d\n" , *** varPtr ); printf ( "\nAddress from pointer\nvarPtr = %p\n" , varPtr ); printf ( "\nAdddress from variable\n&var = %p(in base 10: %d)\n" , & var , & var ); printf ( "\n&varPtr = %p\n" , & varPtr ); printf ( "&varPtr = %p\n" , & varPtr ); printf ( "varPtr = %p\n" , varPtr ); printf ( "\n&var = %d\n" *, & var ); printf ( "var = %d\n" , var ); return 0 ; }
Output
Value through direct access
var = 10
printf ( "*voidPtr1 = %d\n" *, ( int ) voidPtr1 ); printf ( "voidPtr2 = %f\n" *, ( float *) voidPtr2 ); return 0 ; }
Output
aPtr = 2686684 (in Hex: 0028FEDC)
bPtr = 2686680 (in Hex: 0028FED8)
voidPtr1 = 0 (in Hex: 00000000)
voidPtr2 = 4201696 (in Hex: 00401CE0)
*aPtr = 9
*bPtr = 10.
*voidPtr1 = 9
*voidPtr2 = 10.
POINTERS AND ARRAYS
The following are important facts to note about the relationship that exist between pointers and arrays:
1. The name of an array without the square brackets and an index value is a pointer to the address
of the first element in the array.
2. Since the variables (elements) of an array are successively positioned in memory, the size of each
variable can be worked out from difference between their addresses.
3. The address of an element in an array can be referenced by using &name[index]
For purposes of illustration the program below is presented.
Code
#include <stdio.h> int main () { int i [ 10 ], n ; float f [ 10 ]; double d [ 10 ]; printf ( "Size of an integer: %dbytes\n" , sizeof( int )); printf ( "Size of a float: %dbytes\n" , sizeof( float )); printf ( "Size of a double: %dbytes\n\n" , sizeof( double )); /* Print the table heading / printf ( "\t\tInteger\t\tFloat\t\tDouble" ); printf ( "\n================================" ); printf ( "=========================" ); / Print the addresses of each array element. */ for( n = 0 ; n < 10 ; n ++) printf ( "\nElement %d:\t%ld\t\t%ld\t\t%ld" , n , & i [ n ], & f [ n ], & d [ n ]); printf ( "\n================================" ); printf ( "=========================\n\n" ); printf ( "i = %ld\t\t&i[0] = %ld\n" , i , & i [ 0 ]); printf ( "f = %ld\t\t&f[0] = %ld\n" , f , & f [ 0 ]); printf ( "d = %ld\t\t&d[0] = %ld\n" , d , & d [ 0 ]); return 0 ; }
Output
Size of an integer: 4bytes
Size of a float: 4bytes
Size of a double: 8bytes
Integer Float Double
Element 0: 2686660 2686620 2686536
Element 1: 2686664 2686624 2686544
Element 2: 2686668 2686628 2686552
Element 3: 2686672 2686632 2686560
Element 4: 2686676 2686636 2686568
Element 5: 2686680 2686640 2686576
Element 6: 2686684 2686644 2686584
Element 7: 2686688 2686648 2686592
Element 8: 2686692 2686652 2686600
Element 9: 2686696 2686656 2686608
i = 2686660 &i[0] = 2686660
f = 2686620 &f[0] = 2686620
d = 2686536 &d[0] = 2686536
Notice that the difference between successive addresses corresponds to size of the various data types. The
size of a data type as has been shown in the program, is obtainable with the function sizeof(). In
addition, it has been shown that the name of the array and the address of the first element are one and
the same.
POINTER ARITHMETIC
If a pointer is incremented or decremented by a value, it is done by the size of the data type.
Code
#include <stdio.h> int main () { int i ; int ***** iPtr = & i ; printf ( "iPtr: %ld\n" , iPtr ); iPtr ++; // increase by 4 bytes printf ( "iPtr++: %ld\n" , iPtr ); iPtr += 2 ; // increase by 8 bytes (2 x 4) printf ( "iPtr += 2: %ld\n" , iPtr ); iPtr - = 3 ; // decrease by 12 bytes printf ( "iPtr - = 3: %ld\n" , iPtr ); return 0 ; }
Output
iPtr: 2686696
iPtr++: 2686700
iPtr += 2: 2686708
iPtr -= 3: 2686696