CS 265 Final Exam Review 1 Final Exam • The Final Exam is scheduled for Monday, June 10, 2024, 10:3012:30, in 3675MK room 1054-1055. • The exam will be given online via Bb Learn. You must be physically present in class to take the exam. • Format of the exam will be similar to the midterm. 2 Final Exam Topics • • • • • • • • C Fundamentals Pointers Strings and Arrays Command line arguments Dynamic memory Declarations Structures, Unions, and Enumerations Linked Lists 3 C vs other programming languages • C is a compiled language that compiles to machine code • C is a procedural language • C is a low-level programming language – Closer to the HWD and the OS 4 C Compiler - Under the hood There are four steps: 1. Pre-processing 2. Compiling - translating code to assembly code 3. Assembling – translating assembly code to machine code 4. Linking 5 Compiling vs Linking a1.c machine code but not an executable #include <stdio.h> int main { user_function1 (); user_function2(); } compile a1.o machine code complete executable a2.c int user_function1() { … } compile a2.o compile a3.o link a3.c int user_function2() { … } /usr/include/stdio.h … printf() … Header file for precompiled library /usr/lib/libc.a 6 a.out char vs int Data Types in C designed to hold a single ASCII char, not Unicode a char can hold 256 ( 28 ) different numbers (there are some variations based on OS and compiler) 7 Assignment An assignment is both a statement and an operator that evaluates to the value of the assignment • This assigns 3 to a and, in addition, it evaluates to 3 a = 3 • This assignment sets all variables to 0 and evaluates to 0 a = b = c = 0 • It evaluates from right to left. It is equivalent to a = (b = (c = 0)) • This is valid C if (a=b=c=0) {..} • The semantics of the assignment are different from Java / Python 8 Weakly-typed and statically-typed counts digits, white space, others main() { int c, i, nwhite, nother; int ndigit[10]; nwhite = nother = 0; while ((c = getchar()) != EOF) { if (c >= ′0′ && c <= ′9′) ++ndigit[c-′0′]; else if (c == ′ ′ ¦¦ c == ′\n′ ¦¦ c == ′\t′) ++nwhite; else ++nother; } Statically typed • You must declare variables before using them (unlike Python) • You must declare a datatype for them (unlike Python) Weakly typed • C is not too particular about comparing the values of different data types (unlike Java/Python) 9 Arrays • Define an array of 10 integers int a[10]; • Can be initialized at declaration int a[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9} ; int a[] = { 121, 17, 2, 88, -273 } ; • Access the array using indexes 0,… 9 a[0], a[1], … a[9] 10 Strings • Strings are arrays of characters ending with ‘\0’ (null) Define a string as an array using characters char name[10] = {‘d’, ‘i’, ‘m’, ‘i’, ‘t’, ‘r’, ‘a’, ‘\0’}; • • or, define a string array using double quotes “” char name[10] = “Dimitra”; • Access the array elements using indexes 0,… 9, e.g., name[0], name[1], … name[9] 11 Functions • Functions are defined as return-type function-name(parameter declarations, if any) { declarations statements } • • • Return types can be any data type or void (no return value) In C, there are two ways to pass parameters Call by value – Any changes made to a parameter inside a function are gone when the function completes • Call by reference – Any changes made to a parameter inside a function persist even after the function completes 12 Character data type • • When a character appears in a computation, C uses its integer value. Consider the following examples, which assume the ASCII character set: char ch; int i; i = 'a'; ch = 65; ch = ch + 1; ch++; /* i is now 97 */ /* ch is now 'A' */ /* ch is now 'B' */ /* ch is now 'C' */ 13 Precedence and Associativity of Operators This table lists the precedence and associativity of C operators. Operators are listed top to bottom, in descending precedence. 14 https://en.cppreference.com/w/c/language/operator_precedence Compound Assignment • • • • The compound assignment operators (e.g., +=) are widely used in C programs. When using the compound assignment operators, be careful not to switch the two characters that make up the operator. Although i =+ j will compile, it is equivalent to i = (+j), which merely copies the value of j into i. v += e isn’t always “equivalent” to v = v + e due to operator precedence. For example: i *= j + k isn’t the same as i = i * j + k. Be careful when using += not to confuse it with =+ 15 Increment and Decrement Operators • Two of the most common operations on a variable are “incrementing” (adding 1) and “decrementing” (subtracting 1): i = i + 1; j = j - 1; • • C provides special ++ (increment) and -- (decrement) operators. The ++ operator adds 1 to its operand. The -- operator subtracts 1. • The increment and decrement operators are tricky to use: – They can be used as prefix operators (++i and –-i) or postfix operators (i++ and i--). – They have side effects: they modify the values of their operands. 16 Increment and Decrement Operators • • Evaluating the expression ++i (a “pre-increment”) yields i + 1 and—as a side effect—increments i: i = 1; printf("i is %d\n", ++i); /* prints "i is 2" */ printf("i is %d\n", i); /* prints "i is 2" */ Evaluating the expression i++ (a “post-increment”) produces the result i, but causes i to be incremented afterwards: i = 1; printf("i is %d\n", i++); /* prints "i is 1" */ printf("i is %d\n", i); /* prints "i is 2" */ 17 Increment and Decrement Operators • ++i means “increment i immediately,” while i++ means “use the old value of i for now, but increment i later.” • How much later? The C standard doesn’t specify a precise time, but it’s safe to assume that i will be incremented before the next statement is executed. 18 If statement • Typical form if (expression) { statements1 } else { statements2 } • Ternary form expr1 ? expr2 : expr3 Instead of if (a < b) { c = a; } else { c = b; } Use c = (a < b) ? a : b; 19 Switch statement switch (expression) { case const-expr : statements; break; /* optional */ case const-expr : statements; break; /* optional */ default: statements; } 20 For loop for (expr1; expr2; statement expr3) Valid for loops in C: int i; for (i=0; i< 10; i++) { …} for( ; ; ) { printf("This loop will run forever.\n"); } for (int i=0; i< 10; i++) { …} 21 While Loops while (expression) { statements; } do statements; while (expression); 22 Loop Control Statements break • Terminates the loop or switch statement and transfers execution to the statement immediately following the loop or switch. continue: • Causes the loop to skip the remainder of its body and immediately retest its condition prior to reiterating. goto • Transfers control to the labeled statement. if (expression) { goto error; } … error: printf(“This is an error”); 23 Pointer Variables • • Addresses can be stored in special pointer variables When we store the address of a variable i in the pointer variable p, we say that p “points to” i. 24 Address and Redirection Operators *p unary operator * is the value of what the pointer p points to &i unary operator & is the address of variable i 25 Pointers – how to declare Variables int *p; char *p; void *p; a pointer to a memory location for an integer a pointer to a memory location for a char a generic pointer i.e., a pointer to any location i.e., a pointer to an undefined data type Note int *p; *p as a declaration it is a mnemonic for pointer it says that *p (where p points to) is an int anywhere else *p it is the value of where p points to 26 Pointer arithmetic The value of pointers are memory addresses, thus numbers, so we can do arithmetic. There are four arithmetic operators that can be used in pointers ++ -+ - • Move the pointer to the next object (move one byte, if char *p, two bytes if short *p) p = p + 1; • or p++; Advance the pointer by 4 “objects” p = p + 4; 27 Functions – Call by value swap void swap(int x, int y) { int temp; /* WRONG */ temp = x; x = y; y = temp; } Before calling swap(x,y) the values are x=1 y=2 After calling swap(x,y) the values are x=1, y=2 28 Functions – call by reference swap void swap(int *px, int *py) { int temp; /* interchange *px and *py */ temp = *px; *px = *py; *py = temp; } Before calling swap(&x,&y) the values are x=1 y=2 After calling swap(&x,&y) the values are x=2 y=1 29 Pointers and Arrays • • • In C there is a strong relationship between pointers and arrays Any operation that involves array subscripting can use pointers The declaration int a[10] defines and array a • • a[0] a[1] a[2] a[9] If we declare a pointer that points to the beginning of the array int *p; p = &a[0]; Then a[i] is the same as *(p+i) and &a[i] is the same as p+i a a[0] a[1] a[2] a[9] The name of an array is a pointer! p p+1 (p+1 points to the next object, not next byte) 30 Strings • Strings can be defined as character arrays char date[10]; char date[] = "June 14"; • Strings can be defined as pointers char *date; char *date = "June 14"; Thanks to the close relationship between arrays and pointers, either version can be used as a string 31 Command Line Arguments • When main has command line arguments it is defined using int main(int argc, char *argv[]) { … } where argc argv is the “argument count”, the number of arguments is the “argument vector”, an array of pointers to the argv[0] arguments (stored as strings) points to the name of the program argv[1] argv[argc-1] argv[argc] point to the remaining arguments is always a null pointer 32 Memory Allocation Functions <stdlib.h> void *malloc(size_t size) Allocates a block of memory but doesn’t initialize it void *calloc(size_t nmemb, size_t size); Allocates a block of memory and clears it void *realloc(void *ptr, size_t size); Resizes a previously allocated block of memory void free(void *ptr); Releases the memory pointed to by the pointer ptr 33 Function Stack Call void f(int a, int b) { int x; } void main() { f(1,2); printf("hello world"); } • • • • • • • main function calls f() with arguments a and b f function stack frame is created below the main function frame The arguments to f are stored in reverse order (first b then a) The returns address in the f() stack frame points to the next instruction address after the function is executed and returned Here, it points to the printf() instruction Every function frame has a pointer to the previous frame The local variable x is stored in the frame 34 C Structures A struct (structure) is a collection of one or more variables, possibly of different types, grouped together under a single name for convenient handling • A structure is a collection of related data • A struct is an aggregate data type, allowing related data elements to be accessed and assigned as a unit. • It defines a user-defined data type • It is similar to a table in a database • It is a foundational component that allows you to build more complex data structures, like linked lists, stacks, trees, etc. 35 Defining Structure Variables • • You can define a variable and its structure at the same time Here is a declaration of two variables part1 and part2 as structures: struct { int number; char name[NAME_LEN+1]; int on_hand; } part1, part2; 36 How are structures stored in memory • The members of a structure are stored in memory in the order in which they’re declared • Appearance of part1 • Assumptions: – part1 is located at address 2000 – Integers occupy four bytes – NAME_LEN has the value 25 – There are no gaps between the members – There is no null character at the end – There is no null pointer at the end 37 Operators on Structures – The . operator struct { int number; char name[NAME_LEN+1]; int on_hand; } part1; • To access the members, we use the . operator part1.number part1.name part1.on_hand 38 Structures can be Nested • • Nesting one structure inside another is often useful Suppose that person_name is the following structure: struct person_name { char first[FIRST_NAME_LEN+1]; char middle_initial; char last[LAST_NAME_LEN+1]; }; • • We can define a student as struct student { struct person_name name; int id, age; } student1; Accessing student1’s first name requires two applications of the . operator: strcpy(student1.name.first, "Fred"); student1.name = {“Fred”, ’X’, “Astaire”}; 39 Pointers to Structures struct student s; struct student *p = &s; • Pointers to structures are so frequently used that an alternative notation is provided as a shorthand. If p is a pointer to a structure, then p->x • is equivalent to the expression (*p).x 40 Unions • • • A union, like a structure, consists of one or more members, possibly of different types The compiler allocates only enough space for the largest of the members, which overlay each other within this space Assigning a new value to one member alters the values of the other members as well 41 Enumerations • • Enumerated data types are possible with the enum keyword. They are freely interconvertible with integers. Examples: deck of cards enum {CLUBS, DIAMONDS, HEARTS, SPADES} s1, s2; • Can be defined with an enumeration tag enum suit {CLUBS, DIAMONDS, HEARTS, SPADES}; enum suit s1, s2; • Or with a typedef typedef enum {CLUBS, DIAMONDS, HEARTS, SPADES} Suit; Suit s1, s2; 42 enum #include <stdio.h> enum day {SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY}; int main() { enum day d = TUESDAY; printf("The day number stored in d is %d", d); return 0; } • Output: The day number stored in d is 2 43 Linked Lists • A linked list consists of a chain of structures (called nodes), with each node containing a pointer to the next node in the chain: NULL pointer vs arrays • More flexible • More dynamic • Not as fast 44 Defining a Node Type • To create a linked list • First, we need to define a structure that defines a single node struct node { int value; /* data stored in the node */ struct node *next; /* pointer to the next node */ }; 45 Inserting a Node at the Beginning of a Linked List • A function that inserts a node containing n into a linked list, which is pointed to by list: struct node *add_to_list(struct node *list, int n) { struct node *new_node; } new_node = malloc(sizeof(struct node)); if (new_node == NULL) { printf("Error: malloc failed in add_to_list\n"); exit(EXIT_FAILURE); } new_node->value = n; new_node->next = list; return new_node; 46 Searching a Linked List • struct node *search_list(struct node *list, int n) { for (; list != NULL && list->value != n; list = list->next) ; return list; } Since list is NULL if we reach the end of the list, returning list is correct even if we don’t find n. 47 Deleting a Node from a Linked List struct node *delete_from_list(struct node *list, int n) { struct node *cur, *prev; } for (cur = list, prev = NULL; cur != NULL && cur->value != n; prev = cur, cur = cur->next) ; if (cur == NULL) return list; /* n was not found */ if (prev == NULL) list = list->next; /* n is in the first node */ else prev->next = cur->next; /* n is in some other node */ free(cur); return list; 48 Properties of variables • Every variable in a C program has three properties: – Storage duration – Scope – Linkage 49 Declaration Syntax and Examples storage-class type-qualifier type-specifier function-specifier declarators; A declaration with a storage class and three declarators: A declaration with a type qualifier and initializer but no storage class: 50