{%hackmd @themes/orangeheart %} # Associativity, Control Flow, Loops, Functions ### Author's Note: Practices shown in **Precedence - Order of Evaluation** are extremely important, it's best to revise them through to understand how operator priority is evaluated in C. ## Operators ### Logical Operators | Operator | Meaning | Examples | | - | - | - | | `&&` | and | `('A' == 'a') && (3 == 3);` evaluates to 0 | `\|\|` | or | `2 == 3 \|\| 'A' == 'a';` evaluates to 1 | `!` | not | `!(3 == 3);` evaluates to 0 Example: ```C #include <stdio.h> int main(){ int a = 2, b = 3, c = 10; printf("a=%d, b=%d, c=%d\n\n", a, b, c); printf("(a==b)&&(b<c) evaluates to %d\n", (a==b)&&(b<c)); /* evaluates to 1 */ printf("(a==b)&&(b>c) evaluates to %d\n\n", (a==b)&&(b>c)); /*evaluates to 0 */ printf("(a==b)||(b>c) evaluates to %d\n", (a==b)||(b>c)); /* evaluates to 1 */ printf("(a==c)||(b>c) evaluates to %d\n\n", (a==c)||(b>c)); /* evaluates to 0 */ printf("!(a==c) evaluates to %d\n", !(a==c)); /* evaluates to 1*/ printf("!(a==b) evaluates to %d\n", !(a==b)); /* evaluates to 0 */ return 0; } ``` ### Increment and Decrement Operators | Normal Arithmetic Expressions | Shortcut | | - | - | | `x += 1` | `x++` | `x -= 1` | `x--` | `y = x; x += 1` | `y = x++` | `y = x; x -= 1` | `y = x--` **Note for 3rd and 4th expressions, `x` is evaluated before it is decremented. Switching the positions will also switch their order of evaluation.** ### Preincrement and Predecrement vs Postincrement and Postdecrement Prefix operators increases or decreases before execution, postfix does it the other way round: ```C int a = 2, b = 3; printf("%d", a); /* prints 2 */ printf("%d", a++); /* prints 2 then adds 1 so a = 3 */ printf("%d", a); /* prints 3 */ printf("%d", b); /* prints 3 */ printf("%d", ++b); /* adds 1 to b first then prints 4 */ printf("%d", b); /* prints 4 */ ``` ### Assignment Operators Common expressions that can be found while programming is of the type: ``` variable = variable (operator) expression ``` C provides compact assignment operators that can be used instead, this is also known as **syntatic sugar**: ```C x += 1 --> x = x + 1 x -= 1 --> x = x - 1 x *= 5 --> x = x * 5 x %= 10 --> x = x % 10 x /= 2 --> x = x / 2 ``` ## Type Casting Converting data types can be expressed as: ``` (data_type) expresssion ``` Example: ```C #include <stdio.h> int main(void){ int a = 11, b = 5; float result; result = (float) a/b; printf("\n"); printf("\n"); printf("%f\n", result); return 0; } ``` ## Bitwise Operators **Bitwise operators manipulate bits of the integral operands** (unsigned and signed `char`, `short`, `int`, `long` ): | Operator | Expression | Explanation | | - | - | - | | `&` | bitwise AND | bits in the result are set to 1 if **both operands are 1** | `|` | bitwise inclusive OR | bits in the result are set to 1 if **at least one operand is 1** | `^` | bitwise exclusive OR | bits in the result are set to 1 if **only one operand is 1** | `<<` | left shift | **shift the bits of the first operand to the left by a value set by the second operand** | `>>` | right shift | **shift the bits of the first operand to the right by a value set by the second operand** | `~` | one's complement | **all 0 bits are set to 1 and vice-versa** ### Bitwise AND Bitwise AND `&` sets each bit to 1 **if the corresponding bit in both operands is 1:** | Bit 1 | Bit 2 | Bit 1 & Bit 2 | | - | - | - | | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 1 | 0 | 1 | 1 | 1 ### Bitwise OR and XOR **Inclusive vs Exclusive:** - Inclusive OR `|`: each bit to 1 if either (or both bits) is 1 - Exclusive OR `^`: each bit to 1 **if and only if either bits is 1** | Bit 1 | Bit 2 | Bit 1 `\|` Bit 2 | Bit 1 `^` Bit 2 | | - | - | - | - | | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 0 ### Bitwise Complement (One's Complement) - turn all 0 bits to 1 and vice-versa: ```C #include <stdio.h> int main(void){ unsigned short int a = 65535, b = 13, complement_a, complement_b; complement_a = ~a; complement_b = ~b; printf("Bitwise Complement: %d\n", complement_a); printf("Bitwise Complement: %d\n", complement_b); return 0; } ``` Largest `unsigned int` for a 16bit CPU is `65535`, this means that the complement of 0 is `65535` since $65535-0=65535$ Complement of 13 will be $65535-13=65522$ ### Left And Right Shift `<<` and `>>` shifts the bits by a value to the left or right: ```C #include <stdio.h> int main(void){ unsigned short int a = 13, b = 6; printf("Shift Left: %d\n", a<<1); printf("Shift Left: %d\n", b<<2); printf("Shift Right: %d\n", a>>1); printf("Shift Right: %d\n", b>>2); return 0; } ``` We shift the variables' binary values with this example: **Left Shift:** ``` decimal a = 13 binary a = 1101 shifting a to the left by 1: (a<<1): 1101 --> 11010 decimal a = 13*2 = 26 decimal b = 6 binary b = 0110 shifting b to the left by 2: (b<<2): 0110 --> 011000 decimal b = 6*4 = 24 ``` **Right Shift:** ``` decimal a = 13 binary a = 11010 shifting a to the right by 1: (a>>1): 1101 --> 0110 decimal a = 13//2 = 6 decimal b = 6 binary b = 0110 shifting b to the left by 2: (b<<2): 0110 --> 0001 decimal b = 6/4 = 1 ``` ## Associativity - Priority of Operators - Order of Evaluation | Operators | Operator Type | Associativity (LR/RL) | | - | - | - | | `()` | parentheses | LR | `[]` | brackets (array subscript) | LR | `.` | member selection via object name | LR | `->` | member selection via pointer | LR | `++` `--` | postfix increment/decrement | LR | `++` `--` | prefix increment/decrement | ==RL== | `!` `~` | logical negation / bitwise complement | ==RL== | `(type)` | type casting | ==RL== | `sizeof` | byte size check | ==RL== | `*` `/` `%` | multiplicative | LR | `+` `-` | additive and subtractive | LR | `<<` `>>` | shifting | LR | `<` `<=` `>` `>=` | relational | LR | `==` `!=` | equality | LR | `&` | bitwise AND | LR | `^` | bitwise XOR | LR | `\|` | bitwise OR | LR | `&&` | logical AND | LR | \|\| | logical OR | LR | `?:` | conditional | LR | `==` `+=` `-=` `*=` `/=` `%=` `<<=` `>>=` `^=` `\|=` `&=` | assignment | ==RL== | `,` | comma | LR ## Precedence and Order of Evaluation (Practices) ![Precedence and Associativity](https://media.geeksforgeeks.org/wp-content/uploads/20190708173715/Operator-Precedence-and-Associativity-2.jpg) [Reference](https://www.geeksforgeeks.org/operator-precedence-and-associativity-in-c/) **Order of Assignments** ```C int a = 2; a=a+(a=a-(a*a)); printf("%d", a); ``` - `a*a`: 4 - assignment of `a`: `(a=a-(a*a))` - `(a=2-4)`: `a = -2` - since `a` is now `-2`, another assignment of `a`: `a=-2+(-2)` - `a = -4` **Relational Operators** ```C int a = 0; int b; a = (a == (a == 1)); printf("%d", a); //prints 1 ``` - inner parentheses: `(a == 1)` evaluated to `0` - outer paratheses: `(a == 0)` evaluated to `1` - assignment of `a`: `a = 1` ```C int x, y = 5, z = 5; x = y == z; printf("%d", x); ``` - `(y == z)` evaluated to `1` - assignment of `x`: `x = 1` ### Increment and Decrement Operators (Prefix vs Postfix) ```C int p, i = 2, j = 3; p = -i++; p = i+++j; p = i+--j; p = i+++--j; p = i+++i++; p = ++i+(++i); ``` - `p = -i++` - `-i++` evaluated to `3` but not assigned to `p` yet - `p = -2` - `p = i+++j` - `i++` is considered first because it carries a higher precedence, therefore it evaluates to `3` but not assigned to `p` yet - `2+j` where `j = 3` - `p = 5` - `p = i+--j` - `--j` **instantly** evaluates to `2` - `i+2` where `i = 2` - `p = 4` - `p = i+++--j` - `--j` **instantly** evaluates to `2` - `i++` evaluated to `3` but not assigned to `p` yet - `p = 2+2`: `p = 4` - `p = i+++i++` - 1st occurence of `i++` is evaluated to `3` but not assigned yet - consider 2nd occurence of `i++` is already `3++` since the 1st occurence of `i++` **is called to execute** - `p = 2+3`: `p = 5` - `p = ++i+(++i)` - **parentheses has the highest precedence:** `(++i)` **instantly** evaluates to `3`, note that **all** `i` **are also instantly evaluated to** `3`**, postfix operators call and update values to every occurence of the desired variable that has not yet been executed** - since `i` is now `3`, the other `++i` is now `++3`: - `++i` gets evaluated to `4`: `++4` - `(++i)`, was previously `(++3)`, **is now** `(++4)` due to another call by postfix `++i` - `p = 4+4`: `p = 8` ### Arithmetic Vs Assignment Operators ```C int a=12; a+=a-=a*a; ``` - `a*a` evaluated to `12*12 = 144` - `a-=144` where `a` is still `12`: - `a = 12 - 144`: `a = -132` - `a+= -132` where a is `-132`: - `a = -132 + (-132)` - `a = -264` ```C int a=2; a%=4-1; a+=a*=a-=a*=3; ``` - `a*=3` evaluated to `2*6 = 12` - `a-=12` where `a` is `12: - `a = 12 - 12`: `a = 0` - `a*=0` evaluated `0` - `a+=0` evaluated to `0` - `a = 0` ```C #include <stdio.h> void main ( ){ int x, y = 7; float z = 4; x = (y = y + 6, y / z); printf ("x = %d\n", x); } ``` - parentheses evaluated from left to right separated by comma: `y += 6` - `y = 13` - `y/z` is evaluated as `y//z`: `13//4 = 3` - `x = 3` ### Order of Evaluation on `int` and `float` Types Precedences are prioritized from highest precision point to lowest: **Left (Highest) to Right (Lowest)** ```mermaid flowchart LR; id1(long double)-->id2(double) id2(double)-->id3(float) id3(float)-->id4(long) id4(long)-->id5(unsigned) id5(unsigned)-->id6(int) id6(int)-->id7(char, short) ``` ```C #include <stdio.h> void main ( ){ float a, b, c; a = 7 / 2; b = 7 / 2 * 1.0; c = 1.0 * 7 / 2; printf ("a = %f, b = %f, c = %f", a, b, c); } ``` - `a`: division of int between int then change to float `3.000000` - `b`: division of int between int then a multiplication of int and float: `3.000000` - `c`: multiplication of float and int, **int is converted to float first then multiply:** `1.0*7.0 = 7.000000` - division of float between int, int is also converted to float first before being divided: `7.000000/2.000000 = 3.500000` ### **Note that** `float` **always outputs 6-7 precision digits, in this context it's 6.** ## Enumeration Constants - `enum` - set of integer enumeration constants represented by identifiers: ```C enum months { JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC }; ``` This creates a new type `months` in which the index in the enumeration array are set to integers 0-11, respectively. Enumeration ranges can be manipulated by placing an integer as the starting point of the enumeration: ```C enum months{ 1, JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC }; ``` The range of `months` is now **1-12.** ## Conditional Expressions If else blocks can be declared similarly to python: ```C if (condition){ expression; } else{ expressions; } ``` **Using tenary operators to provide syntatic sugar for this particular statement:** ``` variable=(condition)?(expressions if true):(expressions if false) ``` ```C if (x>=0){ s = 1 } else{ x = -1 } //simplified: s=x>=0?1:-1 ``` ## Blocks and Compound Statements ### Simple Blocks - simple statement ends with semicolon `;`: - `z = foo(x+y);` - multiple statements using `foo`: ```C temp = x+y; z = foo(temp); ``` `foo()` provides ease of use to replace expressions with variables without reassigning. - block can substitute for simple statement - compiled as a single unit - variables can be declared inside - semicolons not needed ```C { int temp = x+y; z = foo(temp); } ``` Block can be empty too: `{}` ### Nested Blocks ```C { int temp1 = x-y; z = foo(temp); { float temp2 = x*y; z += bar(temp2); } } ``` `bar()` is used to create bar tables. ## Control Flow Unlike C++, Java, Python etc that has `boolean` values (can include `<stdbool.h>`). **In C, condition is an expression or a series of expressions.** - if expression is non-zero - **Condition is true** - expression must be numeric (or a pointer) ```C const char str[] = "this is a series of characters"; if (str) /* string is not null */ return 0; ``` ## Conditional Statements ### C's Equivalent of `elif` `Else if` is C's equivalent to python's `elif` function, it adds alternative control paths so in order, the program will look into else if blocks before going to the else statement when the previous if condition is not met. ```C if (a>0) x++; else if (a==0) x--; else x = 1 ``` This can be written with nested if blocks too: ```C if (a>0) x++; if (a==0) x--; else x = 1; ``` ### `switch` Statement `switch` is used to iterate an alternative conditional statements. It can be used for `int` and `char` input. - consider cases for value of variable: ```C char s; printf("Type a character from a-d:\n") scanf("%c", &s); switch (s){ case 'a': printf("U typed a"); break; case 'b': printf("U typed b"); break; case 'c': printf("U typed c"); break; case 'd': printf("U typed d"); break; default: /*otherwise or else*/ printf("error"); break } ``` `switch` is a more tidy approach to store multiple test cases in a block. **It is a must to include** `break` **statement to conclude a case.** ### Multiply Case - compare variable to each case in order - inner code starts to execute when match found until `break;` - execution "falls through" if `break;` not included (not recommended) ```C switch (s){ case 'a': case 'b': /* expression starts when either case 'a' or case 'b' is met */ break; } ``` `break` vs no `break`: With `break` every case can be treated individually: ```C switch (s){ case 'a': printf("U typed a"); /* do print method when case 'a' is reached until break; */ break; case 'b': printf("U typed b"); /* do print method when case 'b' is reached and case 'a' is not met, until break */ break; ``` Without `break`, cases are joined similarly to `or` statements: ```C switch (s){ case 'a': printf("U typed a"); /* do print method when case 'a' is reached */ case 'b': printf("U typed b"); /* do print method when either case 'a' or case 'b' is met */ break; } ``` ## Loop Statements ### `while` Loops Similar to python, `while` loop in C is written with almost the same syntax: ```C while (/* condition */) /* expressions */ ``` ### `for` Loops The loop with a counter, or called *counting* loops, can be written in this syntax: - Python ```python for i in range(5): print("hi!\n") ``` - C ```C n = 5; for (i = 0, i <= n, i++){ printf("hi!\n"); } ``` **Syntax:** ``` for (initial value; condition; increment or decrement;){ expressions; } ``` **Note that missing any one of the statements in** `for` **loops will result in an infinite loop.** ### Factorial Example ```C int factorial (int n){ n = 5; int i, j; for (i = 1, j = 1; i <= n; i++){ j *= i; } return j; } ``` - simplified version ```C int factorial (int n){ int i, j; for (i = 1, j = 1; i <= n; j *= i, i++) ; return j; } ``` ### `for` vs `while` loops | `for` | `while` | | - | - | | **know** number of iterations | iterations are **unbounded** | end early via `break` | end early via `break` | uses a **counter** | **uses a counter but must initialize before loop and increment it inside loop** | can rewrite a `for` loop using a `while` loop | may not be able to rewrite a `while` loop using a `for` loop ### `do-while` Loop `do-while` differs from the conventional `while` loop statement: ```C char c; do { puts("Hi!\nY/N\n"); c = getchar(); } while (c == 'Y'); ``` Notice how the expressions are executed before the loop condition is met. This is to say each loop is iterated first before evaluating the condition so that the iterations are prioritized. **Note that for this statement,** `while` **needs a semicolon to end.** ### `break` Keyword `break` is used to exit an iteration or terminate a loop early, it's usage is also similar to python. ### `continue` Keyword `continue` is used to skip an iteration, or skips the rest of the innermost loop body and jumps to the next loop condition. This could be understood by calling it an *ignore* expression. ## Functions C programs are built on functions like `main()`, functions like this are pre-built on the ide unlike python where u have to define it using `def`. However, in C u can also create new functions and use them in programs. ### Euclidean Algorithm Example Find integers using [Euclidean Algorithm](https://www.khanacademy.org/computing/computer-science/cryptography/modarithmetic/a/the-euclidean-algorithm#:~:text=The%20Algorithm,%3D%20B%E2%8B%85Q%20%2B%20R) in $$ax+by = gcd(a,b)$$ ```C int gcd(int a, int b){ while (b){ int temp = b; b = a % b; a = temp; } return a; } ``` ### Pseudocode Explanation ``` Initialize state variables (x,y) if (a < b) swap(a,b) while (b > 0) { compute quotient, remainder update state variables (x,y) } return gcd and state variables (x,y) ``` **Unlike python, it is important to know that functions in C only return up to one value! U can do a workaround by implementing global variables.** ## Modular Programming - C programs don't need to be monolithic - **Module: interface and implementation** - **interface: header files** - **implementation: auxiliary source/object files** - Same concept carries over to external libraries ## The Euclid Module Euclid algorithms are useful in many implementations and can be included in many programs. Euclid modules contain header (`.h`) and source files (`.c`): ```C int gcd(int a, int b){ while (b){ int temp = b; b = a % b; a = temp; } return a; } ``` Extended Euclidean algorithm can be implemented as `ext_euclid()`, also in `euclid.c`. ### `extern` Keyword `extern` is used to inform other source files about functions/global variables in `euclid.c`. - for functions: put function prototypes in header file - for variables: redeclare global variables `extern` in header file `extern` also informs compiler that variable defined somewhere else and enables access to modify a global variable from other source files. ### `euclid.h` Header Header contains prototypes for `gcd()` and `ext_euclid()`: ```C #ifndef __EUCLID_H__ #define __EUCLID_H__ extern int x, y; int gcd(int a, int b); int ext_euclid(int a, int b); #endif ``` ### Using the `euclid.h` Module - calling `gcd()` or `ext.euclid()` from the main file `diophant.c` - include header file `euclid.h` - can call as any other function: ```C g = gcd(a, b); /* compute g = gcd(a, b); */ g = ext_euclid(a, b); /* compute a and b using pre-defined Extended Euclidean Algorithm ext_euclid(a, b); */ ``` - results in global variables `x` and `y`: ```C grow = c/g x *= grow; y *= grow; ``` ### Compiling With The Euclid Module - just compiling `diophant.c` is insufficient - `gcd()` and `ext_euclid()` are defined in `euclid.c`; this source file needs to be compiled too - when compiling in source files, the outputs need to be linked together into a single output ## Variable Scope Declaring and calling functions are similar to python, a function contains: - **Return Type: data type of the value returned** - `void` is similar to `None`, it's returned when nothing returns from the function when called - **Function Name: name of the function** - **Parameters: placeholder, value passed from the caller to function** - **Function Body: expressions in the function block** ### Example of Different Scopes in C **Local vs Global:** ```C int nmax = 20; int main ( int argc , char ∗∗ argv ) { int a = 0 , b = 1 , c , n ; printf( "%3d: %d\n" ,1 ,a ); printf( "%3d: %d\n" ,2 ,b ); for ( n = 3; n <= nmax ; n++) { c = a + b ; a = b ; b = c ; printf( "%3d: %d\n" ,n , c ) ; } return 0; } ``` `nmax` here is a **global variable** that can be used in all functions that are declared, in this case it's `main()`. Inside main we have `a`, `b`, `c` and `n`, these are **local variables** declared and used **only** in `main()` function, or so called *local to* `main()`. Output: ``` 1: 0 2: 1 3: 1 4: 2 5: 3 6: 5 7: 8 8: 13 9: 21 10: 34 11: 55 12: 89 13: 144 14: 233 15: 377 16: 610 17: 987 18: 1597 19: 2584 20: 4181 ``` ### Scope and Nested Declarations Global variables can be reassigned to a different value in a function: ```C int nmax = 20; int main ( int argc , char ∗∗ argv ) { int a = 0 , b = 1 , c , n, nmax = 15; printf( "%3d: %d\n" ,1 ,a ); printf( "%3d: %d\n" ,2 ,b ); for ( n = 3; n <= nmax ; n++) { c = a + b ; a = b ; b = c ; printf( "%3d: %d\n" ,n , c ) ; } return 0; } ``` Output: ``` 1: 0 2: 1 3: 1 4: 2 5: 3 6: 5 7: 8 8: 13 9: 21 10: 34 11: 55 12: 89 13: 144 14: 233 15: 377 ``` ## `static` and `register` ### Static Variables Static variables have a special property of preserving their value even after they are out of scope. They preserve their previous values and are not initialized again in the new scope. Static variables have two meanings depending on the location they're declared: ### Outside Function Static variables/functions are only visible within that file, not globally (cannot be `extern`-ed) ### Inside Function Static variables: - still local to that function - are initialized only during program initialization - do not get reinitialized with each function call ```C static int random_static_var = 0; ``` ### Register Variables `register` **provides a more useful and faster approach to access commonly used data and minimize load/store overhead.** Registers are faster than memory to access, it hints to compiler that a given variable can be put in a register. It's the compiler choice to put it in or not but generally they do optimizations themselves and put variables in registers. - during execution, data processed in registers - can explicitly declare certain variables as registers using `register` ### What Can Be Registered? - simple types (implementation-dependant) - **only local variables and function arguments** - excess/unallowed register declarations ignored, compiled as regular variables - **Registers do not reside in addressed memory; pointer of a register variable is illegal** Variable scope examples with `register` implementation: ```C #include <stdio.h> int nmax = 20; int main ( register int argc , register char ** argv ) { register int a = 0 , b = 1 , c , n, nmax = 15; printf( "%3d: %d\n" ,1 ,a ); printf( "%3d: %d\n" ,2 ,b ); for ( n = 3; n <= nmax ; n++) { c = a + b ; a = b ; b = c ; printf( "%3d: %d\n" ,n , c ) ; } return 0; } ```