1-D Arrays Click to add text 1 Array Many applications require multiple data items that have common characteristics In mathematics, we often express such groups of data items in indexed form: x1, x2, x3, …, xn Array is a data structure which can represent a collection of data items which have the same data type (float/int/char/…) 2 Example: Printing Numbers in Reverse 4 numbers 3 numbers int a, b, c; scanf(“%d”, &a); scanf(“%d”, &b); scanf(“%d”, &c); printf(“%d ”, c); printf(“%d ”, b); printf(“%d \n”, a); int a, b, c, d; scanf(“%d”, &a); scanf(“%d”, &b); scanf(“%d”, &c); scanf(“%d”, &d); printf(“%d ”, d); printf(“%d ”, c); printf(“%d ”, b); printf(“%d \n”, a); 3 The Problem Suppose we have 10 numbers to handle Or 20 Or 100 Where do we store the numbers ? Use 100 variables ?? How to tackle this problem? Solution: Use arrays 4 Printing in Reverse Using Arrays int main() { int n, A[100], i; printf(“How many numbers to read? “); scanf(“%d”, &n); for (i = 0; i < n; ++i) scanf(“%d”, &A[i]); for (i = n -1; i >= 0; --i) printf(“%d ”, A[i]); printf(“\n”); return 0; } Using Arrays All the data items constituting the group share the same name int x[10]; Individual elements are accessed by specifying the index x[0] x[1] x[2] x[9] X is a 10-element one dimensional array 6 First example “data refers to a block of 10 int main() integer variables, data[0], data[1], { …, data[9] int i; int data[10]; for (i=0; i<10; i++) data[i]= i; i=0; while (i<10) { printf("Data[%d] = %d\n", i, data[i]); i++; } return 0; } 7 The result Array size should be a constant int main() { int i; int data[10]; for (i=0; i<10; i++) data[i]= i; i=0; while (i<10) { printf("Data[%d] = %d\n", i, data[i]); i++; } return 0; } Output Data[0] = 0 Data[1] = 1 Data[2] = 2 Data[3] = 3 Data[4] = 4 Data[5] = 5 Data[6] = 6 Data[7] = 7 Data[8] = 8 Data[9] = 9 8 Declaring Arrays Like variables, the arrays used in a program must be declared before they are used General syntax: type array-name [size]; type specifies the type of element that will be contained in the array (int, float, char, etc.) size is an integer constant which indicates the maximum number of elements that can be stored inside the array int marks[5]; marks is an array that can store a maximum of 5 integers 9 Examples: int x[10]; char line[80]; float points[150]; char name[35]; If we are not sure of the exact size of the array, we can define an array of a large size int marks[50]; though in a particular run we may only be using, say, 10 elements 10 Accessing Array Elements A particular element of the array can be accessed by specifying two things: Name of the array Index (relative position) of the element in the array Important to remember: In C, the index of an array starts from 0, not 1 Example: An array is defined as int x[10]; The first element of the array x can be accessed as x[0], fourth element as x[3], tenth element as x[9], etc. 11 Contd. The array index can be any expression that evaluates to an integer between 0 and n-1 where n is the maximum number of elements possible in the array a[x+2] = 25; b[3*x-y] = a[10-x] + 5; Remember that each array element is a variable in itself, and can be used anywhere a variable can be used (in expressions, assignments, conditions,…) 12 How is an array stored in memory? Starting from a given memory location, the successive array elements are allocated space in consecutive memory locations Array A x: starting address of the array in memory k: number of bytes allocated per array element A[i] is allocated memory location at address x + i*k 13 A Special Operator: AddressOf (&) Remember that each variable is stored at a memory location with an unique address Putting & before a variable name gives the starting address of the variable (where it is stored, not the value) Can be put before any variable (with no blank in between) int a =10; printf(“Value of a is %d, and address of a is %d\n”, a, &a); 14 Example Output &Data[0] = 3221224480 &Data[1] = 3221224484 int main() { int i; int data[10]; for(i=0; i<10; i++) printf("&Data[%d] = %u\n", i, &data[i]); return 0; } &Data[2] = 3221224488 &Data[3] = 3221224492 &Data[4] = 3221224496 &Data[5] = 3221224500 &Data[6] = 3221224504 &Data[7] = 3221224508 &Data[8] = 3221224512 &Data[9] = 3221224516 15 Initialization of Arrays General form: type array_name[size] = { list of values }; Examples: int marks[5] = {72, 83, 65, 80, 76}; char name[4] = {‘A’, ‘m’, ‘i’, ‘t’}; The size may be omitted. In such cases the compiler automatically allocates enough space for all initialized elements int flag[ ] = {1, 1, 1, 0}; char name[ ] = {‘A’, ‘m’, ‘i’, ‘t’}; 16 How to read the elements of an array? By reading them one element at a time for (j=0; j<25; j++) scanf (“%f”, &a[j]); The ampersand (&) is necessary The elements can be entered all in one line or in different lines 17 A Warning In C, while accessing array elements, array bounds are not checked Example: int marks[5]; : : marks[8] = 75; The above assignment would not necessarily cause an error Rather, it may result in unpredictable program results, which are very hard to debug 18 Reading into an array int main() { const int MAX_SIZE = 100; int i, size; float marks[MAX_SIZE]; float total; scanf("%d",&size); for (i=0, total=0; i<size; i++) { scanf("%f",&marks[i]); total = total + marks[i]; } printf("Total = %f \n Avg = %f\n", total, total/size); return 0; } Output 4 2.5 3.5 4.5 5 Total = 15.500000 Avg = 3.875000 19 How to print the elements of an array? By printing them one element at a time for (j=0; j<25; j++) printf (“\n %f”, a[j]); The elements are printed one per line printf (“\n”); for (j=0; j<25; j++) printf (“ %f”, a[j]); The elements are printed all in one line (starting with a new line) 20 How to copy the elements of one array to another? By copying individual elements for (j=0; j<25; j++) a[j] = b[j]; The element assignments will follow the rules of assignment expressions Destination array must have sufficient size 21 Example 1: Find the minimum of a set of 10 numbers int main() { int a[10], i, min; for (i=0; i<10; i++) scanf (“%d”, &a[i]); min = a[0]; for (i=1; i<10; i++) { if (a[i] < min) min = a[i]; } printf (“\n Minimum is %d”, min); return 0; } 22 Alternate Version 1 const int size = 10; int main() { int a[size], i, min; for (i=0; i<size; i++) scanf (“%d”, &a[i]); Change only one line to change the problem size min = a[0]; for (i=1; i<size; i++) { if (a[i] < min) min = a[i]; } printf (“\n Minimum is %d”, min); return 0; } 23 Alternate Version 2 #define size 10 int main() { int a[size], i, min; for (i=0; i<size; i++) scanf (“%d”, &a[i]); Change only one line to change the problem size min = a[0]; for (i=1; i<size; i++) { if (a[i] < min) min = a[i]; } printf (“\n Minimum is %d”, min); return 0; Used #define macro } 24 #define macro #define X Y Preprocessor directive The #include you have been using is also a preprocessor directive Compiler will first replace all occurrences of string X with string Y in the program, then compile the program Similar effect as read-only variables (const), but no storage allocated 25 Alternate Version 3 int main() { int a[100], i, min, n; scanf (“%d”, &n); /* Number of elements */ for (i=0; i<n; i++) scanf (“%d”, &a[i]); Define an array of large size and use only the required number of elements min = a[0]; for (i=1; i<n; i++) { if (a[i] < min) min = a[i]; } printf (“\n Minimum is %d”, min); return 0; } 26 const int nsub = 6; Example 2: Computing cgpa int main() { int grade_pt[nsub], cred[nsub], i, gp_sum=0, cred_sum=0; double gpa; for (i=0; i<nsub; i++) scanf (“%d %d”, &grade_pt[i], &cred[i]); Handling two arrays at the same time for (i=0; i<nsub; i++) { gp_sum += grade_pt[i] * cred[i]; cred_sum += cred[i]; } gpa = ((float) gp_sum) / cred_sum; printf (“\n Grade point average: is %.2lf”, gpa); return 0; } 27 Things you cannot do You cannot use = to assign one array variable to another a = b; /* a and b are arrays */ use == to directly compare array variables if (a = = b) ……….. directly scanf or printf arrays printf (“……”, a); 28 Character Arrays and Strings char C[8] = { 'a', 'b', 'h', 'i', 'j', 'i', 't', '\0' }; C[0] gets the value 'a', C[1] the value 'b', and so on. The last (7th) location receives the null character ‘\0’ Null-terminated (last character is ‘\0’) character arrays are also called null-terminated strings or just strings. Strings can be initialized in an alternative way. The last declaration is equivalent to: char C[8] = "abhijit"; The trailing null character is missing here. C automatically puts it at the end if you define it like this Note also that for individual characters, C uses single quotes, whereas for strings, it uses double quotes 29 Reading strings: %s format int main() { char name[25]; scanf("%s", name); printf("Name = %s \n", name); return 0; } %s reads a string into a character array given the array name or start address. It ends the string with the special “null” character ‘\0’. 30 Example: Finding length of a string #define SIZE 25 int main() { int i, length=0; char name[SIZE]; scanf("%s", name); printf("Name = %s \n", name); for (i=0; name[i]!='\0'; i++) length++; printf(“Length = %d\n", length); return 0; } Output Satyanarayana Name = Satyanarayana Length = 13 Note that character strings read in %s format end with ‘\0’ 31 Example: Counting the number of a’s #define SIZE 25 int main() { int i, count=0; char name[SIZE]; scanf("%s", name); printf("Name = %s \n", name); for (i=0; name[i]!='\0'; i++) if (name[i] == 'a') count++; printf("Total a's = %d\n", count); return 0; } Output Satyanarayana Name = Satyanarayana Total a's = 6 Note that character strings read in %s format end with ‘\0’ 32 Example: Palindrome Checking int main() { int i, flag, count=0; char name[25]; scanf("%s", name); /* Read Name */ for (i=0; name[i]!='\0'; i++); /* Find Length of String */ count=i; flag = 0; /* Loop below checks for palindrome by comparison*/ for(i=0; i<count; i++) if (name[i]!=name[count-i-1]) flag = 1; if (flag ==0) printf ("%s is a Palindrome\n", name); else printf("%s is NOT a Palindrome\n", name); return 0; } 33 Use of 1D Arrays in Sorting 34 Sorting Data Items A Sorting technique is used to rearrange a given array elements according to a comparison operator on the elements. Consider a set of data items Each item may have more than one field Example: a student record with name, roll no, CGPA,… Sort the set in ascending/descending order of some key value (some value of the data) Sort a set of integers (the key value is the value of the integer) Sort a set of student records according to roll no (the key value is roll no, though a student record has other values too) 35 Assumptions For the sorting techniques, we will take the input as an array of integers The sorting technique will reposition the elements in the array such that they are sorted in ascending order Same technique can be used to sort any other data type or sort in descending order 36 Different Sorting Techniques Selection sort Bubble sort Insertion sort Mergesort Quicksort Heapsort Bucket sort …. Will be discussed later in this course 37 Selection Sort 38 Selection sort Step 1 − Set MIN to location 0 Step 2 − Search the minimum element in the list Step 3 − Swap with value at location MIN Step 4 − Increment MIN to point to next element Step 5 − Repeat until list is sorted 39 Selection sort Input: 64 25 12 22 11 11 25 12 22 64 11 12 25 22 64 11 12 22 25 64 11 12 22 25 64 40 Selection sort int main() { int i, j, min_indx, data[100], n, t; scanf(“%d”, &n); for (i = 0; i < n; i++) scanf(“%d”, &data[i]); for (i = 0; i < n-1; i++) { min_indx = i; for (j = i+1; j < n; j++) { if (data[j] < data[min_indx]) min_indx = j;} t = data[i]; data[i] = data[min_indx]; data[min_indx] = t; } } 41 Bubble Sort 42 Bubble sort Bubble Sort is the simplest sorting algorithm that works by repeatedly swapping the adjacent elements if they are in wrong order. Example: Input: 5 1 4 2 8 First Pass: ( 5 1 4 2 8 ) –> ( 1 5 4 2 8 ) ( 1 5 4 2 8 ) –> ( 1 4 5 2 8 ) ( 1 4 5 2 8 ) –> ( 1 4 2 5 8 ) ( 1 4 2 5 8 ) –> ( 1 4 2 5 8 ) Second Pass: ( 1 4 2 5 8 ) –> ( 1 4 2 5 8 ) ( 1 4 2 5 8 ) –> ( 1 2 4 5 8 ) ( 1 2 4 5 8 ) –> ( 1 2 4 5 8 ) ( 1 2 4 5 8 ) –> ( 1 2 4 5 8 ) Third Pass: ( 1 2 4 5 8 ) –> ( 1 2 4 5 8 ) ( 1 2 4 5 8 ) –> ( 1 2 4 5 8 ) ( 1 2 4 5 8 ) –> ( 1 2 4 5 8 ) ( 1 2 4 5 8 ) –> ( 1 2 4 5 8 ) 43 Bubble sort int main() { int i, j, data[100], n; scanf(“%d”, &n); for (i = 0; i < n; i++) scanf(“%d”, &data[i]); for (i = 0; i < n-1; i++) { for (j = 0; j < n-i-1; j++) { if (data[j] > data[j+1]) { t = data[j]; data[j] = data[j+1]; data[j+1] = t;} } } } 44 Practice Problems 1. 2. 3. 4. Read in an integer n (n < 25). Read n integers in an array A. Then do the following (write separate programs for each, only the reading part is common). 1. find the sum of the absolute values of the integers. 2. Copy the positive and negative integers in the array into two additional arrays B and C respectively. Print A, B, and C. 3. Exchange the values of every pair of values from the start (so exchange A[0] and A[1], A[2] and A[3] and so on). If the number of elements is odd, the last value should stay the same. Read in two integers n and m (n, m < 50). Read n integers in an array A. Read m integers in an array B. Then do the following (write separate programs for each, only the reading part is common). 1. Find if there are any two elements x, y in A and an element z in B, such that x +y=z 2. Copy in another array C all elements that are in both A and B (intersection) 3. Copy in another array C all elements that are in either A and B (union) 4. Copy in another array C all elements that are in A but not in B (difference) Read in two null-terminated strings A and B (using %s. Assume max characters < 25 in each). Create another string C that is the concatenation of A and B (A followed by B). Print A, B, C using %s Read in two null-terminated strings A and B. Check if A is lexicographically smaller, 45 larger, or equal to B and print appropriate messages in each case. Functions 1 Function A program segment that carries out some specific, well-defined task Example A function to add two numbers A function to find the largest of n numbers A function will carry out its intended task whenever it is called or invoked Can be called multiple times 2 A function is a block of statements that performs a specific task. Say you are writing a C program and you need to perform a same task in that program more than once. In such case you have two options: a) Use the same set of statements every time you want to perform the task b) Write a function to perform that task, and just call it every time you need to perform that task. 3 Every C program consists of one or more functions One of these functions must be called main Execution of the program always begins by carrying out the instructions in main Functions call other functions as instructions 4 Function Control Flow void print_banner () { printf(“************\n”); } int main () { ... print_banner (); ... print_banner (); } int main () { print_banner (); print_banner { } print_banner { print_banner (); } } 5 Calling function (caller) may pass information to the called function (callee) as parameters/arguments For example, the numbers to add The callee may return a single value to the caller Some functions may not return anything 6 Calling function (Caller) Called function (Callee) int main() { float cent, fahr; scanf(“%f”,&cent); fahr = cent2fahr(cent); printf(“%fC = %fF\n”, cent, fahr); return 0; } Parameter float cent2fahr(float data) { float result; result = data*9/5 + 32; return result; } Parameter passed Returning value Calling/Invoking the cent2fahr function 7 How it runs float cent2fahr(float data) { float result; printf(“data = %f\n”, data); result = data*9/5 + 32; printf(“result = %f\n”, result); return result; } int main() { float cent, fahr; scanf(“%f”,&cent); printf(“Input is %f\n”, cent); fahr = cent2fahr(cent); printf(“%fC = %fF\n”, cent, fahr); return 0; } Outputs 32 Input is 32.000000 data = 32.000000 result = 89.599998 32.000000C = 89.599998F -45.6 Input is -45.599998 data = -45.599998 result = -50.079998 -45.599998C = -50.079998F 8 What will be the output? float cent2fahr(float data) { float result; printf(“data = %f\n”, data); result = data*9/5 + 32; return result; printf(“result = %f\n”, result); } int main() { float cent, fahr; scanf(“%f”,&cent); printf(“Input is %f\n”, cent); fahr = cent2fahr(cent); printf(“%fC = %fF\n”, cent, fahr); return 0; } Outputs 32 Input is 32.000000 data = 32.000000 32.000000C = 89.599998F -45.6 Input is -45.599998 data = -45.599998 -45.599998C = -50.079998F 9 Another Example int factorial (int m) { int i, temp=1; for (i=1; i<=m; i++) temp = temp * i; return (temp); } int main() { int n; for (n=1; n<=5; n++) printf (“%d! = %d \n”, n, factorial (n) ); return 0; } Output 1! = 1 2! = 2 3! = 6 4! = 24 5! = 120 10 Why Functions? Allows one to develop a program in a modular fashion Divide-and-conquer approach Construct a program from small pieces or components Use existing functions as building blocks for new programs - reusability of the code Abstraction: hide internal details (library functions) To improve the readability of code. Debugging of the code would be easier Reduces the size of the code, duplicate set of statements are replaced by function calls. 11 Defining a Function A function definition has two parts: The first line, called header The body of the function return-value-type function-name ( parameter-list ) { declarations and statements } 12 The first line contains the return-value-type, the function name, and optionally a set of commaseparated arguments enclosed in parentheses Each argument has an associated type declaration The arguments are called formal arguments or formal parameters The body of the function is actually a block of statement that defines the action to be taken by the function 13 Return-value type Formal parameters int gcd (int A, int B) { int temp; while ((B % A) != 0) { temp = B % A; B = A; A = temp; } Value returned return (A); } BODY 14 Return value A function can return a value Using return statement Like all values in C, a function return value has a type The return value can be assigned to a variable in the caller int x, y, z; scanf(“%d%d”, &x, &y); z = gcd(x,y); printf(“GCD of %d and %d is %d\n”, x, y, z); 15 Function Not Returning Any Value Example: A function which prints if a number is divisible by 7 or not void div7 (int n) { Return type is void if ((n % 7) == 0) printf (“%d is divisible by 7”, n); else printf (“%d is not divisible by 7”, n); return; Optional } 16 return statement In a value-returning function (result type is not void), return does two distinct things specify the value returned by the execution of the function terminate that execution of the callee and transfer control back to the caller A function can only return one value The value can be any expression matching the return type but it might contain more than one return statement. In a void function return is optional at the end of the function body. return may also be used to terminate execution of the function explicitly. 17 No return value should appear following return. void compute_and_print_itax () { float income; scanf (“%f”, &income); Terminate function if (income < 50000) { execution before printf (“Income tax = Nil\n”); reaching the end return; } if (income < 60000) { printf (“Income tax = %f\n”, 0.1*(income-50000)); return; } if (income < 150000) { printf (“Income tax = %f\n”, 0.2*(income-60000)+1000); return ; } printf (“Income tax = %f\n”, 0.3*(income-150000)+19000); } 18 Calling a function Called by specifying the function name and parameters in an instruction in the calling function When a function is called from some other function, the corresponding arguments in the function call are called actual arguments or actual parameters The function call must include a matching actual parameter for each formal parameter Position of an actual parameters in the parameter list in the call must match the position of the corresponding formal parameter in the function definition The formal and actual arguments must match in their data types 19 Functions (Contd…) 20 Example Formal parameters double operate (double x, double y, char op) { int main () switch (op) { { case ‘+’ : return x+y+0.5 ; double x, y, z; case ‘~’ : if (x>y) char op; return x-y + 0.5; ... return y-x+0.5; z = operate (x, y, op); case ‘x’ : return x*y + 0.5; ... default : return –1; } } } Actual parameters 21 When the function is executed, the value of the actual parameter is copied to the formal parameter parameter passing int main () { ... double circum; ... area1 = area(circum/2.0); ... } double area (double r) { return (3.14*r*r); } 22 Another Example /* Compute the GCD of four numbers */ int main() { int n1, n2, n3, n4, result; scanf (“%d %d %d %d”, &n1, &n2, &n3, &n4); result = gcd ( gcd (n1, n2), gcd (n3, n4) ); printf (“The GCD of %d, %d, %d and %d is %d \n”, n1, n2, n3, n4, result); return 0; } func-gcd.c 23 Another Example int main() { int numb, flag, j=3; scanf(“%d”,&numb); while (j <=numb) { flag = prime(j); if (flag==0) printf(“%d is prime\n”,j); j++; } return 0; } int prime(int x) { int i, test; i=2, test =0; while ((i <= sqrt(x)) && (test ==0)) { if (x%i==0) test = 1; i++; } return test; } 24 Tracking the flow of control int main() { int numb, flag, j=3; scanf(“%d”,&numb); printf(“numb = %d \n”,numb); while (j <= numb) { printf(“Main, j = %d\n”,j); flag = prime(j); printf(“Main, flag = %d\n”,flag); if (flag == 0) printf(“%d is prime\n”,j); j++; } return 0; } int prime(int x) { int i, test; i = 2; test = 0; printf(“In function, x = %d \n”,x); while ((i <= sqrt(x)) && (test == 0)) { if (x%i == 0) test = 1; i++; } printf(“Returning, test = %d \n”,test); return test; } 25 The output 5 numb = 5 Main, j = 3 In function, x = 3 Returning, test = 0 Main, flag = 0 3 is prime Main, j = 4 In function, x = 4 Returning, test = 1 Main, flag = 1 Main, j = 5 In function, x = 5 Returning, test = 0 Main, flag = 0 5 is prime 26 Points to note The identifiers used as formal parameters are “local”. Not recognized outside the function Names of formal and actual arguments may differ A value-returning function is called by including it in an expression A function with return type T (≠ void) can be used anywhere an expression of type T can be used 27 Returning control back to the caller If nothing returned return; or, until reaches the last right brace ending the function body If something returned return expression; 28 Function Prototypes Usually, a function is defined before it is called main() is the last function in the program written Easy for the compiler to identify function definitions in a single scan through the file However, many programmers prefer a top-down approach, where the functions are written after main() Must be some way to tell the compiler Function prototypes are used for this purpose Only needed if function definition comes after use 29 Function prototypes are usually written at the beginning of a program, ahead of any functions (including main()) Prototypes can specify parameter names or just types (more common) Examples: int gcd (int , int ); void div7 (int number); Note the semicolon at the end of the line. The parameter name, if specifed, can be anything; but it is a good practice to use the same names as in the function definition 30 Example: #include <stdio.h> int sum(int, int); int main() { int x, y; scanf(“%d%d”, &x, &y); printf(“Sum = %d\n”, sum(x, y)); } int sum (int a, int b) { return(a + b); } 31 Example: #include <stdio.h> int sum (int a, int b) { return(a + b); } int main() { int x, y; scanf(“%d%d”, &x, &y); printf(“Sum = %d\n”, sum(x, y)); } 32 Some more points A function cannot be defined within another function All function definitions must be disjoint Nested function calls are allowed A calls B, B calls C, C calls D, etc. The function called last will be the first to return A function can also call itself, either directly or in a cycle A calls B, B calls C, C calls back A. Called recursive call or recursion 33 Example: main calls ncr, ncr calls fact int ncr (int n, int r); int fact (int n); int main() { int i, m, n, sum=0; scanf (“%d %d”, &m, &n); for (i=1; i<=m; i+=2) sum = sum + ncr (n, i); printf (“Result: %d \n”, sum); return 0; } int ncr (int n, int r) { return (fact(n) / fact(r) / fact(n-r)); } int fact (int n) { int i, temp=1; for (i=1; i<=n; i++) temp *= i; return (temp); } 34 Local variables A function can define its own local variables The locals have meaning only within the function Each execution of the function uses a new set of locals Local variables cease to exist when the function returns Parameters are also local 35 Local variables /* Find the area of a circle with diameter d */ double circle_area (double d) { double radius, area; radius = d/2.0; area = 3.14*radius*radius; return (area); } parameter local variables 36 Revisiting nCr int fact(int x) { int i,fact=1; for(i=2; i<=x; ++i) fact=fact*i; return fact; } int ncr(int x,int y) { int p,q,r; p=fact(x); q=fact (y); r = fact(x-y); return p/(q*r); } int main() { int n, r; scanf(“%d%d”,&n,&r); printf(“n=%d, r=%d, nCr=%d\n”,n, r, ncr(n,r)); return 0; } The variable x in function fact and x in function ncr are different. The values computed from the arguments at the point of call are copied on to the corresponding parameters of the called function before it starts execution. 37 Scope of a variable Part of the program from which the value of the variable can be used (seen) Scope of a variable - Within the block in which the variable is defined Block = group of statements enclosed within { } Local variable – scope is usually the function in which it is defined So two local variables of two functions can have the same name, but they are different variables Global variables – declared outside all functions (even main) scope is entire program by default, but can be hidden in a block if local variable of same name defined 38 #include <stdio.h> int A = 1; int main() Global variable { myProc(); printf ( "A = %d\n", A); } void myProc() { int A = 2; if ( A==2 ) { int A = 3; printf ( "A = %d\n", A); } printf ( "A = %d\n", A); } Variable Scope Hides the global A Output: A= 3 A= 2 A= 1 39 Functions (Contd…) 40 Parameter Passing: by Value and by Reference Used when invoking functions Call by value Passes the value of the argument to the function Execution of the function does not change the actual parameters All changes to a parameter done inside the function are done on a copy of the actual parameter The copy is removed when the function returns to the caller The value of the actual parameter in the caller is not affected Avoids accidental changes 41 Call by reference Passes the address to the original argument. Execution of the function may affect the original Not directly supported in C except for arrays 42 Parameter passing & return: 1 int main() { int a=10, b; printf (“Initially a = %d\n”, a); b = change (a); printf (“a = %d, b = %d\n”, a, b); return 0; } int change (int x) { printf (“Before x = %d\n”,x); x = x / 2; printf (“After x = %d\n”, x); return (x); } Output Initially a = 10 Before x = 10 After x = 5 a = 10, b = 5 43 Parameter passing & return: 2 int main() { int x=10, b; printf (“M: Initially x = %d\n”, x); b = change (x); printf (“M: x = %d, b = %d\n”, x, b); return 0; } int change (int x) { printf (“F: Before x = %d\n”,x); x = x / 2; printf (“F: After x = %d\n”, x); return (x); } Output M: Initially x = 10 F: Before x = 10 F: After x = 5 M: x = 10, b = 5 44 Parameter passing & return: 3 int main() { int x=10, b; printf (“M: Initially x = %d\n”, x); x = change (x); b = x; printf (“M: x = %d, b = %d\n”, x, x); return 0; } int change (int x) { printf (“F: Before x = %d\n”,x); x = x / 2; printf (“F: After x = %d\n”, x); return (x); } Output M: Initially x = 10 F: Before x = 10 F: After x = 5 M: x = 5, b = 5 45 Parameter passing & return: 4 int main() { int x=10, y=5; printf (“M1: x = %d, y = %d\n”, x, y); interchange (x, y); printf (“M2: x = %d, y = %d\n”, x, y); return 0; } void interchange (int x, int y) { int temp; printf (“F1: x = %d, y = %d\n”, x, y); temp= x; x = y; y = temp; printf (“F2: x = %d, y = %d\n”, x, y); } Output M1: x = 10, y = 5 F1: x = 10, y = 5 F2: x = 5, y = 10 M2: x = 10, y = 5 How do we write an interchange function? (will see later) 46 Passing Arrays to Function Array element can be passed to functions as ordinary arguments IsFactor (x[i], x[0]) sin (x[5]) 47 Passing Entire Array to a Function An array name can be used as an argument to a function Permits the entire array to be passed to the function The way it is passed differs from that for ordinary variables Rules: The array name must appear by itself as argument, without brackets or subscripts The corresponding formal argument is written in the same manner Declared by writing the array name with a pair of empty brackets 48 Whole Array as Parameters const int ASIZE = 5; float average (int B[ ]) Only Array Name/address passed. { [ ] mentioned to indicate that int i, total=0; is an array. for (i=0; i<ASIZE; i++) total = total + B[i]; return ((float) total / (float) ASIZE); } int main ( ) { int x[ASIZE] ; float x_avg; x = {10, 20, 30, 40, 50}; x_avg = average (x) ; return 0; } Called only with actual array name 49 Contd. We don’t need to write the array size. It works with arrays of any size. int main() { int n; float list[100], avg; : avg = average (n, list); : } float average (int a, float x[]) { : sum = sum + x[i]; } 50 Arrays used as Output Parameters void VectorSum (int a[ ], int b[ ], int vsum[ ], int length) { int i; for (i=0; i<length; i=i+1) vsum[i] = a[i] + b[i] ; } void PrintVector (int a[ ], int length) { int i; for (i=0; i<length; i++) printf (“%d “, a[i]); } int main () { int x[3] = {1,2,3}, y[3] = {4,5,6}, z[3]; VectorSum (x, y, z, 3) ; PrintVector (z, 3) ; return 0; } 51 The Actual Mechanism When an array is passed to a function, the values of the array elements are not passed to the function The array name is interpreted as the address of the first array element The formal argument therefore becomes a pointer to the first array element When an array element is accessed inside the function, the address is calculated using the formula stated before Changes made inside the function are thus also reflected in the calling program 52 Contd. Passing parameters in this way is called call-by-reference Normally parameters are passed in C using call-by-value Basically what it means? If a function changes the values of array elements, then these changes will be made to the original array that is passed to the function This does not apply when an individual element is passed on as argument 53 Library Functions 54 Library Functions Set of functions already written for you, and bundled in a “library” Example: printf, scanf, getchar, C library provides a large number of functions for many things Already seen math library functions earlier Will look at string library functions 55 String Library Functions String library functions perform common operations on null terminated strings Must include a special header file #include <string.h> Example printf ("%f", strlen(C)); C is a null-terminated string Calls function strlen, which returns the number of characters in C (not counting the ‘\0’ character) 56 Common string library functions strlen – returns the length of a string strcmp – compares two strings (lexicographic) Returns 0 if the two strings are equal, < 0 if first string is less than the second string, > 0 if the first string is greater than the second string Commonly used for sorting strings strcat – concatenates two strings strcpy – copy one string to another we will need some basic knowledge of pointers to understand how to use strcat and strcpy Many others, but these are the ones you will know in this course 57 #include <stdio.h> #include <string.h> Example int main() { char A[20], B[20]; int n, m, val; scanf(“%s%s”, A, B); n = strlen(A); m = strlen(B); printf(“The lengths of the strings are %d and %d\n”, n, m); val = strcmp(A, B); if (val == 0) printf(“The strings are the same\n”); else if (val < 0) printf(“%s is smaller than %s\n”, A, B); else printf(“%s is greater than %s\n”, A, B); } 58 Outputs program program The lengths of the strings are 7 and 7 The strings are the same arobinda abhijit The lengths of the strings are 8 and 7 arobinda is larger than abhijit iit-kgp iit-mandi The lengths of the strings are 7 and 9 iit-kgp is smaller than iit-mandi arobinda Arobinda The lengths of the strings are 8 and 8 arobinda is larger than Arobinda 59 Practice Problems No separate problems needed. Look at everything that you did so far, such as finding sum, finding average, counting something, checking if something is true or false (“ Is there an element in array A such that….) etc. in which the final answer is one thing only (like sum, count, 0 or 1,…). Then for each of them, rather than doing it inside main (as you have done so far), write it as a function with appropriate parameters, and call from main() to find and print. Normally, read and print everything from main(). Do not read or print anything inside the function. This will give you better practice. However, you can write simple functions for printing an array. 60 Recursion Click to add text Part I (Lecture 15) 1 Recursion ■ A function calls itself: ◻ Either directly. ■ f1 calls f1. f1(){f1();} ◻ Or cyclically. ■ f1 calls f2, and f2 calls f1 f1(){f2();} f2(){f1();} ■ f1 f1 f2 Used for repetitive computations in which each action is stated in terms of a previous result (recurrence). fact(n) = n * fact (n-1) 2 Why Use Recursion? ■ Pro: ◻ ◻ ◻ ■ Allows us to write elegant and simple code for certain complex problems Small, compact code, less effort Easy to modify, debug??? Con: ◻ ◻ Higher overhead: Large number of recursive calls Confusing for beginners Recursion: Basics ■ For a problem to be written in recursive form, two conditions are to be satisfied: ◻ It should be possible to express the problem in recursive form ■ ◻ Solution of the problem in terms of solution of the same problem on smaller sized data The problem statement must include a stopping condition fact(n) = 1, if n = 0 Stopping condition = n * fact(n-1), if n > 0 Recursive definition 4 ■ Examples: ◻ Factorial: fact(0) = 1 fact(n) = n * fact(n-1), if n > 0 ◻ GCD: gcd (m, m) = m gcd (m, n) = gcd (m%n, n), if m > n gcd (m, n) = gcd (n, n%m), if m < n ◻ Fibonacci series (1,1,2,3,5,8,13,21,….) fib (0) = 1 fib (1) = 1 fib (n) = fib (n-1) + fib (n-2), if n > 1 5 Factorial fact(1) = 1 fact(n) = n * fact(n-1), if n > 1 long int fact (int n){ if (n == 1) Stopping condition return (1); else return (n * fact(n-1)); } Recursive call 6 Factorial Execution long int fact (int n) { if (n = = 1) return (1); else return (n * fact(n-1)); } 7 Factorial Execution fact(4) long int fact (int n) { if (n = = 1) return (1); else return (n * fact(n-1)); } 8 Factorial Execution fact(4) if (4 = = 1) return (1); else return (4 * fact(3)); long int fact (int n) { if (n = = 1) return (1); else return (n * fact(n-1)); } 9 Factorial Execution fact(4) if (4 = = 1) return (1); else return (4 * fact(3)); if (3 = = 1) return (1); else return (3 * fact(2)); long int fact (int n) { if (n = = 1) return (1); else return (n * fact(n-1)); } 10 Factorial Execution fact(4) if (4 = = 1) return (1); else return (4 * fact(3)); if (3 = = 1) return (1); else return (3 * fact(2)); if (2 = = 1) return (1); else return (2 * fact(1)); long int fact (int n) { if (n = = 1) return (1); else return (n * fact(n-1)); } 11 Factorial Execution fact(4) if (4 = = 1) return (1); else return (4 * fact(3)); if (3 = = 1) return (1); else return (3 * fact(2)); if (2 = = 1) return (1); else return (2 * fact(1)); long int fact (int n) { if (n = = 1) return (1); else return (n * fact(n-1)); } if (1 = = 1) return (1); 12 Factorial Execution fact(4) if (4 = = 1) return (1); else return (4 * fact(3)); if (3 = = 1) return (1); else return (3 * fact(2)); if (2 = = 1) return (1); else return (2 * fact(1)); long int fact (int n) { if (n = = 1) return (1); else return (n * fact(n-1)); } 1 if (1 = = 1) return (1); 13 Factorial Execution fact(4) if (4 = = 1) return (1); else return (4 * fact(3)); if (3 = = 1) return (1); else return (3 * fact(2)); 2 if (2 = = 1) return (1); else return (2 * fact(1)); long int fact (int n) { if (n = = 1) return (1); else return (n * fact(n-1)); } 1 if (1 = = 1) return (1); 14 Factorial Execution fact(4) if (4 = = 1) return (1); else return (4 * fact(3)); 6 if (3 = = 1) return (1); else return (3 * fact(2)); 2 if (2 = = 1) return (1); else return (2 * fact(1)); long int fact (int n) { if (n = = 1) return (1); else return (n * fact(n-1)); } 1 if (1 = = 1) return (1); 15 Factorial Execution fact(4) 24 if (4 = = 1) return (1); else return (4 * fact(3)); 6 if (3 = = 1) return (1); else return (3 * fact(2)); 2 if (2 = = 1) return (1); else return (2 * fact(1)); long int fact (int n) { if (n = = 1) return (1); else return (n * fact(n-1)); } 1 if (1 = = 1) return (1); 16 Example: Finding max in an array int findMax(int A[ ], int n) { int temp; if (n==1) { return A[0]; } temp = findMax(A, n-1); if (A[n-1] > temp) return A[n-1]; else return temp; } Terminating condition. Small size problem that you know how to solve directly without calling any functions Recursive call. Find the max in the first n-1 elements (exact same problem, just solved on a smaller array). Important things to remember ■ ■ Think how the whole problem (finding max of n elements in A) can be solved by solving the exact same problem on a smaller problem (finding max of first n-1 elements of the array). But then, do NOT think how the smaller problem will be solved, just call the function recursively and assume it will be solved. When you write a recursive function ■ ■ ■ First write the terminating/base condition Then write the rest of the function Always double-check that you have both Back to Factorial: Look at the variable addresses (a slightly different program) ! int main() { int x,y; scanf("%d",&x); y = fact(x); printf ("M: x= %d, y = %d\n", x,y); return 0; } int fact(int data) { int val = 1; printf("F: data = %d, &data = %u \n &val = %u\n", data, &data, &val); if (data>1) val = data*fact(data-1); return val; } Output 4 F: data = 4, &data = 3221224528 &val = 3221224516 F: data = 3, &data = 3221224480 &val = 3221224468 F: data = 2, &data = 3221224432 &val = 3221224420 F: data = 1, &data = 3221224384 &val = 3221224372 M: x= 4, y = 24 19 ■ The memory addresses for the variable data are different in different calls! ■ They are not the same variable. ■ Each function call will have its own set of variables, even if the name of the variable is the same as it is the same function being called ■ Changes made to one will not be seen by the calling function on return 20 int main() { int x,y; scanf("%d",&x); y = fact(x); printf ("M: x= %d, y = %d\n", x,y); return 0; } int fact(int data) { int val = 1, count = 0; if (data>1) val = data*fact(data-1); count++; printf(“count = %d, data = %d\n”, count, data); return val; } Output 4 count = 1, data = 1 count = 1, data = 2 count = 1, data = 3 count = 1, data = 4 M: x= 4, y = 24 • Count did not change even though ++ done! • Each call does it on its own copy, lost on return 21 Recursion Part II (Lecture 16) 22 Fibonacci Numbers 0,1, 1, 2, 3, 5, 8, 13, 21, … ◻ ◻ F0 = 0 , F1 = 1 (Base Cases) Rest are sum of preceding two Fn = Fn-1 + Fn-2 (n > 1) Leonardo Pisano Fibonacci (11701250) Fibonacci’s Problem: Multiplying Mice Month 1 Month 2 Quite difficult to keep track of the population explosion. Month 3 Month 4 Month 5 Month 6 Month 7 Babies Recursive relation: #babies in Month 7= #mice in Month 5 #adults in Month 7 = #mice in Month 6 24 The Fibonacci numbers ■f(0) = 0, f(1) = 1 ■f(n) = f(n – 1) + f(n - 2) ■f(0) =0 ■f(1) = 1 ■f(2) = f(1) + f(0) = 1 + 0 = 1 ■f(3) = f(2) + f(1) = 1 + 1 = 2 ■f(4) = f(3) + f(2) = 2 + 1 = 3 ■f(5) = f(4) + f(3) = 3 + 2 = 5 ■f(6) = f(5) + f(4) = 5 + 3 = 8 25 Fibonacci Numbers Fibonacci recurrence: fib(n) = 1 if n = 0 or 1; = fib(n – 2) + fib(n – 1) otherwise; int fib (int n) { if (n == 0 or n == 1) return 1; [BASE] return fib(n-2) + fib(n-1) ; [Recursive] } 26 int fib (int n) { if (n == 0 || n == 1) return 1; return fib(n-2) + fib(n-1) ; } Fibonacci recurrence: fib(n) = 1 if n = 0 or 1; = fib(n – 2) + fib(n – 1) otherwise; fib (5) fib (3) fib (1) fib (0) fib (4) fib (2) fib (1) fib (2) fib (0) fib (1) fib (3) fib (1) fib (2) fib (0) 27 fib (1) int fib (int n) { if (n == 0 || n == 1) return 1; return fib(n-2) + fib(n-1) ; } Fibonacci recurrence: fib(n) = 1 if n = 0 or 1; = fib(n – 2) + fib(n – 1) otherwise; fib (5) fib (3) fib (1) 1 fib (0) 1 fib (4) fib (2) fib (1) 1 fib (2) fib (3) fib (0) fib (1) fib (1) 1 1 1 fib (2) fib (0) 1 28 fib (1) 1 fib.c int fib (int n) { if (n==0 || n==1) return 1; return fib(n-2) + fib(n-1) ; } 8 fib (5) Fibonacci recurrence: fib(n) = 1 if n = 0 or 1; = fib(n – 2) + fib(n – 1) otherwise; 3 5 fib (4) fib (3) 2 1 fib (1) fib (2) 1 1 1 fib (0) fib (1) 1 1 2 fib (2) 3 fib (3) 2 1 1 1 fib (0) fib (1) fib (1) 1 1 1 29 fib (2) 1 1 fib (0) fib (1) 1 1 Exercise ■ Write recursive function to compute the sum of squares of the numbers between two given numbers m and n. int sumSquares (int m, int n); ■ First form the recurrence relation: S(m,n)= m*n if m=n = S(m,mid)+S(mid+1,n) 30 Sum of Squares int sumSquares (int m, int n){ int middle ; if (m == n) return m*m; else { middle = (m+n)/2; return sumSquares(m,middle) + sumSquares(middle+1,n); } } 31 Annotated Call Tree 355 sumSquares(5,10) 245 sumSquares(8,10) 110 sumSquares(5,7) 49 61 sumSquares(5,6) sumSquares(7,7) sumSquares(8,9) sumSquares(10,10) 36 25 sumSquares(5,5) sumSquares(6,6) 25 100 145 36 49 81 64 sumSquares(8,8) sumSquares(9,9) 64 100 81 32 Example: Printing the digits of an Integer in Reverse ■ Print the last digit, then print the remaining number in reverse ◻ Ex: If integer is 743, then reversed is print 3 first, then print the reverse of 74 void printReversed(int i) { if (i < 10) { printf(“%d\n”, i); return; } else { printf(“%d”, i%10); printReversed(i/10); } } 33 Exercise: How to print the numer as it is with hyphen between digits ■ ■ 12345 1—2—3—4—5 Head recursion versus tail recursion void printReversed(int i) { if (i < 10) { printf(“%d---”, i); return; } else { printReversed(i/10); printf(“%d ---”, i%10); } } 34 ■ Counting Zeros in a Positive Integer Check last digit from right ◻ If it is 0, number of zeros = 1 + number of zeroes in remaining part of the number ◻ If it is non-0, number of zeros = number of zeroes in remaining part of the number int zeros(int number) { if(number<10) return 0; if (number%10 == 0) return(1+zeros(number/10)); else return(zeros(number/10)); } 35 Example: Binary Search ■ ■ Searching for an element k in a sorted array A with n elements 12 11 22 25 64 Idea: Choose the middle element A[n/2] ◻ If k == A[n/2], we are done ◻ If k < A[n/2], search for k between A[0] and A[n/2 -1] ◻ If k > A[n/2], search for k between A[n/2 + 1] and A[n-1] ◻ Repeat until either k is found, or no more elements to search ◻ ■ Requires less number of comparisons than linear search in the worst case (log2n instead of n) 36 int binsearch(int A[ ], int low, int high, int k) { 12 11 22 25 64 int mid; printf(“low = %d, high = %d\n”, low, high); if (low >= high) return 0; mid = (low + high)/2; printf(“mid = %d, A[%d] = %d\n\n”, mid, mid, A[mid]); if (A[mid] == k) return 1; else { if (A[mid] > k) return (binsearch(A, low, mid-1, k)); else return(binsearch(A, mid+1, high, k)); } } 37 38 int main() { int A[25], n, k, i, found; scanf(“%d”, &n); for (i=0; i<n; i++) scanf(“%d”, &A[i]); scanf(“%d”, &k); found = binsearch(A, 0, n-1, k); if (found == 1) printf(“%d is present in the array\n”, k); else printf(“%d is not present in the array\n”, k); } 39 Output 8 9 11 14 17 19 20 23 27 21 low = 0, high = 7 mid = 3, A[3] = 17 8 9 11 14 17 19 20 23 27 14 low = 0, high = 7 mid = 3, A[3] = 17 low = 4, high = 7 mid = 5, A[5] = 20 low = 0, high = 2 mid = 1, A[1] = 11 low = 6, high = 7 mid = 6, A[6] = 23 low = 2, high = 2 mid = 2, A[2] = 14 14 is present in the array low = 6, high = 5 21 is not present in the array 40 Static Variables int Fib(int n, int i) { int Fib (int, int); int main() { int n; scanf("%d", &n); if (n == 0 || n ==1) printf("F(%d) = %d \n", n, 1); else printf("F(%d) = %d \n", n, Fib(n,2)); return 0; } static int m1, m2; int res, temp; if (i==2) {m1 =1; m2=1;} if (n == i) res = m1+ m2; else { temp = m1; m1 = m1+m2; m2 = temp; res = Fib(n, i+1); } return res; } Static variables remain in existence rather than coming and going each time a function is activated 41 Static Variables: See the addresses! int Fib(int n, int i) { static int m1, m2; int res, temp; if (i==2) {m1 =1; m2=1;} printf("F: m1=%d, m2=%d, n=%d, i=%d\n", m1,m2,n,i); printf("F: &m1=%u, &m2=%u\n", &m1,&m2); printf("F: &res=%u, &temp=%u\n", &res,&temp); if (n == i) res = m1+ m2; else { temp = m1; m1 = m1+m2; m2 = temp; res = Fib(n, i+1); } return res; } Output 5 F: m1=1, m2=1, n=5, i=2 F: &m1=134518656, &m2=134518660 F: &res=3221224516, &temp=3221224512 F: m1=2, m2=1, n=5, i=3 F: &m1=134518656, &m2=134518660 F: &res=3221224468, &temp=3221224464 F: m1=3, m2=2, n=5, i=4 F: &m1=134518656, &m2=134518660 F: &res=3221224420, &temp=3221224416 F: m1=5, m2=3, n=5, i=5 F: &m1=134518656, &m2=134518660 F: &res=3221224372, &temp=3221224368 F(5) = 8 42 Common Errors in Writing Recursive Methods ■ Non-terminating Recursive Methods (Infinite recursion): a) No base case. int badFactorial(int x) { return x * badFactorial(x-1); } The base case is never reached for some parameter values. x) { int anotherBadFactorial(int b) } if(x == 0) return 1; else return x*(x-1)*anotherBadFactorial(x -2); // When x is odd, base case never reached!! Common Errors in Writing Recursive Functions int badSum1(int x) { if(x==1) return 1; return(badSum2(x--)); } Common Errors in Writing Recursive Functions ■ Mixing up loops and recursion int anotherBadFactorial(int x) { int i, fact = 0; if (x == 0) return 1; else { for (i=x; i>0; i=i-1) { fact = fact + x*anotherBadFactorial(x-1); } return fact; } } ■ In general, if you have recursive function calls within a loop, think carefully if you need it. Most recursive functions you will see in this course will not need this Recursion vs. Iteration ■ ■ Repetition ◻ Iteration: explicit loop ◻ Recursion: repeated function calls Termination ◻ Iteration: loop condition fails ◻ Recursion: base case recognized ■ Both can have infinite loops ■ Balance ◻ Choice between performance (iteration) and good software engineering (recursion). 46 ■ Every recursive program can also be written without recursion ■ Recursion is used for programming convenience, not for performance enhancement ■ Sometimes, if the function being computed has a nice recursive form, then a recursive code may be more readable 47 Recursion Part III (Lecture 17) 48 How are function calls implemented? ■ The following applies in general, with minor variations that are implementation dependent ◻ ◻ The system maintains a stack in memory ■ Stack is a last-in first-out structure ■ Two operations on stack, push and pop Whenever there is a function call, the activation record gets pushed into the stack ■ Activation record consists of the return address in the calling program, the return value from the function, and the local variables inside the function 49 STACK int main() { …….. x = gcd (a, b); …….. } Activation record Before call int gcd (int x, int y) { …….. …….. return (result); } Local Variables Return Value Return Addr After call After return 50 int main() { …….. x = ncr (a, b); …….. } int ncr (int n, int r) { return (fact(n)/ fact(r)/fact(n-r)); } 3 times int fact (int n) { ……… return (result); } 3 times LV2, RV2, RA2 Before call LV1, RV1, RA1 LV1, RV1, RA1 LV1, RV1, RA1 Call ncr Call fact fact returns ncr returns 51 What happens for recursive calls? ■ ■ What we have seen …. ◻ Space for activation record is allocated on the stack when a function call is made ◻ Space allocated for activation record is de-allocated on the stack when the function returns In recursion, a function calls itself ◻ Several function calls going on, with none of the function calls returning back ■ Space for activation records allocated on the stack increases continuously ■ Large stack space required 52 ◻ ■ Space for activation records are de-allocated, when the termination condition of recursion is reached We shall illustrate the process by an example of computing factorial ◻ Activation record looks like: Local Variables Return Value Return Addr 53 Example:: main() calls fact(3) int main() { int n; n = 3; printf (“%d \n”, fact(n) ); return 0; } int fact (int n) { if (n = = 0) return (1); else return (n * fact(n-1)); } 54 TRACE OF THE STACK DURING EXECUTION n=0 1 RA .. fact fact returns to main n=1 - n=1 - n=1 1*1 = 1 RA .. fact RA .. fact RA .. fact n=2 - n=2 - n=2 - n=2 - n=2 2*1 = 2 RA .. fact RA .. fact RA .. fact RA .. fact RA .. fact n=3 - n=3 - n=3 - n=3 - n=3 - n=3 - n=3 3*2 = 6 RA .. main RA .. main RA .. main RA .. main RA .. main RA .. main RA .. main main calls fact 55 Do Yourself Trace the activation records for the following version of Fibonacci sequence ■ X Y main int f (int n) { int a, b; if (n < 2) return (n); else { a = f(n-1); b = f(n-2); return (a+b); } } void main() { printf(“Fib(4) is: %d \n”, f(4)); } Local Variables (n, a, b) Return Value Return Addr (either main, or X, or Y) 56 Additional Example 57 Towers of Hanoi Problem 1 2 3 4 5 LEFT CENTER RIGHT 58 ■ Initially all the disks are stacked on the LEFT pole ■ Required to transfer all the disks to the RIGHT pole ■ ◻ Only one disk can be moved at a time. ◻ A larger disk cannot be placed on a smaller disk CENTER pole is used for temporary storage of disks 59 60 ■ Recursive statement of the general problem of n disks ◻ Step 1: ■ ◻ Step 2: ■ ◻ Move the top (n-1) disks from LEFT to CENTER Move the largest disk from LEFT to RIGHT Step 3: ■ Move the (n-1) disks from CENTER to RIGHT 61 Tower of Hanoi A B C 62 Tower of Hanoi A B C 63 Tower of Hanoi A B C 64 Tower of Hanoi A B C 65 Towers of Hanoi function void towers (int n, char from, char to, char aux) { /* Base Condition */ if (n==1) { printf (“Disk 1 : %c -> &c \n”, from, to) ; return ; } /* Recursive Condition */ towers (n-1, from, aux, to) ; ……………………. ……………………. } 66 Towers of Hanoi function void towers (int n, char from, char to, char aux) { /* Base Condition */ if (n==1) { printf (“Disk 1 : %c -> &c \n”, from, to) ; return ; } /* Recursive Condition */ towers (n-1, from, aux, to) ; printf (“Disk %d : %c -> %c\n”, n, from, to) ; ……………………. } 67 Towers of Hanoi function void towers (int n, char from, char to, char aux) { /* Base Condition */ if (n==1) { printf (“Disk 1 : %c -> %c \n”, from, to) ; return ; } /* Recursive Condition */ towers (n-1, from, aux, to) ; printf (“Disk %d : %c -> %c\n”, n, from, to) ; towers (n-1, aux, to, from) ; } 68 TOH runs void towers(int n, char from, char to, char aux) { if (n==1) { printf ("Disk 1 : %c -> %c \n", from, to) ; return ; } towers (n-1, from, aux, to) ; printf ("Disk %d : %c -> %c\n", n, from, to) ; towers (n-1, aux, to, from) ; } int main() { int n; scanf("%d", &n); towers(n,'A',‘C',‘B'); return 0; } Output 3 Disk 1 : A -> C Disk 2 : A -> B Disk 1 : C -> B Disk 3 : A -> C Disk 1 : B -> A Disk 2 : B -> C Disk 1 : A -> C 69 More TOH runs void towers(int n, char from, char to, char aux) { if (n==1) { printf ("Disk 1 : %c -> %c \n", from, to) ; return ; } towers (n-1, from, aux, to) ; printf ("Disk %d : %c -> %c\n", n, from, to) ; towers (n-1, aux, to, from) ; } int main() { int n; scanf("%d", &n); towers(n,'A',‘C',‘B'); return 0; } Output 4 Disk 1 : A -> B Disk 2 : A -> C Disk 1 : B -> C Disk 3 : A -> B Disk 1 : C -> A Disk 2 : C -> B Disk 1 : A -> B Disk 4 : A -> C Disk 1 : B -> C Disk 2 : B -> A Disk 1 : C -> A Disk 3 : B -> C Disk 1 : A -> B Disk 2 : A -> C Disk 1 : B -> C 70 Exercise 1: count zeros in an Integer The problem is: given an integer, how many of its digits are zero? Insight: ■ How to get least digit? ■ ■ 71 Check if that is zero Based on this, what can we say about the number of zeros in the integer? int zeros( int number){ if(number<10) return 0; if (number%10 == 0) return(1+zeros(number/10)); else return(zeros(number/10)); } zeros(10200) zeros(1020) + zeros(0) zeros(102) + zeros(0) + zeros(0) zeros(10) + zeros(2) + zeros(0) + zeros(0) zeros(1) + zeros(0) + zeros(2) + zeros(0) + zeros(0) Another Approach int zeros(int numb){ if(numb==0) // 1 digit (zero/non-zero): return 1; // bottom out. else if(numb < 10) return 0; else // > 1 digits: recursion return (zeros(numb/10) + zeros(numb%10)); } 73 Exercise 1 ■ ■ ■ Write a recursive function to compute the nth term of a series given by the following recurrence relation: F(0)=0 F(n)=2*F(n-1)+3*F(n-2)+n 74 Exercise 2 ■ Write a recursive routine that takes an array and its size as parameters and computes its mean. 75 Practice Problems 1. Write a recursive function to search for an element in an array 2. Write a recursive function to count the digits of a positive integer (do also for sum of digits) 3. Write a recursive function to reverse a null-terminated string 4. Write a recursive function to convert a decimal number to binary 5. Write a recursive function to check if a string is a palindrome or not 6. Write a recursive function to copy one array to another Note: • For each of the above, write the main functions to call the recursive function also • Practice problems are just for practicing recursion, recursion is not necessarily the most efficient way of doing them 76 77 2-d Arrays 1 Two Dimensional Arrays We have seen that an array variable can store a list of values Many applications require us to store a table of values Subject 1 Subject 2 Student 1 Student 2 Student 3 Student 4 75 68 88 50 82 75 74 65 Subject 3 Subject 4 Subject 5 90 80 85 68 65 70 76 40 76 72 80 70 2 Contd. The table contains a total of 20 values, five in each line The table can be regarded as a matrix consisting of four rows and five columns C allows us to define such tables of items by using two-dimensional arrays 3 Declaring 2-D Arrays General form: type array_name [row_size][column_size]; Examples: int marks[4][5]; float sales[12][25]; double matrix[100][100]; 4 Initializing 2-d arrays int a[2][3] = {1,2,3,4,5,6}; int a[2][3] = {{1,2,3}, {4,5,6}}; int a[][3] = {{1,2,3}, {4,5,6}}; All of the above will give the 2x3 array 1 4 2 3 5 6 5 Accessing Elements of a 2-d Array Similar to that for 1-d array, but use two indices First indicates row, second indicates column Both the indices should be expressions which evaluate to integer values (within range of the sizes mentioned in the array declaration) Examples: x[m][n] = 0; c[i][k] += a[i][j] * b[j][k]; a = sqrt (a[j*3][k]); 6 Example int a[3][5]; A two-dimensional array of 15 elements Can be looked upon as a table of 3 rows and 5 columns col0 col1 col2 col3 col4 row0 a[0][0] a[0][1] a[0][2] a[0][3] a[0][4] row1 a[1][0] a[1][1] a[1][2] a[1][3] a[1][4] row2 a[2][0] a[2][1] a[2][2] a[2][3] a[2][4] 7 How is a 2-d array is stored in memory? Starting from a given memory location, the elements are stored row-wise in consecutive memory locations (row-major order) x: starting address of the array in memory c: number of columns k: number of bytes allocated per array element a[i][j] is allocated memory location at address x + (i * c + j) * k a[0]0] a[0][1] a[0]2] a[0][3] a[1][0] a[1][1] a[1][2] a[1][3] a[2][0] a[2][1] a[2][2] a[2][3] Row 0 Row 1 Row 2 8 Array Addresses int main() { int a[3][5]; int i,j; for (i=0; i<3;i++) { for (j=0; j<5; j++) printf("%u\n", &a[i][j]); printf("\n"); } return 0; } Output 3221224480 3221224484 3221224488 3221224492 3221224496 3221224500 3221224504 3221224508 3221224512 3221224516 3221224520 3221224524 3221224528 3221224532 3221224536 9 How to read the elements of a 2-d array? By reading them one element at a time for (i=0; i<nrow; i++) for (j=0; j<ncol; j++) scanf (“%f”, &a[i][j]); The ampersand (&) is necessary The elements can be entered all in one line or in different lines 10 How to print the elements of a 2-d array? By printing them one element at a time for (i=0; i<nrow; i++) for (j=0; j<ncol; j++) printf (“\n %f”, a[i][j]); The elements are printed one per line for (i=0; i<nrow; i++) for (j=0; j<ncol; j++) printf (“%f”, a[i][j]); The elements are all printed on the same line11 Contd. for (i=0; i<nrow; i++) { printf (“\n”); for (j=0; j<ncol; j++) printf (“%f ”, a[i][j]); } The elements are printed nicely in matrix form 12 Example: Matrix Addition int main() { int a[100][100], b[100][100], c[100][100], p, q, m, n; for (p=0; p<m; p++) for (q=0; q<n; q++) c[p][q] = a[p][q] + b[p][q]; scanf (“%d %d”, &m, &n); for (p=0; p<m; p++) { printf (“\n”); for (q=0; q<n; q++) printf (“%d ”, c[p][q]); } return 0; for (p=0; p<m; p++) for (q=0; q<n; q++) scanf (“%d”, &a[p][q]); for (p=0; p<m; p++) for (q=0; q<n; q++) scanf (“%d”, &b[p][q]); } 13 Passing 2-d Arrays as Parameters Similar to that for 1-D arrays The array contents are not copied into the function Rather, the address of the first element is passed For calculating the address of an element in a 2-d array, we need: The starting address of the array in memory Number of bytes per element Number of columns in the array The above three pieces of information must be known to the function 14 Example Usage int main() { int a[15][25], b[15]25]; : : add (a, b, 15, 25); : } void add (int x[][25], int y[][25], int rows, int cols) { : } We can also write int x[15][25], y[15][25]; But at least 2nd dimension must be given 15 Example: Matrix Addition with Functions void ReadMatrix(int A[][100], int x, int y) { int i, j; for (i=0; i<x; i++) for (j=0; j<y; j++) scanf (“%d”, &A[i][j]); } void AddMatrix( int A[][100], int B[][100], int C[][100], int x, int y) { int i , j; for (i=0; i<x; i++) for (j=0; j<y; j++) C[i][j] = A[i][j] + B[i][j]; } 16 int main() { void PrintMatrix(int A[][100], int x, int y) int a[100][100], b[100][100], { c[100][100], p, q, m, n; int i, j; printf(“\n”); scanf (“%d%d”, &m, &n); for (i=0; i<x; i++) { ReadMatrix(a, m, n); for (j=0; j<y; j++) ReadMatrix(b, m, n); printf (“ %5d”, A[i][j]); printf(“\n”); AddMatrix(a, b, c, m, n); } } PrintMatrix(c, m, n); return 0; } 17 Practice Problems 1. 2. 3. 4. 5. Write a function that takes an n x n square matrix A as parameter (n < 100) and returns 1 if A is an upper-triangular matrix, 0 otherwise. Repeat 1 to check for lower-triangular matrix, diagonal matrix, identity matrix Write a function that takes as parameter an m x n matrix A (m, n < 100) and returns the transpose of A (modifies in A only). Consider an n x n matrix containing only 0 or 1. Write a function that takes such a matrix and returns 1 if the number of 1’s in each row are the same and the number of 1’s in each column are the same; it returns 0 otherwise Write a function that reads in an m x n matrix A and an n x p matrix B, and returns the product of A and B in another matrix C. Pass appropriate parameters. For each of the above, also write a main function that reads the matrices, calls the function, and prints the results (a message, the transposed matrix etc.) 18 Structures 19 What is a Structure? Used for handling a group of logically related data items Examples: Student name, roll number, and marks Real part and complex part of a complex number Helps in organizing complex data in a more meaningful way The individual structure elements are called members 20 Defining a Structure struct tag { member 1; member 2; : member m; }; struct is the required C keyword tag is the name of the structure member 1, member 2, … are individual member declarations Do not forget the ; at the end! 21 Contd. The individual members can be ordinary variables, pointers, arrays, or other structures (any data type) The member names within a particular structure must be distinct from one another A member name can be the same as the name of a variable defined outside of the structure Once a structure has been defined, the individual structure-type variables can be declared as: struct tag var_1, var_2, …, var_n; 22 Example A structure definition struct student { char name[30]; int roll_number; int total_marks; char dob[10]; }; Defining structure variables: struct student a1, a2, a3; A new data-type 23 A Compact Form It is possible to combine the declaration of the structure with that of the structure variables: struct tag { member 1; member 2; : member m; } var_1, var_2,…, var_n; Declares three variables of type struct tag In this form, tag is optional 24 Accessing a Structure The members of a structure are processed individually, as separate entities Each member is a separate variable A structure member can be accessed by writing variable.member where variable refers to the name of a structure-type variable, and member refers to the name of a member within the structure Examples: a1.name, a2.name, a1.roll_number, a3.dob 25 Example: Complex number addition struct complex { float real; Defines the structure float img; }; int main() Declares 3 variable of type struct complex { struct complex a, b, c; scanf (“%f %f”, &a.real, &a.img); Accessing the variables is the same scanf (“%f %f”, &b.real, &b.img); as any other variable, just have to follow the syntax to specify which field c.real = a.real + b.real; of the Structure you want c.img = a.img + b.img; printf (“\n %f + %f j”, c.real, c.img); return 0; } 26 Operations on Structure Variables Unlike arrays, a structure variable can be directly assigned to another structure variable of the same type a1 = a2; All the individual members get assigned Two structure variables cannot be compared for equality or inequality if (a1 == a2)…… this cannot be done 27 Arrays of Structures Once a structure has been defined, we can declare an array of structures struct student class[50]; type name The individual members can be accessed as: class[i].name class[5].roll_number 28 Example: Reading and Printing Array of Structures int main() { struct complex A[100]; int n; scanf(“%d”, &n); for (i=0; i<n; i++) scanf(“%f%f”, &A[i].real, &A[i].img); for (i=0; i<n; i++) printf(“%f + i%f\n”, A[i].real, A[i].img); } 29 Arrays within Structures A structure member can be an array struct student { char name[30]; int roll_number; int marks[5]; char dob[10]; } a1, a2, a3; The array element within the structure can be accessed as: a1.marks[2], a1.dob[3],… 30 Structure Initialization Structure variables may be initialized following similar rules of an array. The values are provided within the second braces separated by commas An example: struct complex a={1.0,2.0}, b={-3.0,4.0}; a.real=1.0; a.img=2.0; b.real=-3.0; b.img=4.0; 31 Parameter Passing in a Function Structure variables can be passed as parameters like any other variables. Only the values will be copied during function invocation int chkEqual(struct complex a, struct complex b) { if ((a.real==b.real) && (a.img==b.img)) return 1; else return 0; } 32 Returning Structures It is also possible to return structure values from a function. The return data type of the function should be as same as the data type of the structure itself struct complex add(struct complex a, struct complex b) { struct complex tmp; tmp.real = a.real + b.real; tmp.img = a.img + b.img; return(tmp); } Direct arithmetic operations are not possible with structure variables 33 Defining Data Type: using typedef One may define a structure data-type with a single name typedef struct newtype { member-variable1; member-variable2; . member-variableN; } mytype; mytype is the name of the new data-type Also called an alias for struct newtype Writing the tag name newtype is optional, can be skipped Naming follows rules of variable naming 34 typedef : An example typedef struct { float real; float imag; } _COMPLEX; Defined a new data type named _COMPLEX. Now can declare and use variables of this type _COMPLEX a, b, c; 35 More about typedef Note: typedef is not restricted to just structures, can define new types from any existing type Example: typedef int INTEGER Defines a new type named INTEGER from the known type int Can now define variables of type INTEGER which will have all properties of the int type INTEGER a, b, c; 36 The earlier program using typedef typedef struct{ float real; float img; } _COMPLEX; _COMPLEX add(_COMPLEX a, _COMPLEX b) { _COMPLEX tmp; tmp.real = a.real + b.real; tmp.img = a.img + b.img; return(tmp); } 37 Contd. void print (_COMPLEX a) { printf("(%f, %f) \n",a.real,a.img); } Output (4.000000, 5.000000) (10.000000, 15.000000) (14.000000, 20.000000) int main() { _COMPLEX x={4.0,5.0}, y={10.0,15.0}, z; print(x); print(y); z = add(x,y); print(z); return 0; } swap.c 38 Practice Problems 1. 2. Extend the complex number program to include functions for addition, subtraction, multiplication, and division Define a structure for representing a point in two-dimensional Cartesian co-ordinate system. Using this structure for a point Write a function to return the distance between two given points 2. Write a function to return the middle point of the line segment joining two given points 3. Write a function to compute the area of a triangle formed by three given points 4. Write a main function and call the functions from there after reading in appropriate inputs (the points) from the keyboard 1. 39 3. Define a structure STUDENT to store the following data for a student: name (nullterminated string of length at most 20 chars), roll no. (integer), CGPA (float). Then 1. 2. 3. 4. 5. In main, declare an array of 100 STUDENT structures. Read an integer n and then read in the details of n students in this array Write a function to search the array for a student by name. Returns the structure for the student if found. If not found, return a special structure with the name field set to empty string (just a ‘\0’) Write a function to search the array for a student by roll no. Write a function to print the details of all students with CGPA > x for a given x Call the functions from the main after reading in name/roll no/CGPA to search 40 Number Representation Part I 1 Basics of Number System We are accustomed to using the so-called decimal number system Ten digits :: 0,1,2,3,4,5,6,7,8,9 Every digit position has a weight which is a power of 10 Base or radix is 10 Example: 234 = 2 x 102 + 3 x 101 + 4 x 100 250.67 = 2 x 102 + 5 x 101 + 0 x 100 + 6 x 10-1 + 7 x 10-2 2 Binary Number System Two digits: 0 and 1 Every digit position has a weight which is a power of 2 Base or radix is 2 Example: 110 = 1 x 22 + 1 x 21 + 0 x 20 101.01 = 1 x 22 + 0 x 21 + 1 x 20 + 0 x 2-1 + 1 x 2-2 3 Positional Number Systems (General) Decimal Numbers: 10 Symbols {0,1,2,3,4,5,6,7,8,9}, Base or Radix is 10 136.25 = 1 102 + 3 101 + 6 100 + 2 10–1 + 3 10–2 4 Positional Number Systems (General) Decimal Numbers: 10 Symbols {0,1,2,3,4,5,6,7,8,9}, Base or Radix is 10 136.25 = 1 102 + 3 101 + 6 100 + 2 10–1 + 3 10–2 Binary Numbers: 2 Symbols {0,1}, Base or Radix is 2 101.01 = 1 22 + 0 21 + 1 20 + 0 2–1 + 1 2–2 5 Positional Number Systems (General) Decimal Numbers: 10 Symbols {0,1,2,3,4,5,6,7,8,9}, Base or Radix is 10 136.25 = 1 102 + 3 101 + 6 100 + 2 10–1 + 5 10–2 Binary Numbers: 2 Symbols {0,1}, Base or Radix is 2 101.01 = 1 22 + 0 21 + 1 20 + 0 2–1 + 1 2–2 Octal Numbers: 8 Symbols {0,1,2,3,4,5,6,7}, Base or Radix is 8 621.03 = 6 82 + 2 81 + 1 80 + 0 8–1 + 3 8–2 6 Positional Number Systems (General) Decimal Numbers: 10 Symbols {0,1,2,3,4,5,6,7,8,9}, Base or Radix is 10 136.25 = 1 102 + 3 101 + 6 100 + 2 10–1 + 3 10–2 Binary Numbers: 2 Symbols {0,1}, Base or Radix is 2 101.01 = 1 22 + 0 21 + 1 20 + 0 2–1 + 1 2–2 Octal Numbers: 8 Symbols {0,1,2,3,4,5,6,7}, Base or Radix is 8 621.03 = 6 82 + 2 81 + 1 80 + 0 8–1 + 3 8–2 Hexadecimal Numbers: 16 Symbols {0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F}, Base is 16 6AF.3C = 6 162 + 10 161 + 15 160 + 3 16–1 + 12 16–2 7 Binary-to-Decimal Conversion Each digit position of a binary number has a weight Some power of 2 A binary number: B = bn-1 bn-2 …..b1 b0 . b-1 b-2 ….. b-m Corresponding value in decimal: n-1 D = bi 2i i = -m 8 Examples 101011 1x25 + 0x24 + 1x23 + 0x22 + 1x21 + 1x20 = 43 (101011)2 = (43)10 .0101 0x2-1 + 1x2-2 + 0x2-3 + 1x2-4 = .3125 (.0101)2 = (.3125)10 1x22 + 0x21 + 1x20 + 1x2-1 + 1x2-2 = 5.75 (101.11)2 = (5.75)10 101.11 9 Decimal to Binary: Integer Part Consider the integer and fractional parts separately. For the integer part: Repeatedly divide the given number by 2, and go on accumulating the remainders, until the number becomes zero. Arrange the remainders in reverse order. Base NumbRem 2 2 2 2 2 2 2 89 44 22 11 5 2 1 0 1 0 0 1 1 0 1 (89)10 = (1011001)2 10 Decimal to Binary: Integer Part Consider the integer and fractional parts separately. For the integer part: Repeatedly divide the given number by 2, and go on accumulating the remainders, until the number becomes zero. Arrange the remainders in reverse order. Base NumbRem 2 2 2 2 2 2 2 89 44 22 11 5 2 1 0 1 0 0 1 1 0 1 2 2 2 2 2 2 2 66 33 16 8 4 2 1 0 0 1 0 0 0 0 1 (66)10 = (1000010)2 (89)10 = (1011001)2 11 Decimal to Binary: Integer Part Consider the integer and fractional parts separately. For the integer part: Repeatedly divide the given number by 2, and go on accumulating the remainders, until the number becomes zero. Arrange the remainders in reverse order. Base NumbRem 2 2 2 2 2 2 2 89 44 22 11 5 2 1 0 1 0 0 1 1 0 1 2 2 2 2 2 2 2 66 33 16 8 4 2 1 0 0 1 0 0 0 0 1 (66)10 = (1000010)2 (89)10 = (1011001)2 2 2 2 2 2 2 2 2 239 119 59 29 14 7 3 1 0 1 1 1 1 0 1 1 1 (239)10 = (11101111)2 12 Decimal to Binary: Fraction Part Repeatedly multiply the given fraction by 2. Accumulate the integer part (0 or 1). If the integer part is 1, chop it off. Arrange the integer parts in the order they are obtained. Example: 0.634 .634 .268 .536 .072 .144 x x x x x 2 2 2 2 2 = = = = = 1.268 0.536 1.072 0.144 0.288 : : (.634)10 = (.10100……)2 13 Decimal to Binary: Fraction Part Repeatedly multiply the given fraction by 2. Accumulate the integer part (0 or 1). If the integer part is 1, chop it off. Arrange the integer parts in the order they are obtained. Example: 0.634 Example: 0.0625 .634 .268 .536 .072 .144 .0625 .1250 .2500 .5000 x x x x x 2 2 2 2 2 = = = = = 1.268 0.536 1.072 0.144 0.288 x x x x 2 2 2 2 = = = = 0.125 0.250 0.500 1.000 (.0625)10 = (.0001)2 : : (.634)10 = (.10100……)2 14 Decimal to Binary: Fraction Part Repeatedly multiply the given fraction by 2. Accumulate the integer part (0 or 1). If the integer part is 1, chop it off. Arrange the integer parts in the order they are obtained. Example: 0.634 Example: 0.0625 .634 .268 .536 .072 .144 .0625 .1250 .2500 .5000 x x x x x 2 2 2 2 2 = = = = = 1.268 0.536 1.072 0.144 0.288 : : (.634)10 = (.10100……)2 x x x x 2 2 2 2 = = = = 0.125 0.250 0.500 1.000 (.0625)10 = (.0001)2 (37)10 = (100101)2 (.0625)10 = (.0001)2 (37.0625)10 = (100101.0001)2 15 Hexadecimal Number System A compact way of representing binary numbers 16 different symbols (radix = 16) 0 1 2 3 4 5 6 7 0000 0001 0010 0011 0100 0101 0110 0111 8 9 A B C D E F 1000 1001 1010 1011 1100 1101 1110 1111 16 Binary-to-Hexadecimal Conversion For the integer part, Scan the binary number from right to left Translate each group of four bits into the corresponding hexadecimal digit Add leading zeros if necessary For the fractional part, Scan the binary number from left to right Translate each group of four bits into the corresponding hexadecimal digit Add trailing zeros if necessary 17 Example 1. (1011 0100 0011)2 = (B43)16 2. (10 1010 0001)2 = (2A1)16 3. (.1000 010)2 = (.84)16 4. (101 . 0101 111)2 = (5.5E)16 18 Hexadecimal-to-Binary Conversion Translate every hexadecimal digit into its 4-bit binary equivalent Examples: (3A5)16 = (0011 1010 0101)2 (12.3D)16 = (0001 0010 . 0011 1101)2 (1.8)16 = (0001 . 1000)2 19 Number Representation Part II 20 Unsigned Binary Numbers An n-bit binary number B = bn-1bn-2 …. b2b1b0 2n distinct combinations are possible, 0 to 2n-1. For example, for n = 3, there are 8 distinct combinations 000, 001, 010, 011, 100, 101, 110, 111 Range of numbers that can be represented n=8 n=16 n=32 0 to 28-1 (255) 0 to 216-1 (65535) 0 to 232-1 (4294967295) 21 Signed Integer Representation Many of the numerical data items that are used in a program are signed (positive or negative) Question:: How to represent sign? Three possible approaches: Sign-magnitude representation One’s complement representation Two’s complement representation 22 Sign-magnitude Representation For an n-bit number representation The most significant bit (MSB) indicates sign 0 positive 1 negative The remaining n-1 bits represent magnitude bn-1 Sign bn-2 b1 b0 Magnitude 23 Contd. Range of numbers that can be represented: Maximum :: + (2n-1 – 1) Minimum :: (2n-1 – 1) A problem: Two different representations of zero +0 0 000….0 -0 1 000….0 24 One’s Complement Representation Basic idea: Positive numbers are represented exactly as in sign-magnitude form Negative numbers are represented in 1’s complement form How to compute the 1’s complement of a number? Complement every bit of the number (10 and 01) MSB will indicate the sign of the number 0 positive 1 negative 25 Example: n=4 0000 0001 0010 0011 0100 0101 0110 0111 +0 +1 +2 +3 +4 +5 +6 +7 1000 1001 1010 1011 1100 1101 1110 1111 -7 -6 -5 -4 -3 -2 -1 -0 To find the representation of, say, -4, first note that +4 = 0100 -4 = 1’s complement of 0100 = 1011 26 Contd. Range of numbers that can be represented: Maximum :: + (2n-1 – 1) Minimum :: (2n-1 – 1) A problem: Two different representations of zero. +0 0 000….0 -0 1 111….1 Advantage of 1’s complement representation Subtraction can be done using addition Leads to substantial saving in circuitry 27 Two’s Complement Representation Basic idea: Positive numbers are represented exactly as in sign-magnitude form Negative numbers are represented in 2’s complement form How to compute the 2’s complement of a number? Complement every bit of the number (10 and 01), and then add one to the resulting number MSB will indicate the sign of the number 0 positive 1 negative 28 Example: n=4 0000 0001 0010 0011 0100 0101 0110 0111 +0 +1 +2 +3 +4 +5 +6 +7 1000 1001 1010 1011 1100 1101 1110 1111 -8 -7 -6 -5 -4 -3 -2 -1 To find the representation of, say, -4, first note that +4 = 0100 -4 = 2’s complement of 0100 = 1011+1 = 1100 Rule : Value = – msb*2(n–1) + [unsigned value of rest] Example: 0110 = 0 + 6 = 6 1110 = – 8 + 6 = – 2 29 Contd. Range of numbers that can be represented: Maximum :: + (2n-1 – 1) Minimum :: 2n-1 Advantage: Unique representation of zero Subtraction can be done using addition Leads to substantial saving in circuitry Almost all computers today use the 2’s complement representation for storing negative numbers 30 Adding Binary Numbers Basic Rules: Example: 0+0=0 0+1=1 1+0=1 1+1=0 (carry 1) 01101001 00110100 ------------10011101 31 Subtraction Using Addition: 1’s Complement How to compute A – B ? Compute the 1’s complement of B (say, B1). Compute R = A + B1 If the carry obtained after addition is ‘1’ Add the carry back to R (called end-around carry) That is, R = R + 1 The result is a positive number Else The result is negative, and is in 1’s complement form 32 Example 1: 6 – 2 1’s complement of 2 = 1101 6 :: 0110 -2 :: 1101 1 0011 1 0100 A B1 R Assume 4-bit representations Since there is a carry, it is added back to the result The result is positive +4 End-around carry 33 Example 2: 3 – 5 1’s complement of 5 = 1010 3 :: 0011 -5 :: 1010 A B1 1101 R Assume 4-bit representations Since there is no carry, the result is negative 1101 is the 1’s complement of 0010, that is, it represents –2 -2 34 Subtraction Using Addition: 2’s Complement How to compute A – B ? Compute the 2’s complement of B (say, B2) Compute R = A + B2 If the carry obtained after addition is ‘1’ Ignore the carry The result is a positive number Else The result is negative, and is in 2’s complement form 35 Example 1: 6 – 2 2’s complement of 2 = 1101 + 1 = 1110 6 :: 0110 -2 :: 1110 1 0100 Ignore carry +4 A B2 R Assume 4-bit representations Presence of carry indicates that the result is positive No need to add the endaround carry like in 1’s complement 36 Example 2: 3 – 5 2’s complement of 5 = 1010 + 1 = 1011 3 :: 0011 -5 :: 1011 1110 -2 A B2 R Assume 4-bit representations Since there is no carry, the result is negative 1110 is the 2’s complement of 0010, that is, it represents –2 37 2’s complement arithmetic: More Examples Example 1: 18-11 = ? 18 is represented as 00010010 11 is represented as 00001011 1’s complement of 11 is 11110100 2’s complement of 11 is 11110101 Add 18 to 2’s complement of 11 00010010 + 11110101 ---------------00000111 (with a carry of 1 which is ignored) 00000111 is 7 38 Example 2: 7 - 9 = ? 7 is represented as 00000111 9 is represented as 00001001 1’s complement of 9 is 11110110 2’s complement of 9 is 11110111 Add 7 to 2’s complement of 9 00000111 + 11110111 ---------------11111110 (with a carry of 0 which is ignored) 11111110 is -2 39 Number Representation Part III 40 Overflow/Underflow: Adding two +ve (-ve) numbers should not produce a –ve (+ve) number. If it does, overflow (underflow) occurs 41 Overflow/Underflow: Adding two +ve (-ve) numbers should not produce a –ve (+ve) number. If it does, overflow (underflow) occurs Another equivalent condition : carry in and carry out from Most Significant Bit (MSB) differ. 42 Overflow/Underflow: Adding two +ve (-ve) numbers should not produce a –ve (+ve) number. If it does, overflow (underflow) occurs Another equivalent condition : carry in and carry out from Most Significant Bit (MSB) differ. (64) 01000000 ( 4) 00000100 -------------(68) 01000100 carry (out)(in) 0 0 43 Overflow/Underflow: Adding two +ve (-ve) numbers should not produce a –ve (+ve) number. If it does, overflow (underflow) occurs Another equivalent condition : carry in and carry out from Most Significant Bit (MSB) differ. (64) 01000000 ( 4) 00000100 -------------(68) 01000100 (64) 01000000 (96) 01100000 -------------(-96) 10100000 carry (out)(in) 0 0 carry out in differ: 0 1 overflow 44 Floating-point Numbers The representations discussed so far applies only to integers Cannot represent numbers with fractional parts We can assume a decimal point before a signed number In that case, pure fractions (without integer parts) can be represented We can also assume the decimal point somewhere in between This lacks flexibility Very large and very small numbers cannot be represented 45 Representation of Floating-Point Numbers A floating-point number F is represented by a doublet <M,E> : F = M x BE B exponent base (usually 2) M mantissa E exponent M is usually represented in 2’s complement form, with an implied binary point before it For example, In decimal, 0.235 x 106 In binary, 0.101011 x 20110 46 Example :: 32-bit representation M M E 24 8 represents a 2’s complement fraction 1 > M > -1 E represents the exponent (in 2’s complement form) 127 > E > -128 Points to note: The number of significant digits depends on the number of bits in M 6 significant digits for 24-bit mantissa The range of the number depends on the number of bits in E 38 to 10-38 for 8-bit exponent. 10 47 Sign bit is added in front to represent both +ve and –ve numbers The representation shown for floating-point numbers as shown is just for illustration The actual representation is a little more complex, we will not do here Example: IEEE 754 Floating Point format 48 Pointers: Basics Lecture 24 What is a pointer? First of all, it is a variable, just like other variables you studied So it has type, storage etc. Difference: it can only store the address (rather than the value) of a data item Type of a pointer variable – pointer to the type of the data whose address it will store Example: int pointer, float pointer,… Can be pointer to any user-defined types also like structure types 2 Usage of Pointers They have a number of useful applications Enables us to access a variable that is defined outside the function Can be used to pass information back and forth between a function and its reference point More efficient in handling data tables Reduces the length and complexity of a program Sometimes also increases the execution speed Basic Concept As seen before, in memory, every stored data item occupies one or more contiguous memory cells The number of memory cells required to store a data item depends on its type (char, int, double, etc.). Whenever we declare a variable, the system allocates memory location(s) to hold the value of the variable. Since every byte in memory has a unique address, this location will also have its own (unique) address. 4 Contd. Consider the statement int xyz = 50; This statement instructs the compiler to allocate a location for the integer variable xyz, and put the value 50 in that location Suppose that the address location chosen is 1380 xyz variable 50 1380 value address 5 Contd. During execution of the program, the system always associates the name xyz with the address 1380 The value 50 can be accessed by using either the name xyz or the address 1380 Since memory addresses are simply numbers, they can be assigned to some variables which can be stored in memory Such variables that hold memory addresses are called pointers Since a pointer is a variable, its value is also stored in some memory location 6 Contd. Suppose we assign the address of xyz to a variable p p is said to point to the variable xyz Variable Value Address xyz 50 1380 p 1380 2545 p = &xyz; 7 Address vs. Value Each memory cell has an address associated with it ... 101 102 103 104 105 ... ... 8 Address vs. Value Each memory cell has an address associated with it Each cell also stores some value ... 101 102 103 104 105 ... 23 42 ... 9 Address vs. Value Each memory cell has an address associated with it Each cell also stores some value Don’t confuse the address referring to a memory location with the value stored in that location ... 101 102 103 104 105 ... 23 42 ... 10 Values vs Locations Variables name memory locations, which hold values 1024: address 32 x value name 11 Pointers in C A pointer is just a C variable whose value can contain the address of another variable Needs to be declared before use just like any other variable General form: data_type *pointer_name; Three things are specified in the above declaration: The asterisk (*) tells that the variable pointer_name is a pointer variable pointer_name needs a memory location pointer_name points to a variable of type data_type 12 Example int *count; float *speed; char *c; Once a pointer variable has been declared, it can be made to point to a variable using an assignment statement like int *p, xyz; : p = &xyz; 13 Structure Pointer Pointers can be defined for any type, including user defined types Example struct name { char first[20]; char last[20]; }; struct name *p; p is a pointer which can store the address of a struct name type variable Accessing the Address of a Variable The address of a variable is given by the & operator The operator & immediately preceding a variable returns the address of the variable Example: p = &xyz; The address of xyz (1380) is assigned to p The & operator can be used only with a simple variable (of any type, including user-defined types) or an array element &distance &x[0] &x[i-2] 15 Illegal Use of & &235 Pointing at constant int arr[20]; : &arr; Pointing at array name &(a+b) Pointing at expression In all these cases, there is no storage, so no address either 16 Example #include <stdio.h> int main() { int a; float b, c; double d; char ch; a = 10; b = 2.5; c = 12.36; d = 12345.66; ch = ‘A’; printf (“%d is stored in location %u \n”, a, &a) ; printf (“%f is stored in location %u \n”, b, &b) ; printf (“%f is stored in location %u \n”, c, &c) ; printf (“%lf is stored in location %u \n”, d, &d) ; printf (“%c is stored in location %u \n”, ch, &ch) ; return 0; } 17 Output 10 is stored in location 3221224908 2.500000 is stored in location 3221224904 12.360000 is stored in location 3221224900 12345.660000 is stored in location 3221224892 A is stored in location 3221224891 18 Accessing a Variable Through its Pointer Once a pointer has been assigned the address of a variable, the value of the variable can be accessed using the indirection operator (*). int a, b; int *p; p = &a; b = *p; Equivalent to b = a; 19 Example #include <stdio.h> int main() { int a, b; int c = 5; int *p; a = 4 * (c Equivalent + 5) ; p = &c; b = 4 * (*p + 5) ; printf (“a=%d b=%d \n”, return 0; a, b); a=40 b=40 } 20 Example int main() { int x, y; int *ptr; x = 10 ; ptr = &x ; y = *ptr ; printf (“%d printf (“%d printf (“%d printf (“%d printf (“%u printf (“%d is is is is is is stored stored stored stored stored stored in in in in in in location location location location location location %u %u %u %u %u %u \n”, \n”, \n”, \n”, \n”, \n”, x, &x); *&x, &x); *ptr, ptr); y, &*ptr); ptr, &ptr); y, &y); *ptr = 25; printf (“\nNow x = %d \n”, x); return 0; } 21 Suppose that Address of x: 3221224908 Address of y: 3221224904 Address of ptr: 3221224900 Then output is 10 is stored in location 3221224908 10 is stored in location 3221224908 10 is stored in location 3221224908 10 is stored in location 3221224908 3221224908 is stored in location 3221224900 10 is stored in location 3221224904 Now x = 25 22 Example pointer to int int x; int xp ; 1024: xp = &x ; 32 x 1024 xp address of x xp = 0; /* Assign 0 to x */ xp = xp + 1; /* Add 1 to x */ 23 Value of the pointer Declaring a pointer just allocates space to hold the pointer – it does not allocate something to be pointed to! Local variables in C are not initialized, they may contain anything After declaring a pointer: int *ptr; ptr doesn’t actually point to anything yet. We can either: make it point to something that already exists, or allocate room in memory for something new that it will point to… (dynamic allocation, to be done later) 24 Example 0 Memory and Pointers: 1500 2300 25 0 Memory and Pointers: int *p, v; v: arbitrary value 1500 p: arbitrary value 2300 26 0 v: arbitrary value p: 1500 1500 Memory and Pointers: int v, *p; p = &v; 2300 27 0 v: 17 1500 p: 1500 2300 Memory and Pointers: int v, *p; p = &v; v = 17; 28 0 v: 25 1500 p: 1500 2300 Memory and Pointers: int v, *p; p = &v; v = 17; *p = *p + 4; v = *p + 4 29 Pointers: More ... Lecture 25 More Examples of Using Pointers in Expressions If p1 and p2 are two pointers, the following statements are valid: sum = *p1 + *p2; prod = *p1 * *p2; prod = (*p1) * (*p2); *p1 = *p1 + 2; x = *p1 / *p2 + 5; *p1 can appear on the left hand side Note that this unary * has higher precedence than all arithmetic/relational/logical operators 31 Important Things to Remember Pointer variables must always point to a data item of the same type float x; int *p; : p = &x; will result in wrong output Never assign an absolute address to a pointer variable int *count; count = 1268; 32 Whenever you use *p to access the value of the location pointed to by a pointer variable p, always check that p has been assigned a valid value before by an assignment statement ( p = …..) Very common mistake while writing programs with pointers int main() { int *p; *p = 4; printf(“*p = %d\n”, *p); } Run it and see what happens. p is not assigned anything. So whatever the content of p is, when *p is done, it tries to write to that location. So if p contained 1325 (say), it will try to write at memory location with address 1325. This may cause an error (OS does not allow writes to some addresses) or will overwrite whatever that location contained, which may corrupt other variable values. Second case is very hard to debug, as to the compiler 1325 is a free location and can be given to other 33 variables later, which will then overwrite again. Pointer Expressions Like other variables, pointer variables can appear in expressions What are allowed in C? Add an integer to a pointer Subtract an integer from a pointer Subtract one pointer from another (related) If p1 and p2 are both pointers to the same array, then p2 – p1 gives the number of elements between p1 and p2 34 Contd. What are not allowed? Adding two pointers. p1 = p1 + p2; Multiply / divide a pointer in an expression p1 = p2 / 5; p1 = p1 – p2 * 10; 35 Scale Factor We have seen that an integer value can be added to or subtracted from a pointer variable int *p1, *p2; int i, j; : p1 = p1 + 1; p2 = p1 + j; p2++; p2 = p2 – (i + j); In reality, it is not the integer value which is added/subtracted, but rather the scale factor times the value 36 Contd. Data Type char int float double Scale Factor 1 4 4 8 If p1 is an integer pointer, then p1++ will increment the value of p1 by 4 37 The scale factor indicates the number of bytes used to store a value of that type So the address of the next element of that type can only be at the (current pointer value + size of data) The exact scale factor may vary from one machine to another Can be found out using the sizeof function Gives the size of that data type Syntax: sizeof (data_type) 38 int main() { printf (“No. of bytes in int is %u \n”, sizeof(int)); printf (“No. of bytes in float is %u \n”, sizeof(float)); printf (“No. of bytes in double is %u \n”, sizeof(double)); printf (“No. of bytes in char is %u \n”, sizeof(char)); printf (“No. of bytes in int * is %u \n”, printf (“No. of bytes in float * is %u \n”, printf (“No. of bytes in double * is %u \n”, printf (“No. of bytes in char * is %u \n”, return 0; Example sizeof(int *)); sizeof(float *)); sizeof(double *)); sizeof(char *)); } Output on a PC No. of bytes in int is 4 No. of bytes in float is 4 No. of bytes in double is 8 No. of bytes in char is 1 No. of bytes in int * is 4 No. of bytes in float * is 4 No. of bytes in double * is 4 No. of bytes in char * is 4 39 Note that pointer takes 4 bytes to store, independent of the type it points to However, this can vary between machines Output of the same program on a server No. of bytes in int is 4 No. of bytes in float is 4 No. of bytes in double is 8 No. of bytes in char is 1 No. of bytes in int * is 8 No. of bytes in float * is 8 No. of bytes in double * is 8 No. of bytes in char * is 8 Always use sizeof() to get the correct size` Should also print pointers using %p (instead of %u as we have used so far for easy comparison) Example int main() { int A[5], i; printf(“The addresses of the array elements are:\n”); for (i=0; i<5; i++) printf(“&A[%d]: Using \%p = %p, Using \%u = %u”, i, &A[i], &A[i]); return 0; } Output on a server machine &A[0]: Using %p = 0x7fffb2ad5930, &A[1]: Using %p = 0x7fffb2ad5934, &A[2]: Using %p = 0x7fffb2ad5938, &A[3]: Using %p = 0x7fffb2ad593c, &A[4]: Using %p = 0x7fffb2ad5940, Using %u = 2997705008 Using %u = 2997705012 Using %u = 2997705016 Using %u = 2997705020 Using %u = 2997705024 0x7fffb2ad5930 = 140736191093040 in decimal (NOT 2997705008) so print with %u prints a wrong value (4 bytes of unsigned int cannot hold 8 bytes for the pointer value) 41 Pointers: Parameter Passing and Return 42 Passing Pointers to a Function Pointers are often passed to a function as arguments Allows data items within the calling function to be accessed by the called function, altered, and then returned to the calling function in altered form Useful for returning more than one value from a function Still call-by-value, but now the address is copied, not the content 43 Example: Swapping int main() { int a, b; a = 5; b = 20; swap (a, b); printf (“\n a=%d, b=%d”, a, b); return 0; } void swap (int x, int y) { int t; t = x; x = y; y = t; } Output a=5, b=20 Parameters passed by value, so changes done on copy, not returned to calling function 44 Example: Swapping using pointers int main() { int a, b; a = 5; b = 20; swap (&a, &b); printf (“\n a=%d, b=%d”, a, b); return 0; } void swap (int *x, int *y) { int t; t = *x; *x = *y; *y = t; } Output a=20, b=5 Parameters passed by address, changes done on the value stored at that address, correctly swapped 45 While passing a parameter to a function, when should you pass its address instead of the value? Pass address if both these conditions are satisfied The parameter value will be modified inside the function body The modified value is needed in the calling function after the called function returns Consider the swap function to see this 46 Passing Arrays as Pointers Both the forms below are fine in the function body, as arrays are passed by passing the address of the first element. Calling function calls it the same way int main() { int n; float list[100], avg; : avg = average (n, list); : } int main() { int n; float list[100], avg; : avg = average (n, list); : } float average (int a, float x[]) { : sum = sum + x[i]; } float average (int a, float *x) { : sum = sum + x[i]; } 47 Returning multiple values from a function Return statement can return only one value What if we want to return more than one value? Use pointers Return one value as usual with a return statement For other return values, pass the address of a variable in which the value is to be returned 48 Example: Returning max and min of an array Both returned through pointers (could have returned one of them through return value of the function also) int main() { int n, min, max, i, A[100]; scanf(“%d”, &n); for (i=0; i<n; ++i) scanf(“%d”, &A[i]); MinMax(A, n, &min, &max); printf(“Min and max are %d, %d”, min, max); return 0; } void MinMax(int A[], int n, int *min, int *max) { int i, x, y; x = y = A[0]; for (i=1; i<n; ++i) { if (A[i] < x) x = A[i]; if (A[i] > y) y = A[i]; } *min = x; *max = y; } 49 Example: Passing structure pointers struct complex { float re; float im; }; int main() { struct complex a, b, c; scanf(“%f%f”, &a.re, &a.im); scanf(“%f%f”, &b.re, &b.im); add(&a, &b, &c) ; printf(“\n %f %f”, c.re, c.im); return 0; } void add (struct complex *x, struct complex *y, struct complex *t) { t->re = x->re + y->re; t->im = x->im + y->im; } The program will print the sum of a and b correctly. Just try passing a, b, c directly (no pointers in call or in function declaration) and see what happens 50 Strings Lecture 26 51 Strings • • • 1-d arrays of type char By convention, a string in C is terminated by the end-of-string sentinel ‘\0’ (null character) char s[21] - can have variable length string delimited with \0 • • • Max length of the string that can be stored is 20 as the size must include storage needed for the ‘\0’ String constants : “hello”, “abc” “abc” is a character array of size 4 52 String Constant • A string constant is treated as a pointer • Its value is the base address of the string char *p = “abc”; p a b c \0 printf (“%s %s\n”,p,p+1); /* abc bc is printed */ 53 Differences : array & pointers char *p = “abcde”; The compiler allocates space for p, puts the string constant “abcde” in memory somewhere else, initializes p with the base address of the string constant char s[ ] = “abcde”; char s[ ] = {‘a’,’b’,’c’,’d’,’e’.’\0’}; The compiler allocates 6 bytes of memory for the array s which are initialized with the 6 characters p S a b c d e \0 a b c d e \0 54 Library Functions for String Handling You can write your own C code to do different operations on strings like finding the length of a string, copying one string to another, appending one string to the end of another etc. C library provides standard functions for these that you can call, so no need to write your own code To use them, you must do #include <string.h> At the beginning of your program (after #include <stdio.h>) 55 String functions we will see strlen : finds the length of a string strcat : concatenates one string at the end of another strcmp : compares two strings lexicographically strcpy : copies one string to another 56 strlen() You cannot change contents of s in the function int strlen(const char *s) Takes a null-terminated strings (we routinely refer to the char pointer that points to a null-terminated char array as a string) Returns the length of the string, not counting the null (\0) character int strlen (const char *s) { int n; for (n=0; *s!=‘\0’; ++s) ++n; return n; } 57 strcat() char *strcat (char *s1, const char *s2); Takes 2 strings as arguments, concatenates them, and puts the result in s1. Returns s1. Programmer must ensure that s1 points to enough space to hold the result. You cannot change contents of s2 in the function char *strcat(char *s1, const char *s2) { char *p = s1; while (*p != ‘\0’) /* go to end */ ++p; while(*s2 != ‘\0’) *p++ = *s2++; /* copy */ *p = ‘\0’; return s1; } 58 Dissection of the strcat() function char *p = s1; p is being initialized, not *p. The pointer p is initialized to the pointer value s1. Thus p and s1 point to the same memory location 59 Dissection of the strcat() function char *p = s1; p is being initialized, not *p. The pointer p is initialized to the pointer value s1. Thus p and s1 point to the same memory location while (*p != ‘\0’) ++p; As long as the value pointed to by p is not ‘\0’, p is incremented, causing it to point at the next character in the string. When p points to \0, the control exits the while statement 60 Dissection of the strcat() function char *p = s1; p is being initialized, not *p. The pointer p is initialized to the pointer value s1. Thus p and s1 point to the same memory location while (*p != ‘\0’) ++p; As long as the value pointed to by p is not ‘\0’, p is incremented, causing it to point at the next character in the string. When p points to \0, the control exits the while statement while(*s2 != ‘\0’) *p++ = *s2++; /* copy */ At the beginning, p points to the null character at the end of string s1. The characters in s2 get copied one after another until end of s2 61 Dissection of the strcat() function char *p = s1; p is being initialized, not *p. The pointer p is initialized to the pointer value s1. Thus p and s1 point to the same memory location while (*p != ‘\0’) ++p; As long as the value pointed to by p is not ‘\0’, p is incremented, causing it to point at the next character in the string. When p points to \0, the control exits the while statement while(*s2 != ‘\0’) *p++ = *s2++; /* copy */ At the beginning, p points to the null character at the end of string s1. The characters in s2 get copied one after another until end of s2 *p = ‘\0’; put the ‘\0’ at the end of the string 62 strcmp() int strcmp (const char *s1, const char *s2); Two strings are passed as arguments. An integer is returned that is less than, equal to, or greater than 0, depending on whether s1 is lexicographically less than, equal to, or greater than s2. 63 strcmp() int strcmp(char *s1, const char *s2) { for (;*s1!=‘\0’&&*s2!=‘\0’; s1++,s2++) int strcmp (const char { *s1, const char *s2); if (*s1>*s2) return 1; Two strings are passed if (*s2>*s1) return -1; as arguments. An } if (*s1 != ‘\0’) return 1; integer is returned if (*s2 != ‘\0’) return -1; that is less than, return 0; equal to, or greater } than 0, depending on whether s1 is lexicographically less than, equal to, or greater than s2. Important: When you use strcmp() from the string library, check the return value for >, < or = 0, not for +1, -1, and 0 (which are just one possible return value to satisfy the >, <, and = 0 condition 64 strcpy() char *strcpy (char *s1, char *s2); The characters is the string s2 are copied into s1 until \0 is moved. Whatever exists in s1 is overwritten. It is assumed that s1 has enough space to hold the result. The pointer s1 is returned. 65 strcpy() char *strcpy (char *s1, const char *s2); The characters is the string s2 are copied into s1 until ‘\0’ is moved. Whatever exists in s1 is overwritten. It is assumed that s1 has enough space to hold the result. The pointer s1 is returned. char * strcpy (char *s1, const char *s2) { char *p = s1; while (*p++ = *s2++) ; return s1; } 66 Example: Using string functions int main() { char s1[ ] = "beautiful big sky country", s2[ ] = "how now brown cow"; printf("%d\n",strlen (s1)); printf("%d\n",strlen (s2+8)); printf("%d\n", strcmp(s1,s2)); printf("%s\n",s1+10); strcpy(s1+10,s2+8); strcat(s1,"s!"); printf("%s\n", s1); return 0; } Output 25 9 -1 big sky country beautiful brown cows! 67 Practice Problems 1. 2. 3. 4. 5. Write a function to search for an element in an array of integers that returns 1 if the element is found, 0 otherwise. If found, it also returns the index in the array where found Write a function that returns the number of lowercase letters, uppercase letters, and digit characters in a string Define a structure POINT to store the coordinates (integer) of a point in 2d plane. Write a function that returns the two farthest (largest distance) points in an array of POINT structures Write a function that takes two arrays of integers A and B and returns the size of the union set and the size of the intersection set of A and B Write a function that returns the lengths of the largest palindromes formed by any substring (sequence of consecutive characters) of the string. It should also return the index in the string from which the palindrome starts. For all of the above, add suitable main() functions to call the functions. Also, decide on what parameters you will need; for better practice, for all problems other than problems 1, assume that the return type of the function is void. 68 Pointers and Arrays Lecture 27 1 Pointers and Arrays ... 201 202 203 204 205 ... x When an array is declared, int x[100]; ... The compiler allocates sufficient amount of storage to contain all the elements of the array in contiguous memory locations… The base address is the location of the first element (index 0) of the array The compiler also defines the array name as a constant pointer to the first element 2 Example Consider the declaration: int x[5] = {1, 2, 3, 4, 5}; Suppose each integer requires 4 bytes Compiler allocates a contiguous storage of size 5x4 = 20 bytes Assume starting address of that storage x is 2500: Element Value Address x[0] 1 2500 x[1] 2 2504 x x[2] 3 2508 x[3] 4 2512 3 x[4] 5 2516 Pointer arithmetic Array name x is the starting address of the array Both x and &x[0] have the value 2500 2500 x is a constant pointer, so cannot be changed X = 3400, x++, x += 2 are all illegal If int *p is declared, then p = x; and 2504 2508 2512 2516 p = &x[0]; are equivalent We can access successive values of x by using p++ or p-- to move from one element to another 4 x Relationship between p and x: p = &x[0] = 2500 p+1 = &x[1] = 2504 How will you get the value stored at x[i] using p? p+2 = &x[2] = 2508 p+3 = &x[3] = 2512 p+4 = &x[4] = 2516 In general, *(p+i) gives the value of x[i] x C compiler knows the type of each element 2500 in array x: 2504 so knows how many bytes to move the pointer to get to the next element 2508 2512 2516 5 Is Array Name a Pointer? Pointers and arrays are not equivalent in two main ways: Array name is a constant pointer, it cannot be assigned any value. Are the Pointer by itself, does not allocate memory. int arr[10]; int xyz[20]; arr=xyz; int *iptr; 6 iptr=50; 22 February 2022 following statements valid? iptr=xyz; Programming and Data Structure Would following programs work? main(){ main(){ int i, arr[100]; int arr[100]; for(i=0;i<100;i++) print(arr); printf(“%d\n”,*arr++); } } print(int p[]){ int i; for(i=0;i<100;i++) arr p 7 printf(“%d\n”, *p++); } 22 February 2022 Programming and Data Structure Example: function to find average int main(){ avg: Use pointer int x[100], k, n; arithmetic and not array indexing. scanf (“%d”, &n); for (k=0; k<n; k++) scanf (“%d”, &x[k]); printf (“\nAverage is %f”, avg (x, n)); return 0; float avg (int array[], int size){ } int *p, i , sum = 0; p = array; for (i=0; i<size; i++) sum = sum + *(p+i); return ((float) sum / size); } 8 Pointer p can be indexed also --- just like an array! int main() { int x[100], k, n; scanf (“%d”, &n); float avg (int array[], int size) { int *p, i , sum = 0; for (k=0; k<n; k++) scanf (“%d”, &x[k]); p = array; for (i=0; i<size; i++) sum = sum + p[i]; printf (“\nAverage is %f”, avg (x, n)); return 0; } return ((float) sum / size); } 9 Important to remember Pitfall: An array in C does not know its own length, & bounds not checked! Consequence: While traversing the elements of an array (either using [ ] or pointer arithmetic), we can accidentally access off the end of an array (access more elements than what is there in the array) Consequence: We must pass the array and its size to a function which is going to traverse it, or there should be some way of knowing the end based on the values (Ex., a –ve value ending a string of +ve values) Accessing arrays out of bound can cause segmentation faults: Very hard to debug Always be careful when traversing arrays in programs 10 2D Array A[0][0] A[0][1] A[0][2] A[1][0] A[1][1] A[1][2] A[2][0] A[2][1] A[2][2] 11 2D Array A[0][0] A[0][1] A[0][2] A[1][0] A[1][1] A[1][2] A[2][0] A[2][1] A[2][2] In Memory: A[0][0] A[0][1] A[0][2] A[1][0] A[1][1] A[1][2] A[2][0] A[2][1] A[2][2] 12 2D Array A[0] A[0][0] A[0][1] A[0][2] A[1] A[1][0] A[1][1] A[1][2] A[2] A[2][0] A[2][1] A[2][2] In Memory: A[0][0] A[0][1] A[0][2] A[1][0] A[1][1] A[1][2] A[2][0] A[2][1] A[2][2] 13 2D Array A A[0] A[0][0] A[0][1] A[0][2] A[1] A[1][0] A[1][1] A[1][2] A[2] A[2][0] A[2][1] A[2][2] In Memory: A[0][0] A[0][1] A[0][2] A[1][0] A[1][1] A[1][2] A[2][0] A[2][1] A[2][2] 14 2D Array A *(A+0) *(A+1) *(A+2) A[0] A[0][0] A[0][1] A[0][2] A[1] A[1][0] A[1][1] A[1][2] A[2] A[2][0] A[2][1] A[2][2] In Memory: A[0][0] A[0][1] A[0][2] A[1][0] A[1][1] A[1][2] A[2][0] A[2][1] A[2][2] 15 2D Array A *(A+0) *(A+1) *(A+2) A[0] A[0][0] *(*(A+0)+0) A[0][1] *(*(A+0)+1) A[0][2] *(*(A+0)+2) A[1] A[1][0] *(*(A+1)+0) A[1][1] *(*(A+1)+1) A[1][2] *(*(A+1)+2) A[2] A[2][0] *(*(A+2)+0) A[2][1] *(*(A+2)+1) A[2][2] *(*(A+2)+2) In Memory: A[0][0] A[0][1] A[0][2] A[1][0] A[1][1] A[1][2] A[2][0] A[2][1] A[2][2] 16 2D Array In general, A[i][j] = *(*(A+i)+j) A *(A+0) *(A+1) *(A+2) A[0] A[0][0] *(*(A+0)+0) A[0][1] *(*(A+0)+1) A[0][2] *(*(A+0)+2) A[1] A[1][0] *(*(A+1)+0) A[1][1] *(*(A+1)+1) A[1][2] *(*(A+1)+2) A[2] A[2][0] *(*(A+2)+0) A[2][1] *(*(A+2)+1) A[2][2] *(*(A+2)+2) In Memory: A[0][0] A[0][1] A[0][2] A[1][0] A[1][1] A[1][2] A[2][0] A[2][1] A[2][2] 17 2D Array In general, A[i][j] = *(*(A+i)+j) A *(A+0) *(A+1) *(A+2) A[0] A[0][0] *(*(A+0)+0) A[0][1] *(*(A+0)+1) A[0][2] *(*(A+0)+2) A[1] A[1][0] *(*(A+1)+0) A[1][1] *(*(A+1)+1) A[1][2] *(*(A+1)+2) A[2] A[2][0] *(*(A+2)+0) A[2][1] *(*(A+2)+1) A[2][2] *(*(A+2)+2) In Memory: A[0][0] A[0][1] *(*(A+0)+0) A[0][2] *(*(A+0)+2) A[1][0] A[1][1] A[1][2] A[2][0] A[2][1] A[2][2] 18 2D Array In general, A[i][j] = *(*(A+i)+j) A *(A+0) *(A+1) *(A+2) A[0] A[0][0] *(*(A+0)+0) A[0][1] *(*(A+0)+1) A[0][2] *(*(A+0)+2) A[1] A[1][0] *(*(A+1)+0) A[1][1] *(*(A+1)+1) A[1][2] *(*(A+1)+2) A[2] A[2][0] *(*(A+2)+0) A[2][1] *(*(A+2)+1) A[2][2] *(*(A+2)+2) In Memory: A[0][0] A[0][1] *(*(A+0)+0) A[0][2] A[1][0] *(*(A+0)+3) A[1][1] A[1][2] *(*(A+0)+5) A[2][0] A[2][1] A[2][2] *(*(A+0)+7) 19 Pointers to Structures 20 Pointers to Structures Pointer variables can be defined to store the address of structure variables Example: struct student { int roll; char dept_code[25]; float cgpa; }; struct student *p; 21 Just like other pointers, p does not point to anything by itself after declaration Need to assign the address of a structure to p Can use & operator on a struct student type variable Example: struct student x, *p; scanf(“%d%s%f”, &x.roll, x.dept_code, &x.cgpa); p = &x; 22 Once p points to a structure variable, the members can be accessed in one of two ways: (*p).roll, p Note the ( ) around *p –> roll, p –> dept_code, p –> cgpa (*p).dept_code, (*p).cgpa The symbol –> is called the arrow operator Example: printf(“Roll = %d, Dept.= %s, CGPA = %f\n”, (*p).roll, (*p).dept_code, (*p).cgpa); printf(“Roll = %d, Dept.= %s, CGPA = %f\n”, p->roll, p->dept_code, p->cgpa); 23 Pointers and Array of Structures Recall that the name of an array is the address of its 0-th element Also true for the names of arrays of structure variables Consider the declaration: struct student class[100], *ptr ; 24 The name class represents the address of the 0-th element of the structure array ptr is a pointer to data objects of the type struct student The assignment ptr = class; will assign the address of class[0] to ptr Now ptr->roll is the same as class[0].roll. Same for other members When the pointer ptr is incremented by one (ptr++) : The value of ptr is actually increased by sizeof(struct student) It is made to point to the next record Note that sizeof operator can be applied on any data type 25 struct student { char name[20]; int roll; } int main() { struct student class[50], *p; int i, n; scanf(“%d”, &n); for (i=0; i<n; i++) scanf(“%s%d”, class[i].name, &class[i].roll); p = class; for (i=0; i<n; i++) { printf(“%s %d\n”, class[i].name, class[i].roll); printf(“%s %d\n”, *(p+i).name, *(p+i).roll); printf(“%s %d\n”, (p+i)->name, (p+i)->roll); printf(“%s %d\n”, p[i].name, p[i].roll); } } Output 3 Ajit 1001 Abhishek 1005 Riya 1007 Ajit 1001 Ajit 1001 Ajit 1001 Ajit 1001 Abhishek 1005 Abhishek 1005 Abhishek 1005 Abhishek 1005 Riya 1007 Riya 1007 Riya 1007 Riya 1007 26 A Warning When using structure pointers, be careful of operator precedence Member operator “.” has higher precedence than “*” ptr –> roll and (*ptr).roll mean the same thing *ptr.roll will lead to error The operator “–>” enjoys the highest priority among operators ++ptr –> roll will increment ptr->roll, not ptr (++ptr) –> roll will access (ptr + 1)->roll (for example, if you want to print the roll no. of all elements of the class array) When not sure, use ( and ) to force what you you want 27 Example: Complex Number Addition #include <stdio.h> struct complex { struct complex add (struct complex x, struct complex y) { struct complex t; float re; float im; }; main(){ struct complex a, b, c; scanf (“%f %f”, &a.re, &a.im); scanf (“%f %f”, &b.re, &b.im); c = add (a, b) ; printf (“\n %f %f”, c.re, c.im); } 28 t.re = x.re + y.re ; t.im = x.im + y.im ; return (t) ; } 22 February 2022 Programming and Data Structure Example: Alternative Way Using Pointers #include <stdio.h> struct complex { float re; float im; }; void add (struct complex *x, *y, *t) { t->re = x->re + y->re ; t->im = x->im + y->im ; } main(){ struct complex a, b, c; scanf (“%f %f”, &a.re, &a.im); scanf (“%f %f”, &b.re, &b.im); add (&a, &b, &c) ; printf (“\n %f %f”, c.re, c.im); } 29 22 February 2022 Programming and Data Structure Example: Sorting by Roll Numbers #include <stdio.h> struct stud { int roll; char dept_code[25]; float cgpa; }; main() { struct stud class[100], t; int j, k, n; scanf (“%d”, &n); /* no. of students */ 30 for (k=0; k<n; k++) scanf (“%d %s %f”, &class[k].roll, class[k].dept_code, &class[k].cgpa); for (j=0; j<n-1; j++) for (k=j+1; k<n; k++) { if (class[j].roll > class[k].roll) { t = class[j] ; class[j] = class[k] ; class[k] = t } } <<<< PRINT THE RECORDS >>>> } 22 February 2022 Programming and Data Structure Practice Problems Look at all problems you have done earlier on arrays (including arrays of structures). Now rewrite all of them using equivalent pointer notations Example: If you had declared an array int A[50] Now do int A[50], *p; p = A; and then write the rest of the program using the pointer p (without using [ ] notation) 32 Dynamic Memory Allocation Lecture 28 33 sizeof with Pointers to Arrays int array[10]; int *p=array; What is… sizeof(array[3])? 4 Sizeof(p)? 4 sizeof(array)? 40 A Problem with Arrays Sometimes Amount of data cannot be predicted beforehand Number of data items keeps changing during program execution Example: Seach for an element in an array of N elements One solution: find the maximum possible value of N and allocate an array of N elements Wasteful of memory space, as N may be much smaller in some executions Example: maximum value of N may be 10,000, but a particular run may need to search only among 100 elements Using array of size 10,000 always wastes memory in most cases 35 Better Solution Dynamic memory allocation Know how much memory is needed after the program is run Example: ask the user to enter from keyboard Dynamically allocate only the amount of memory needed C provides functions to dynamically allocate memory malloc, calloc, realloc 36 Memory Allocation Functions malloc Allocates requested number of bytes and returns a pointer to the first byte of the allocated space calloc Allocates space for an array of elements, initializes them to zero and then returns a pointer to the memory. free Frees previously allocated space. realloc Modifies the size of previously allocated space. We will only do malloc and free 37 Allocating a Block of Memory A block of memory can be allocated using the function malloc Reserves a block of memory of specified size and returns a pointer of type void The return pointer can be type-casted to any pointer type General format: type *p; p = (type *) malloc (byte_size); 38 Example p = (int *) malloc(100 * sizeof(int)); A memory space equivalent to 100 times the size of an int bytes is reserved The address of the first byte of the allocated memory is assigned to the pointer p of type int p 400 bytes of space 39 Contd. cptr = (char *) malloc (20); Allocates 20 bytes of space for the pointer cptr of type char sptr = (struct stud *) malloc(10*sizeof(struct stud)); Allocates space for a structure array of 10 elements. sptr points to a structure element of type struct stud Always use sizeof operator to find number of bytes for a data type, as it can vary from machine to machine40 Points to Note malloc always allocates a block of contiguous bytes The allocation can fail if sufficient contiguous memory space is not available If it fails, malloc returns NULL if ((p = (int *) malloc(100 * sizeof(int))) == NULL){ printf (“\n Memory cannot be allocated”); exit(); } 41 Using the malloc’d Array Once the memory is allocated, it can be used with pointers, or with array notation Example: int *p, n, i; scanf(“%d”, &n); p = (int *) malloc (n * sizeof(int)); for (i=0; i<n; ++i) scanf(“%d”, &p[i]); The n integers allocated can be accessed as *p, *(p+1), *(p+2),…, *(p+n-1) or just as p[0], p[1], p[2], …,p[n-1] 42 Example #include <stdio.h> printf("Input heights Input the number of students. for %d students \n",N); 5 main(){ for(i=0;i<N;i++) Input heights for 5 students int i,N; scanf("%f",&height[i]); 23 24 25 26 27 float *height; for(i=0;i<N;i++) Average height= 25.000000 float sum=0,avg; sum+=height[i]; printf("Input the number of avg=sum/(float) N; students. \n"); printf("Average height= scanf("%d",&N); %f \n",avg); height=(float *) malloc(N * } sizeof(float)); 43 22 February 2022 Programming and Data Structure Releasing the Allocated Space: free An allocated block can be returned to the system for future use by using the free function General syntax: free (ptr); where ptr is a pointer to a memory block which has been previously created using malloc Note that no size needs to be mentioned for the allocated block, the system remembers it for each pointer returned 44 Can we allocate only arrays? malloc can be used to allocate memory for single variables also p = (int *) malloc (sizeof(int)); Allocates space for a single int, which can be accessed as *p Single variable allocations are just special case of array allocations Array with only one element 45 malloc( )-ing array of structures typedef struct{ char name[20]; int roll; float SGPA[8], CGPA; } person; int main() { person *student; int i,j,n; scanf("%d", &n); student = (person *)malloc(n*sizeof(person)); for (i=0; i<n; i++) { scanf("%s", student[i].name); scanf("%d", &student[i].roll); for(j=0;j<8;j++) scanf("%f", &student[i].SGPA[j]); scanf("%f", &student[i].CGPA); } return 0; } 46 Static array of pointers #define N 20 #define M 10 int main() ... { char word[N], *w[M]; int i, n; scanf("%d",&n); for (i=0; i<n; ++i) { scanf("%s", word); w[i] = (char *) malloc ((strlen(word)+1)*sizeof(char)); strcpy (w[i], word) ; } for (i=0; i<n; i++) printf("w[%d] = %s \n",i,w[i]); return 0; } w 47 Static array of pointers Output #define N 20 #define M 10 int main() { char word[N], *w[M]; int i, n; scanf("%d",&n); for (i=0; i<n; ++i) { scanf("%s", word); w[i] = (char *) malloc ((strlen(word)+1)*sizeof(char)); strcpy (w[i], word) ; } for (i=0; i<n; i++) printf("w[%d] = %s \n",i,w[i]); return 0; } 4 Tendulkar Sourav Khan India w[0] = Tendulkar w[1] = Sourav w[2] = Khan w[3] = India 48 How it will look like w 0 1 2 3 T e n d u l S o u r a v \0 K h a n \0 I d i n k a r \0 a \0 9 49 Pointers to Pointers Pointers are also variables (storing addresses), so they have a memory location, so they also have an address Pointer to pointer – stores the address of a pointer variable w int x = 10, *p, **q; p = &x; q = &p; printf(“%d %d %d”, x, *p, *(*q)); 0 1 2 3 will print 10 10 10 (since *q = p) 9 50 Allocating Pointer to Pointer int **p; p = (int **) malloc(3 * sizeof(int *)); p[0] p int ** int * p[1] int * p[2] int * 51 Dynamic Arrays of pointers int main() { char word[20], **w; /* “**w” is a pointer to a pointer array */ int i, n; scanf("%d",&n); w = (char **) malloc (n * sizeof(char *)); for (i=0; i<n; ++i) { scanf("%s", word); w[i] = (char *) malloc ((strlen(word)+1)*sizeof(char)); strcpy (w[i], word) ; } for (i=0; i<n; i++) printf("w[%d] = %s \n",i, w[i]); return 0; } 52 Dynamic Arrays of pointers Output int main() 5 { char word[20], **w; /* “**w” is a pointer to a pointer array */ India Australia int i, n; Kenya scanf("%d",&n); NewZealand w = (char **) malloc (n * sizeof(char *)); SriLanka for (i=0; i<n; ++i) { w[0] = India scanf("%s", word); w[1] = Australia w[i] = (char *) malloc ((strlen(word)+1)*sizeof(char)); w[2] = Kenya strcpy (w[i], word) ; w[3] = NewZealand } w[4] = SriLanka for (i=0; i<n; i++) printf("w[%d] = %s \n",i, w[i]); return 0; } 53 How this will look like w 0 1 2 3 4 I n d i a \0 A u s t r K e n y a \0 N e w Z e S r a i L a l I a \0 a l a n n k a \0 d \0 54 Dynamic Allocation of 2-d Arrays int **allocate (int h, int w) { int **p; Allocate array int i, j; of pointers p = (int **) malloc(h*sizeof (int *) ); for (i=0;i<h;i++) p[i] = (int *) malloc(w * sizeof (int)); return(p); } Allocate array of integers for each row void read_data (int **p, int h, int w) { int i, j; for (i=0;i<h;i++) for (j=0;j<w;j++) scanf ("%d", &p[i][j]); } Elements accessed like 2-D array elements. 55 Arrays and Pointers: Exercise int foo(int array[], unsigned int size){ … printf(“%d\n”, sizeof(array)); } int main(){ int a[10], b[5]; … foo(a, 10)… foo(b, 5) … printf(“%d\n”, sizeof(a)); } What does this print? 8 ... because array is really a pointer What does this print? 40 Contd. void print_data (int **p, int h, int w) { int i, j; for (i=0;i<h;i++) { for (j=0;j<w;j++) printf ("%5d ", p[i][j]); printf ("\n"); } } int main() { int **p; int M, N; printf ("Give M and N \n"); scanf ("%d%d", &M, &N); p = allocate (M, N); read_data (p, M, N); printf ("\nThe array read as \n"); print_data (p, M, N); return 0; } 57 Contd. void print_data (int **p, int h, int w) { int i, j; for (i=0;i<h;i++) { for (j=0;j<w;j++) Give M and N printf ("%5d ", p[i][j]); 33 printf ("\n"); 123 } 456 } 789 The array read as 1 2 3 4 5 6 7 8 9 int main() { int **p; int M, N; printf ("Give M and N \n"); scanf ("%d%d", &M, &N); p = allocate (M, N); read_data (p, M, N); printf ("\nThe array read as \n"); print_data (p, M, N); return 0; } 58 Memory Layout in Dynamic Allocation int main() { int **p; int M, N, i, j; printf ("Give M and N \n"); scanf ("%d%d", &M, &N); p = allocate (M, N); for (i=0;i<M;i++) { for (j=0;j<N;j++) printf ("%u", &p[i][j]); printf(“\n”); } return 0; } int **allocate (int h, int w) { int **p; int i, j; p = (int **)malloc(h*sizeof (int *)); for (i=0; i<h; i++) printf(“%u”, &p[i]); printf(“\n\n”); for (i=0;i<h;i++) p[i] = (int *)malloc(w*sizeof(int)); return(p); } 59 Output 33 31535120 31535128 31535136 31535152 31535156 31535160 31535184 31535188 31535192 31535216 31535220 31535224 Starting address of each row, contiguous (pointers are 8 bytes long) Elements in each row are contiguous 60 Practice Problems Take any of the problems you have done so far using 1-d arrays or 2-d arrays. Now do them by allocating the arrays dynamically first instead of declaring then statically 61 Linked List 1 List A list refers to a sequence of data items Example: An array The array index is used for accessing and manipulation of array elements Problems with arrays The array size has to be specified at the beginning (at least during dynamic allocation) realloc can be used to readjust size in middle, but contiguous chunk of memory may not be available Deleting an element or inserting an element may require shifting of elements Wasteful of space 2 Linked List A completely different way to represent a list Make each data in the list part of a structure The structure also contains a pointer or link to the structure (of the same type) containing the next data This type of list is called a linked list Structure 1 data Structure 2 data Structure 3 data 3 Forming a linked list Let each structure of the list (lets call it node) have two fields: One containing the data The other containing the address of the structure holding the next data in the list The structures in the linked list need not be contiguous in memory They are ordered by logical links that are stored as part of the data in the structure itself The link is a pointer to another structure of the same type 4 Contd. struct node { int data; struct node *next; } data next The pointer variable next contains either the address of the location in memory of the successor list element or the special value NULL defined as 0 node NULL is used to denote the end of the list (no successor element) Such structures which contain a member field pointing to the same structure type are called selfreferential structures 5 Example: nodes of the list struct node a, b, c; a.data = 1; b.data = 2; c.data = 3; a.next = b.next = c.next = NULL; a b 1 NULL data next c 2 NULL data next 3 NULL data next 6 Chaining these together a.next = &b; b.next = &c; a b 1 data next c 2 data next 3 NULL data next What are the values of : • a.next->data • a.next->next->data 7 Chaining these together a.next = &b; b.next = &c; a b 1 data next c 2 3 data next What are the values of : • a.next->data • a.next->next->data NULL data next 2 3 8 Linked Lists A singly linked list is a data structure consisting of a sequence of nodes Each node stores data link to the next node next data node NULL 9 Contd. A head pointer addresses the first element of the list Each element points at a successor element The last element has a link value NULL head NULL 10 Contd. In general, a node of the linked list may be represented as Name of the type of nodes struct node_name { Data items in each type member1; element of the list type member2; ……… Link to the next struct node_name *next; element in the list }; 11 Example: list of student records Structure for each node struct stud { int roll; char name[30]; int age; struct stud *next; }; Suppose the list has three students’ records Declare three nodes n1, n2, and n3 struct stud n1, n2, n3; 12 Contd. Create the links between the nodes n1.next = &n2 ; n2.next = &n3 ; n3.next = NULL ; /* No more nodes follow */ The final list looks like roll name age next n1 n2 n3 13 Code for the Example #include <stdio.h> struct stud { int roll; char name[30]; int age; struct stud *next; }; int main() { struct stud struct stud scanf (“%d %s scanf (“%d %s scanf (“%d %s n1, n2, n3; *p; %d”, &n1.roll, n1.name, &n1.age); %d”, &n2.roll, n2.name, &n2.age); %d”, &n3.roll, n3.name, &n3.age); 14 n1.next n2.next n3.next = = = &n2 ; &n3 ; NULL ; /* Now traverse the list and print the elements */ p = &n1 ; /* point to 1st element */ while (p != NULL) { printf (“\n %d %s %d”, p->roll, p->name, p->age); p = p->next; } return 0; } 15 15 Alternative Way Instead of statically declaring the structures n1, n2, n3, Dynamically allocate space for the nodes Use malloc individually for every node allocated This is the usual way to work with linked lists, as number of elements in the list is usually not known in advance (if known, we could have used arrays) See examples next 16 16 Example of dynamic node allocation Storing a set of elements = {15,18,12,7} 15 18 struct node { int data ; struct node * next ; }; struct node *p, *q; 12 7 NULL data int next node * 17 Adding 15 and 18 only struct node { int data ; struct node * next ; }; struct node *p, *q; p = (struct node *) malloc(sizeof(struct node)); p->data=15; 15 p q = (struct node *) malloc(sizeof(struct node)); q->data=18; q->next = NULL; p 15 18 NULL q p->next = q; p 15 18 q NULL 18 Traversing the elements added struct node { int data; struct node * next; }; int main() { struct node *p,*q,*r; p = (struct node *) malloc(sizeof(struct node)); : r=p; while(r!=NULL){ printf("Data = %d \n",r->data); r=r->next; } return 0; } Output Data = 15 Data = 18 We could have done anything else other than printing as we traverse each element, like searching for example. Just like traversing an array. 19 Contd. We assumed two elements in the list, so took two pointers p and q What if the number of elements are not known? Precisely the reason we use linked list Solution: Remember the address of the first element in a special pointer (the head pointer), make sure to not overwrite it Any other pointer can be reused 20 Example: adding n elements read from keyboard int main() { int n, i; struct node *head = NULL, *p, *prev; scanf(“%d”, &n); for (i = 0; i < n; ++i) { p = (struct node *) malloc(sizeof(struct node)); scanf(“%d”, &p->data); p->next = NULL; if (head == NULL) head = p; else prev->next = p; prev = p; } return 0; } head changes only once, when the first element is added prev remembers the pointer to the last element added, p is linked to its next field p and prev are reused as many times as needed 21 Example: printing an arbitrary sized list int main() { int n, i; struct node *head = NULL, *p; : p = head; while (p != NULL) { printf(“%d “, p->data); p = p->next; } return 0; } Assumed that the list is already created and head points to the first element in the list p is reused to point to the elements in the list (initially, to the first element) When p points to the last element, p>next = NULL, so the loop terminates after this iteration 22 Important to remember Store the address of the first element added in a separate pointer (head in our examples), and make sure not to change it If you lose the start pointer, you cannot access any element in the list, as elements are only accessible from the next pointers in the previous element In the print example, we could have reused head, (head=head->next instead of p=p->next) as we do not need to remember the start pointer after printing the list, but this is considered bad practice, so we used a separate temporary pointer p 23 Function to print a list The pointer to the start of the list (head pointer) is passed as parameter void display (struct node *r) { struct node *p = r; printf(“List = {”); while(p != NULL) { printf("%d, ", p->data); p = p->next; } printf(“}\n”); } 24 Common Operations on Linked Lists Creating a linked list (already seen) Printing a linked list (already seen) Search for an element in a linked list (can be easily done by traversing) Inserting an element in a linked list Insert at front of list Insert at end of list Insert in sorted order Delete an element from a linked list 25 Search for an element Takes the head pointer as parameter struct node *search (struct node *r, int value) { struct node *p; Traverses the list and p = r; compares while (p!=NULL){ value with if (p->data == value) return p; each data p = p->next; Returns the } node with the return p; value if found, NULL } otherwise 26 Insertion in a list To insert a data item into a linked list involves creating a new node containing the data finding the correct place in the list, and linking in the new node at this place Correct place may vary depending on what is needed Front of list End of list Keep the list … in sorted order 27 Takes the head pointer and the value to be inserted (NULL if list is empty) Inserts the value as the first element of the list Returns the new head pointer value Insert in front of list struct node *insert(struct node *r, int value) { struct node *p; p = (struct node *) malloc(sizeof(struct node)); p->data = value; p ->next = r; return p; } 28 Contd. 18 … 3 r 15 18 p … 3 r 15 18 p 3 … r 29 Using the Insert Function void display (struct node *); struct node * insert(struct node * , int); int main() { struct node *head; head = NULL; head = insert(head, 10); display(head); head = insert(head, 11); display(head); head = insert(head, 12); display(head); return 0; } Output List = {10, } List = {11, 10, } List = {12, 11, 10, } 30 Takes the head pointer and the value to be inserted (NULL if list is empty) Inserts the value as the last element of the list Returns the new head pointer value Insert at end struct node *insert_end(struct node *r, int value) { struct node *p,*q; p = (struct node *) malloc(sizeof(struct node)); p->data = value; p ->next = NULL; if (r==NULL) return p; /* list passed is empty */ q=r; while (q->next!=NULL) q=q->next; /* find the last element */ q->next =p; return r; } 31 Contd. … 11 r 4 NULL q p … 11 r 4 15 NULL q NULL p … 11 r 4 15 q NULL 32 Using the Insert at End Function void display (struct node *); struct node * insert(struct node * , int); struct node * insert_end(struct node * , int); int main() { struct node *start; start = NULL; start = insert_end(start, 10); display(start); start = insert_end(start, 11); display(start); start = insert_end(start, 12); display(start); return 0; } Output List = {10, } List = {10, 11, } List = {10, 11, 12, } 33 Insertion in Ascending Order first prev 3 5 new ptr 8 12 - 7 Create new node for the 7 Find correct place – when ptr finds the 8 (7 < 8) Link in new node with previous (even if last) and ptr nodes Also check insertion before first node! 34 Insert in ascending order & sort struct node * insert_asc(struct node * r, int value) { struct node *p, *q, *new; new = (struct node *) malloc(sizeof(struct node)); new->data = value; new ->next = NULL; p = r; q = p; while(p!=NULL) { if (p->data >= value) { /* insert before */ if (p==r) { new->next =r; /* insert at start */ return new; } new->next = p; /* insert before p */ q->next = new; return r; } q = p; p = p->next; } /* exists loop if > largest */ if (r==NULL) return new; /* first time */ else q->next = new; /* insert at end */ return r; } int main() { struct node *start; int i,n,value; start = NULL; scanf("%d",&n); for(i=0; i<n; i++) { printf("Give Data: " ); scanf("%d",&value); start = insert_asc(start, value); } display(start); return 0; } 35 Deletion from a list To delete a data item from a linked list Find the data item in the list, and if found Delink this node from the list Free up the malloc’ed node space 36 Example of Deletion first 3 prev 5 ptr 8 12 - When ptr finds the item to be deleted, e.g. 8, we need the previous node to make the link to the next one after ptr (i.e. ptr -> next) Also check whether first node is to be deleted 37 Deleting an element struct node * delete(struct node * r, int value) { struct node *p, *q; p =r; 11 q = p; q while(p!=NULL) { if (p->data == value){ if (p==r) r = p->next; else q->next = p->next; p->next = NULL; 11 free(p); q return r; } else { q = p; p = p->next; 11 } q } return r; } … … … delete(r,4) 4 15 … 15 … 15 … p 4 p NULL 38 Practice Problems Print a list backwards (try a recursive print) Count the number of elements in a list (both using and not using recursion) Concatenate two lists Reverse a list Delete the maximum element from a list For each of the above, first create the linked list by reading in integers from the keyboard and inserting one by one to an empty list 39 Stack and Queue 1 Stack Data structure with Last-In First-Out (LIFO) behavior In C B A Out B C 2 Typical Operations on Stack Pop Push isempty: determines if the stack has no elements isfull: determines if the stack is full in case of a bounded sized stack top: returns the top element in the stack push: inserts an element into the stack pop: removes the top element from the stack push is like inserting at the front of the list pop is like deleting from the front of the list 3 Creating and Initializing a Stack Declaration #define MAX_STACK_SIZE 100 typedef struct { int key; /* just an example, can have any type of fields depending on what is to be stored */ } element; typedef struct { element list[MAX_STACK_SIZE]; int top; /* index of the topmost element */ } stack; Create and Initialize stack Z; Z.top = -1; 4 Operations int isfull (stack *s) { if (s->top >= MAX_STACK_SIZE – 1) return 1; return 0; } int isempty (stack *s) { if (s->top == -1) return 1; return 0; } 5 Operations element top( stack *s ) { return s->list[s->top]; } void push( stack *s, element e ) { (s->top)++; s->list[s->top] = e; } void pop( stack *s ) { (s->top)--; } 6 Application: Parenthesis Matching Given a parenthesized expression, test whether the expression is properly parenthesized Examples: ( )( { } [ ( { } { } ( ) ) ] ) is proper ( ){ [ ] is not proper ({)} is not proper )([ ] is not proper ([])) is not proper 7 Approach: Whenever a left parenthesis is encountered, it is pushed in the stack Whenever a right parenthesis is encountered, pop from stack and check if the parentheses match Works for multiple types of parentheses ( ), { }, [ ] 8 Parenthesis matching while (not end of string) do { a = get_next_token(); if (a is ‘(‘ or ‘{‘ or ‘[‘) push (a); if (a is ‘)’ or ‘}’ or ‘]’) { if (is_stack_empty( )) { print (“Not well formed”); exit(); } x = top(); pop(); if (a and x do not match) { print (“Not well formed”); exit(); } } } if (not is_stack_empty( )) print (“Not well formed”); 9 Recursion can be implemented as a stack Fibonacci recurrence: fib(n) = 1 if n =0 or 1; = fib(n – 2) + fib(n – 1) otherwise; fib (5) fib (3) fib (1) fib (0) fib (4) fib (2) fib (1) fib (2) fib (0) fib (3) fib (1) fib (1) fib (0) fib (2) fib (1) 10 Fibonacci Recursion Stack 5 3 4 1 2 4 0 0 0 1 3 4 3 1 2 5 5 2 4 0 1 4 1 4 1 1 2 2 0 1 1 6 6 7 4 2 3 0 1 3 3 3 3 8 11 Stack and Queue 1 Queue Data structure with First-In First-Out (FIFO) behavior Out In C B A B A 2 Typical Operations on Queue REAR Enqueue isempty: determines if the queue is empty isfull: determines if the queue is full in case of a bounded size queue front: returns the element at front of the queue enqueue: inserts an element at the rear dequeue: removes the element in front Dequeue FRONT 3 Possible Implementations Linear Arrays: (static/dynamicaly allocated) front Circular Arrays: (static/dynamically allocated) rear front rear Linked Lists: Use a linear linked list with insert_rear and delete_front operations Can be implemented by a 1-d array using modulus operations 4 Circular Queue [3] [4] [2] [5] [1] [6] [0] [7] front=0 rear=0 5 Circular Queue [4] [3] [3] [4] C [5] [2] [1] [6] [0] [7] [2] [1] D [5] B A front=0 [0] rear = 4 [6] After insertion of A, B, C, D [7] front=0 rear=0 6 Circular Queue [4] [3] [3] [4] C [2] [5] [2] [1] [1] [6] [0] [7] D rear = 4 [5] B A [6] front=0 [0] After insertion of A, B, C, D [7] front=0 rear=0 [4] [3] front=2 C D rear = 4 [5] [2] [1] [6] [0] [7] After deletion of of A, B 7 front: index of queue-head (always empty) rear: index of last element, unless rear = front [3] rear = 3 [4] [3] [2] [5] [1] [6] [0] front=0 rear=0 [7] Queue Empty front=4 [4] [2] [5] [1] [6] [0] [7] Queue Full Queue Empty Condition: front == rear Queue Full Condition: front == (rear + 1) % MAX_Q_SIZE 8 Creating and Initializing a Circular Queue Declaration #define MAX_Q_SIZE 100 typedef struct { int key; /* just an example, can have any type of fields depending on what is to be stored */ } element; typedef struct { element list[MAX_Q_SIZE]; int front, rear; } queue; Create and Initialize queue Q; Q.front = 0; Q.rear = 0; 9 Operations int isfull (queue *q) { if (q->front == ((q->rear + 1) % MAX_Q_SIZE)) return 1; return 0; int isempty (queue *q) } { if (q->front == q->rear) return 1; return 0; } 10 Operations element front( queue *q ) { return q->list[(q->front + 1) % MAX_Q_SIZE]; } void enqueue( queue *q, element e) { q->rear = (q->rear + 1)% void dequeue( queue *q ) MAX_Q_SIZE; { q-> front = q->list[q->rear] = e; (q-> front + 1)% } MAX_Q_SIZE; } 11 Practice Problems • Implement the Queue as a linked list. • Implement a Priority Queue which maintains the items in an order (ascending/ descending) and has additional functions like remove_max and remove_min • Maintain a Doctor’s appointment list 12