├── .gitattributes ├── C-Syntax ├── .vscode │ └── settings.json ├── assignmentOps ├── assignmentOps.c ├── booleanType.c ├── comments.c ├── compareOps.c ├── conditional-if-else.c ├── conditional-if-elseif.c ├── conditional-if.c ├── debugAssignment1.c ├── debugAssignment2.c ├── do_while_loop.c ├── escapeChars.c ├── floatingPointTypes.c ├── forLoopForever.c ├── forLoopMultiple.c ├── forLoopNoInitializer.c ├── forLoopNoPosterior.c ├── globalScope.c ├── hashInclude.c ├── impOps.c ├── integralTypes.c ├── localScope.c ├── logicalOps.c ├── lsout ├── myHeader.h ├── simpleFor.c ├── simplePrintf.c ├── simpleScanf.c ├── switch-1.c ├── switch-2-nobreak ├── switch-2-nobreak.c ├── switch-3-default.c ├── vars.c └── while_loop.c ├── CCA-Exam-Papers ├── CCA -1 Retest - Div 1.pdf ├── CCA -1 Retest - Div 2.pdf ├── CCA-1 Retest - Div 3.pdf ├── CCA-1-Div-A.pdf ├── CCA-1-Div-B.pdf └── CCA-1-Div-C.pdf ├── HandsOnLabSessions ├── AdvancedConcepts │ ├── ArrayOfFunctionPointers.c │ ├── BadRecursionStackOverflow.c │ ├── FunctionPointers.c │ ├── MultiFileCPrograms │ │ ├── ExplanatoryNotes.txt │ │ ├── File1.c │ │ ├── File2.c │ │ ├── File3.c │ │ └── MyHeader.h │ ├── RawBufferManipulation.c │ └── Typedefs.c ├── ArraysLab │ ├── Array2D.c │ ├── ArrayIndexing.c │ ├── ArrayModifying.c │ ├── ArrayOfStrings.c │ ├── ArraysBasic.c │ ├── CommandLineArgs.c │ ├── CommandLineArgs2.c │ ├── StringGotcha.c │ └── StringsAsArrays.c ├── DynamicMemoryLab │ ├── AllocatingStructs.c │ ├── DynamicMemSimple.c │ ├── DynamicStringCpy.c │ └── ExplanatoryNotes.txt ├── FileHandlingLab │ ├── ExplanatoryNotes.txt │ ├── FileReadSimple.c │ ├── FileWriteBinary.c │ └── FileWriteSimple.c ├── PointersLab │ ├── MultiplePointers.c │ ├── PointerBasic.c │ ├── PointerDerefRead.c │ ├── PointerDerefWrite.c │ ├── PointerMath.c │ └── PointerMath2.c ├── StringsLab │ ├── MyStrcpy.c │ ├── MyStrlen.c │ ├── StringCpy.c │ ├── StringLen.c │ └── StringNCpy.c └── StructsLab │ ├── ArrayOfStructs.c │ ├── ExplanatoryNotes.txt │ ├── StructBetter.c │ └── StructSimple.c ├── Presentation ├── C Syntax Overview.key └── C Syntax Overview.pdf └── README.md /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /C-Syntax/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.associations": { 3 | "stdio.h": "c" 4 | } 5 | } -------------------------------------------------------------------------------- /C-Syntax/assignmentOps: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rahulek/ProgrammingFoundationCourse/989572cf58ea8773197b07c4e05bc7754b6114a1/C-Syntax/assignmentOps -------------------------------------------------------------------------------- /C-Syntax/assignmentOps.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void assignmentOps(void) 4 | { 5 | int x = 10; 6 | 7 | printf("\n Beginning x= %d\n", x); 8 | x += 10; // x = x + 10 -> 20 9 | printf("x += 10, Result=%d\n", x); 10 | x *= 2; // x = x * 2 -> 40 11 | printf("x *= 2, Result= %d\n", x); 12 | x /= 10; // x = x / 10 -> 4 13 | printf("x /= 10, Result= %d\n", x); 14 | x %= 5; // x = x % 5 -> 4 15 | printf("x %%= 5, Result= %d\n", x); //<---- Note %% to make sure % appears! 16 | 17 | // Bitwise Assignments 18 | unsigned char allBits = 0xFF; // AllBits are 1 19 | printf("\n--- Bitwise ---"); 20 | printf("Begin: allBits: 0x%x\n", allBits); 21 | 22 | // Bitwise AND 23 | allBits &= 0xF0; // allBits = 0xFF & 0xF0 -> 0xF0 24 | printf("allBits & 0xF0, Result=0x%x\n", allBits); 25 | 26 | // Bitwise OR 27 | allBits |= 0x0F; // allBits = 0xF0 & 0x0F -> 0xFF 28 | printf("allBits |= 0x0F, Result=0x%x\n", allBits); 29 | 30 | // Bitwuse XOR 31 | allBits ^= 0xFF; // allBits = 0xFF XOR 0xFF -> 0x00 32 | printf("allBits ^= 0xFF, Result=0x%x\n", allBits); 33 | 34 | return; 35 | } 36 | 37 | int main(void) 38 | { 39 | assignmentOps(); 40 | 41 | return 0; 42 | } -------------------------------------------------------------------------------- /C-Syntax/booleanType.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include // for bool types only. 3 | 4 | void booleanType(void) 5 | { 6 | bool underAge = false; // internally represented as 0 7 | bool isGeek = true; // internally represented as 1 8 | 9 | printf("\n----Boolean----\n"); 10 | printf("underAge=%d, isGeek=%d\n", underAge, isGeek); 11 | 12 | return; 13 | } 14 | 15 | int main(void) 16 | { 17 | booleanType(); 18 | 19 | return 0; 20 | } -------------------------------------------------------------------------------- /C-Syntax/comments.c: -------------------------------------------------------------------------------- 1 | /* 2 | * File: comments.c 3 | * 4 | * This file demonstrates the usage of code comments in a typical 5 | * C program. 6 | */ 7 | 8 | #include /* for printf/scanf and other functions */ 9 | 10 | /* 11 | * Function: simpleAdd 12 | * Purpose: Adds two input integers and returns the result of the addition 13 | * Input Parameters: 14 | * a: integer, first operand for the addition operation 15 | * b: integer, second operand for the addition operation 16 | * Return Value: 17 | * integer, result of the addition opeartion 18 | * 19 | */ 20 | int simpleAdd(int a, int b) 21 | { 22 | int c = a + b; // add two input parameters and store the result 23 | 24 | return c; /* return the result */ 25 | } 26 | 27 | /* 28 | * The main function 29 | * Purpose: invoke the addition function and prints the results. 30 | * Input Parameters: 31 | * None 32 | * Return Value: 33 | * Integer, returning 0 to indicate SUCCESS, -1 to failure 34 | */ 35 | int main(void) 36 | { 37 | int op1 = 10; 38 | int op2 = 20; 39 | 40 | /* Block comment 41 | 1. Invoke simpleAdd function passing in two operands and accept 42 | the result. 43 | 2. Print the result by using the printf() C stdlib function. 44 | */ 45 | int result = simpleAdd(op1, op2); 46 | printf("Result of adding %d and %d is %d\n", op1, op2, result); 47 | 48 | return 0; // SUCCESS, returning 0. 49 | } -------------------------------------------------------------------------------- /C-Syntax/compareOps.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include //for the bool type 3 | 4 | void compareOps(void) 5 | { 6 | int a = 144; 7 | int b = 256; 8 | 9 | int c = 0x20; 10 | int d = 32; 11 | 12 | // Example of a C statement 13 | // Statement MUST end with a semi-colon (;) 14 | // RHS is an example of an expression 15 | // Expression is evaluated and then the result 16 | // of an evaluation assigned to the LHS. 17 | 18 | // Expressions involving Relational operators result in 19 | // a boolean value (TRUE/FALSE) 20 | // If the RHS evaluates to TRUE (or 1) -> LHS is set to TRUE 21 | // If the RHS evaluates to FALSE (or 0) -> LHS is set to FALSE 22 | 23 | bool aLessThanB = (a < b); // TRUE 24 | bool bGreatherThana = (b > a); // TRUE 25 | 26 | bool cEqsD = (c == d); // TRUE: c is an HEX notation and d is Decimal 27 | bool dEqsC = (d == c); // TRUE 28 | bool dEqsConstant = (d == 0x99); // FALSE 29 | bool cEqsConstant = (c != 0x20); // FALSE 30 | 31 | // print the results 32 | printf("a < b? = %d\n", aLessThanB); 33 | printf("b > a? = %d\n", bGreatherThana); 34 | printf("c == d? = %d\n", cEqsD); 35 | printf("d == c? = %d\n", dEqsC); 36 | printf("d == 0x99? = %d\n", dEqsConstant); 37 | printf("d == 0x20? = %d\n", cEqsConstant); 38 | 39 | return; 40 | } 41 | 42 | int main(void) 43 | { 44 | compareOps(); 45 | 46 | return 0; 47 | } 48 | -------------------------------------------------------------------------------- /C-Syntax/conditional-if-else.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(void) 4 | { 5 | int age = 7; 6 | 7 | // Conditional Logic with if 8 | if (age > 60) 9 | { 10 | printf("Age: %d is a senior citizen.\n", age); 11 | } 12 | else 13 | { 14 | printf("Age: %d is NOT a senior citizen\n", age); 15 | } 16 | 17 | return 0; 18 | } -------------------------------------------------------------------------------- /C-Syntax/conditional-if-elseif.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(void) 4 | { 5 | int age = 7; 6 | 7 | // Conditional Logic with if 8 | if (age > 60) 9 | { 10 | printf("Age: %d is a senior citizen.\n", age); 11 | } 12 | else if (age >= 18 && age <= 60) 13 | { 14 | printf("Age: %d is a non-senior adult.\n", age); 15 | } 16 | else 17 | { 18 | printf("Age %d is NOT an adult.", age); 19 | } 20 | 21 | return 0; 22 | } -------------------------------------------------------------------------------- /C-Syntax/conditional-if.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(void) 4 | { 5 | int age = 74; 6 | 7 | // Conditional Logic with if 8 | if (age > 60) 9 | { 10 | printf("Age: %d is a senior citizen.\n", age); 11 | } 12 | // NOTE: No else condition here 13 | 14 | return 0; 15 | } -------------------------------------------------------------------------------- /C-Syntax/debugAssignment1.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | // Find the maximum of 2 numbers 4 | int maxOf(int num1, int num2) 5 | { 6 | int max; 7 | 8 | if (num1 < num2) 9 | { 10 | max = num1; 11 | } 12 | else 13 | { 14 | max = num2; 15 | } 16 | 17 | return max; 18 | } 19 | 20 | int main(void) 21 | { 22 | int n1 = 10; 23 | int n2 = -10; 24 | 25 | // FIXME: Debug and fix the program to get the correct result. 26 | int max = maxOf(10, -10); 27 | 28 | // FIXME: Generates a compiler warning. 29 | printf("Max of (%d, %d) is %d\n", max); 30 | 31 | int i = 10; 32 | int j = i++; 33 | printf("i=%d, j=%d\n", i, j); 34 | 35 | return 0; 36 | } -------------------------------------------------------------------------------- /C-Syntax/debugAssignment2.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(void) 4 | { 5 | int x = 10; 6 | int *p = &x; 7 | 8 | int y = *p; 9 | printf("y is %d\n", y); 10 | *p = -10; 11 | printf("x now is %d\n", x); 12 | 13 | return 0; 14 | } -------------------------------------------------------------------------------- /C-Syntax/do_while_loop.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(void) 4 | { 5 | int counter; 6 | 7 | counter = 0; 8 | do 9 | { 10 | printf("do..while(): counter= %d\n", counter); 11 | counter++; 12 | } while (counter <= 10); 13 | 14 | return 0; 15 | } -------------------------------------------------------------------------------- /C-Syntax/escapeChars.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(void) 4 | { 5 | //\n - insert a newline 6 | printf("Line1\nLine2\n"); 7 | 8 | //\t - insert a tab 9 | printf("A\ttabbed\toutput.\n"); 10 | 11 | //\' - insert a quote 12 | printf("He asked - \'How are you\?\'\n"); 13 | 14 | //\\ - insert a backslash 15 | printf("Directory is: C:\\\n"); 16 | 17 | return 0; 18 | } -------------------------------------------------------------------------------- /C-Syntax/floatingPointTypes.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void floatingTypes(void) 4 | { 5 | float pi = 3.1415925; 6 | float kb = 1e3; 7 | double d1 = 987654321.123456789; 8 | double d2 = -12.34e-6; 9 | 10 | printf("\n---Floats and Doubles----\n"); 11 | printf("pi=%f, kb=%f, d1=%f, d2=%f\n", pi, kb, d1, d2); 12 | 13 | return; 14 | } 15 | 16 | int main(void) 17 | { 18 | floatingTypes(); 19 | 20 | return 0; 21 | } -------------------------------------------------------------------------------- /C-Syntax/forLoopForever.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(void) 4 | { 5 | int counter = 0; 6 | 7 | for (;;) 8 | { 9 | // WHAT WILL HAPPEN HERE?? 10 | printf("Counter is %d\n", counter); 11 | } 12 | 13 | return 0; 14 | } -------------------------------------------------------------------------------- /C-Syntax/forLoopMultiple.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(void) 4 | { 5 | int row, col; // NOTE: uninitialized 6 | 7 | for (row = 0, col = 0; // multiple init statements 8 | row <= 5; // condition yielding a bool 9 | row++, col++ // multiple post statements 10 | ) 11 | { 12 | printf("Row %d, Col %d\n", row, col); 13 | } 14 | printf("-----------------\n"); 15 | for (row = 0; row <= 5; row++) 16 | { 17 | for (col = 0; col <= 5; col++) 18 | { 19 | printf("Row %d, Col %d\n", row, col); 20 | } 21 | } 22 | 23 | return 0; 24 | } -------------------------------------------------------------------------------- /C-Syntax/forLoopNoInitializer.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(void) 4 | { 5 | int counter = -10; // initializer 6 | 7 | // NOTE: no initializer here 8 | for (; counter <= 10; counter++) 9 | { 10 | printf("Counter is %d.\n", counter); 11 | } 12 | 13 | return 0; 14 | } -------------------------------------------------------------------------------- /C-Syntax/forLoopNoPosterior.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(void) 4 | { 5 | int counter = -10; 6 | 7 | // NOTE: no initializer; no posterior statements 8 | for (; counter <= 10;) 9 | { 10 | printf("Counter is: %d\n", counter); 11 | 12 | if (counter == 10) 13 | { 14 | break; 15 | } 16 | else 17 | { 18 | counter++; 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /C-Syntax/globalScope.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | // This is an integer defined in the Global scope 4 | // and would be available for all the functions within 5 | // this source file (and also other files that this may 6 | // link with!) 7 | int globalVar = 10; 8 | 9 | void testFunction1(void) 10 | { 11 | // increment globalVar by 15 12 | // this is possible because globalVar is global 13 | globalVar = globalVar + 15; 14 | 15 | return; 16 | } 17 | 18 | void testFunction2(void) 19 | { 20 | // decrement globalVar by 5 21 | // this is possible because globalVar is global 22 | globalVar = globalVar - 5; 23 | 24 | return; 25 | } 26 | 27 | int main(void) 28 | { 29 | // globalVar is accessibly here too 30 | printf("Start of main(): globalVar = %d\n", globalVar); 31 | testFunction1(); 32 | testFunction2(); 33 | printf("End of main(): globalVar = %d\n", globalVar); 34 | 35 | return 0; 36 | } -------------------------------------------------------------------------------- /C-Syntax/hashInclude.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "myHeader.h" 3 | 4 | int main(void) 5 | { 6 | // Run the gcc -E to see the preprocessor output 7 | printf("MONDAY is %d\n", MONDAY); 8 | printf("Interest rate is %d\n", INTEREST_RATE_PERCENT); 9 | printf("Default name: %s\n", DEFAULT_NAME); 10 | 11 | return 0; 12 | } -------------------------------------------------------------------------------- /C-Syntax/impOps.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void impOps(void) 4 | { 5 | char c = 'A'; 6 | short s = 0x2F0; 7 | int i = 0x12345678; 8 | 9 | // sizeof() 10 | printf("sizeof(c) = %d\n", sizeof(c)); 11 | printf("sizeof(s) = %d\n", sizeof(s)); 12 | printf("sizeof(i) = %d\n", sizeof(i)); 13 | 14 | // Tertiary assignment 15 | int y = (i == 0x12345678) ? 0x87654321 : -1; 16 | 17 | // Reference and Derefrence 18 | // Reference -> Get the address location in memory (PEEK) 19 | // Dereference -> Read/Write to the address in memory (PEEK/POKE) 20 | 21 | int *p = &i; // Take a PEEK at where i is stored in memory 22 | printf("i is stored in memory at an address: 0x%x\n", p); 23 | 24 | // PEEK the contents of i via p 25 | // p is pointing to i, therefore it can be READ via p 26 | int iViaP = *p; // Read 4 bytes at p and store in "iViaP" 27 | printf("Read 0x%x via p.\n", iViaP); 28 | 29 | // POKE the contents of i via p 30 | // p is pointing to i, therefore it can WRITE via p 31 | *p = 0xF0F01234; 32 | 33 | // Now read back again as "i" 34 | // its the same variable i that is read 35 | printf("After poking with a pointer, i is 0x%x\n", i); 36 | 37 | return; 38 | } 39 | 40 | int main(void) 41 | { 42 | impOps(); 43 | 44 | return 0; 45 | } -------------------------------------------------------------------------------- /C-Syntax/integralTypes.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void integralTypes() 4 | { 5 | char c1 = 'A'; 6 | char c2 = 0x41; 7 | 8 | unsigned short s1 = 14567; 9 | short s2 = -5463; 10 | short invalidShort = 35432; // Compiler Warning due to overflow 11 | 12 | unsigned int adultAge = 18; 13 | int errorCode = -3456; 14 | int invalidInt = 0xF12345678; // Compiler warning due to overflow 15 | 16 | printf("\n----Chars----\n"); 17 | printf("c1=%c, c2=%c \n", c1, c2); 18 | 19 | printf("----Shorts----\n"); 20 | printf("s1=%d, s2=%d, invalidShort=%d\n", s1, s2, invalidShort); 21 | 22 | printf("----Ints----\n"); 23 | printf("adultAge=%d, errorCode=%d, invalidInt=0x%x\n", 24 | adultAge, errorCode, invalidInt); 25 | 26 | printf("\n"); 27 | } 28 | 29 | /* 30 | Typically, in C Code: 31 | return of 0 indicates SUCCESS 32 | return of -1 or any negative number indicates an ERROR 33 | This is a convention-only, not a rule. 34 | */ 35 | int main(void) 36 | { 37 | integralTypes(); 38 | 39 | return 0; // return SUCCESS. 40 | } -------------------------------------------------------------------------------- /C-Syntax/localScope.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void function1(void) 4 | { 5 | // These two variables are exactly the same 6 | // as in function2 below. 7 | // They would NOT conflict because the scope 8 | // of them are LOCAL. 9 | // When this function returns, localInt, localChar 10 | // are "gone" 11 | 12 | int localInt = 100; 13 | char localChar = 'Z'; 14 | 15 | printf("\n--- function 1 ----\n"); 16 | printf("localInt = %d\n", localInt); 17 | printf("localChar = %c\n", localChar); 18 | 19 | return; 20 | } 21 | 22 | void function2(void) 23 | { 24 | // same named variables as in function1() 25 | int localInt = -100; 26 | char localChar = 'A'; 27 | 28 | printf("\n--- function 2 ----\n"); 29 | printf("localInt = %d\n", localInt); 30 | printf("localChar = %c\n", localChar); 31 | 32 | return; 33 | } 34 | 35 | int main(void) 36 | { 37 | // print the local variables with calls to function1 and function2 38 | function1(); 39 | function2(); 40 | 41 | return 0; 42 | } -------------------------------------------------------------------------------- /C-Syntax/logicalOps.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | void logicalOps(void) 5 | { 6 | int age = 45; 7 | int mathsMarks = 45; 8 | int programmingMarks1 = 56; 9 | 10 | // see if age is > 60 to see -> its a senior citizen age if TRUE 11 | // simple relational > operator 12 | bool isSeniorCitizen = age > 60; 13 | printf("Age= %d, isSeniorCitizen?= %d\n", age, isSeniorCitizen); 14 | 15 | // see if age is >=18 *AND* < 60 -> its a middle-age adult if TRUE 16 | // AND in the condition implies logical AND 17 | bool isMiddleAgedAdult = (age >= 18 && age < 60); 18 | printf("Age= %d, isMiddleAged?= %d\n", age, isMiddleAgedAdult); 19 | 20 | // see if age >= 13 *AND* <= 18 -> its a TEEN ager 21 | // instead of boolean return, we use a if-else construct 22 | // to execute some code conditionally. 23 | if (age >= 13 && age <= 18) 24 | { 25 | // code executed only if both conditions TRUE 26 | printf("Age %d is a TEEN's age.\n", age); 27 | } 28 | else 29 | { 30 | printf("Age %d is NOT a TEENager.\n", age); 31 | } 32 | 33 | // see if the student can progress 34 | // Maths marks >= 35 *OR* Programming Marks >= 35 35 | bool studentPassed = (mathsMarks >= 35 || programmingMarks1 == 35); 36 | printf("Maths Score= %d, Programming Score=%d, Passed= %d\n", 37 | mathsMarks, programmingMarks1, studentPassed); 38 | } 39 | 40 | int main(void) 41 | { 42 | logicalOps(); 43 | 44 | return 0; 45 | } -------------------------------------------------------------------------------- /C-Syntax/lsout: -------------------------------------------------------------------------------- 1 | assignmentOps.c 2 | booleanType.c 3 | comments.c 4 | compareOps.c 5 | conditional-if-else.c 6 | conditional-if-elseif.c 7 | conditional-if.c 8 | do_while_loop.c 9 | escapeChars.c 10 | floatingPointTypes.c 11 | forLoopForever.c 12 | forLoopMultiple.c 13 | forLoopNoInitializer.c 14 | forLoopNoPosterior.c 15 | globalScope.c 16 | hashInclude.c 17 | impOps.c 18 | integralTypes.c 19 | localScope.c 20 | logicalOps.c 21 | lsout 22 | myHeader.h 23 | simpleFor.c 24 | simplePrintf.c 25 | simpleScanf.c 26 | switch-1.c 27 | switch-2-nobreak 28 | switch-2-nobreak.c 29 | switch-3-default.c 30 | vars.c 31 | while_loop.c 32 | -------------------------------------------------------------------------------- /C-Syntax/myHeader.h: -------------------------------------------------------------------------------- 1 | #define MONDAY 1 2 | #define TUESDAY 2 3 | #define NUM_DAYS_PER_WEEK 7 4 | 5 | #define TAX_RATE_PERCENT 10 6 | #define INTEREST_RATE_PERCENT 7 7 | 8 | #define DEFAULT_NAME "TestUser" -------------------------------------------------------------------------------- /C-Syntax/simpleFor.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(void) 4 | { 5 | // Code that iteratively prints out a message with 6 | // the current counter value. 7 | 8 | int counter = 0; 9 | 10 | for (counter = -10; counter <= 10; counter++) 11 | { 12 | printf("Current counter: %d\n", counter); 13 | } 14 | 15 | return 0; 16 | } -------------------------------------------------------------------------------- /C-Syntax/simplePrintf.c: -------------------------------------------------------------------------------- 1 | #include //Needed for printf() 2 | 3 | int main(void) 4 | { 5 | char ch = 'A'; 6 | short sh = 4567; 7 | unsigned int i = 100; 8 | unsigned long l = 65536; 9 | float pi = 3.14; 10 | 11 | printf("ch is a character: %c\n", ch); 12 | printf("sh is a short: %hi\n", sh); 13 | printf("i is an unsigned int, %i\n", i); 14 | printf("i is an int printed out in HEX: 0x%x\n", i); 15 | printf("i is an int printed out in OCTAL: %o\n", i); 16 | printf("l is an unsigned long: %ld\n", l); 17 | printf("l as HEX: 0x%lx\n", l); 18 | printf("pi is a float: %f\n", pi); 19 | printf("Printing the Percent sign itself: %%\n"); 20 | 21 | return 0; 22 | } -------------------------------------------------------------------------------- /C-Syntax/simpleScanf.c: -------------------------------------------------------------------------------- 1 | #include //for printf() and scanf() calls. 2 | 3 | int main(void) 4 | { 5 | // Get an age from the user 6 | int age = 0; 7 | printf("Please enter your age: "); // NOTE - No \n here 8 | 9 | // scanf here means - read an interger(%d) *INTO* age 10 | //&age -> address of age in memory. 11 | scanf(" %d", &age); 12 | 13 | // Get user's sex - M/F/T - Male/Female/Third 14 | char sex = 'Z'; // Invalid sex 15 | printf("Enter your Sex(M/F/T): "); 16 | scanf(" %c", &sex); 17 | 18 | // name is an *array* of 25 chars 19 | char name[25]; 20 | printf("Enter your name: "); 21 | scanf(" %s", &name[0]); // provide an address of the first char 22 | 23 | printf("\n-- Following data was entered --\n"); 24 | printf("\t\tName: %s\n", name); // OR also &name[0] 25 | printf("\t\tAge: %d\n", age); 26 | printf("\t\tSex: %c\n", sex); 27 | printf("----------Done--------------------"); 28 | 29 | return 0; 30 | } -------------------------------------------------------------------------------- /C-Syntax/switch-1.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main(void) 5 | { 6 | int weekday = 7; 7 | 8 | switch (weekday) 9 | { 10 | case 1: 11 | printf("It's Monday today.\n"); 12 | break; 13 | case 2: 14 | printf("It's Tuesday today.\n"); 15 | break; 16 | case 3: 17 | printf("It's Wednesday today.\n"); 18 | break; 19 | case 4: 20 | printf("It's Thursday.\n"); 21 | break; 22 | case 5: 23 | printf("It's Friday.\n"); 24 | break; 25 | case 6: 26 | printf("It's Saturday.\n"); 27 | break; 28 | case 7: 29 | printf("It's Sunday.\n"); 30 | break; 31 | } 32 | 33 | // NOTE: no default case handling 34 | return 0; 35 | } -------------------------------------------------------------------------------- /C-Syntax/switch-2-nobreak: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rahulek/ProgrammingFoundationCourse/989572cf58ea8773197b07c4e05bc7754b6114a1/C-Syntax/switch-2-nobreak -------------------------------------------------------------------------------- /C-Syntax/switch-2-nobreak.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(void) 4 | { 5 | int dayOfWeek = 4; 6 | 7 | switch (dayOfWeek) 8 | { 9 | case 4: 10 | printf("Day is the 4th day of the week\n"); 11 | // NOTE: no break 12 | 13 | case 5: 14 | printf("I am day 5\n"); 15 | break; 16 | } 17 | } -------------------------------------------------------------------------------- /C-Syntax/switch-3-default.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(void) 4 | { 5 | int flower = 3; // 1- ROSE, 2 - JASMINE, 3 - CHAMOMILE 6 | 7 | switch (flower) 8 | { 9 | case 1: 10 | printf("its a rose.\n"); 11 | break; 12 | case 2: 13 | printf("it's a jasmine flower\n"); 14 | break; 15 | 16 | // The default block executes when no matching case is 17 | // found. 18 | default: 19 | printf("Neither Rose nor Jasmine, must be Chamomile"); 20 | break; 21 | } 22 | 23 | return 0; 24 | } -------------------------------------------------------------------------------- /C-Syntax/vars.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void vars(void) 4 | { 5 | int validVar; // valid variable name of type int 6 | //char 1number; //invalid since it begins with a digit 7 | char x10; // ok as begines with an alphabet 8 | // int x-y; //invalid becase uses a reserved - operator 9 | int _debugInfo = 1; // OK to begin with an underscore but used mainly by the tools 10 | // avoid using it in your programs. 11 | int x_y = 999; // OK; 12 | long $xxx = 12345678; // ok but not meaningful in the programming context 13 | int X10; // OK even though 'x10' exits. Variables are case-sensitive 14 | 15 | return; 16 | } 17 | 18 | int main(void) 19 | { 20 | vars(); 21 | 22 | return 0; 23 | } -------------------------------------------------------------------------------- /C-Syntax/while_loop.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(void) 4 | { 5 | int counter; // NOTE: uninitialized 6 | 7 | counter = 10; 8 | while (counter > 0) 9 | { 10 | printf("while loop: counter= %d\n", counter); 11 | 12 | // NOTE: counter must be incremented/decremented before 13 | // the while condition is checked again. 14 | // FAILURE mode - cause of bugs 15 | counter--; 16 | } 17 | 18 | return 0; 19 | } -------------------------------------------------------------------------------- /CCA-Exam-Papers/CCA -1 Retest - Div 1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rahulek/ProgrammingFoundationCourse/989572cf58ea8773197b07c4e05bc7754b6114a1/CCA-Exam-Papers/CCA -1 Retest - Div 1.pdf -------------------------------------------------------------------------------- /CCA-Exam-Papers/CCA -1 Retest - Div 2.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rahulek/ProgrammingFoundationCourse/989572cf58ea8773197b07c4e05bc7754b6114a1/CCA-Exam-Papers/CCA -1 Retest - Div 2.pdf -------------------------------------------------------------------------------- /CCA-Exam-Papers/CCA-1 Retest - Div 3.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rahulek/ProgrammingFoundationCourse/989572cf58ea8773197b07c4e05bc7754b6114a1/CCA-Exam-Papers/CCA-1 Retest - Div 3.pdf -------------------------------------------------------------------------------- /CCA-Exam-Papers/CCA-1-Div-A.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rahulek/ProgrammingFoundationCourse/989572cf58ea8773197b07c4e05bc7754b6114a1/CCA-Exam-Papers/CCA-1-Div-A.pdf -------------------------------------------------------------------------------- /CCA-Exam-Papers/CCA-1-Div-B.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rahulek/ProgrammingFoundationCourse/989572cf58ea8773197b07c4e05bc7754b6114a1/CCA-Exam-Papers/CCA-1-Div-B.pdf -------------------------------------------------------------------------------- /CCA-Exam-Papers/CCA-1-Div-C.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rahulek/ProgrammingFoundationCourse/989572cf58ea8773197b07c4e05bc7754b6114a1/CCA-Exam-Papers/CCA-1-Div-C.pdf -------------------------------------------------------------------------------- /HandsOnLabSessions/AdvancedConcepts/ArrayOfFunctionPointers.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | //a new type with typedef. 4 | // Name of the type: PRINTFN 5 | // Its a pointer to a function which 6 | // takes a char* input and returns nothing 7 | // ie. (void) 8 | typedef void (*PRINTFN)(char*); 9 | 10 | //Define 3 samples functions 11 | //which would match the PRINTFN type 12 | // - 1 char* param and void return 13 | // 14 | void printFunc1(char *msg) { 15 | printf("printFunc1: %s\n", msg); 16 | return; 17 | } 18 | 19 | void printFunc2(char *msg) { 20 | printf("printFunc2: %s\n", msg); 21 | } 22 | 23 | void printFunc3(char *msg) { 24 | printf("printFunc3: %s\n", msg); 25 | } 26 | 27 | 28 | int main(void) { 29 | //Array of 3 function pointers. 30 | //Because this is an array of 3 PRINTFNs, 31 | //and the PRINTFN type is a function pointer, 32 | //we can store name of the printxxxx function 33 | //there. 34 | //Each printXXXX function fulfills the PRINTFN 35 | //type's signtaure requirement - char* input and 36 | //void return. 37 | PRINTFN allPrintFuncs[3] = { 38 | printFunc1, 39 | printFunc2, 40 | printFunc3 41 | }; 42 | 43 | //Iterate through the array and invoke the function 44 | //Each array element's type is PRINTFN. Therefore, 45 | //we index into the allPrintFunc array and use the index 46 | //where the pointer-to-the-function is stored and we invoke 47 | //it. 48 | char buf[100]; 49 | for(int i=0; i<3; i++) { 50 | sprintf(buf, "*** function pointer %d.***", i); 51 | allPrintFuncs[i](buf); 52 | } 53 | 54 | 55 | //Done 56 | return 0; 57 | 58 | } 59 | 60 | -------------------------------------------------------------------------------- /HandsOnLabSessions/AdvancedConcepts/BadRecursionStackOverflow.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | /* 4 | DONT WRITE RECURSION LIKE THIS. 5 | THIS IS A DEMO OF BAD RECURSIVE CODE. 6 | 7 | Depending upoin the platform you run this program, 8 | it will fail/crash with - 9 | - Segmentation fault( Unix/Mac ) 10 | - Stack Overflow error (Windows) 11 | 12 | WHY DOES IT CRASH? 13 | - In general, functions use call stack and for 14 | every call, a call-frame is created on the stack memory 15 | - In the recursive function, such as this, the function code 16 | calls itself. 17 | - If the function DOES NOT have any terminating condition, the 18 | code would keep calling itself. This infinite calling sequence 19 | will cause the runtime to create a call-frame for each such 20 | call 21 | - Its therefore easy to see that at some point, the call stack 22 | will run out of space 23 | - This causes the program to crash 24 | 25 | SOLUTION: 26 | - In a recursive code, always put a termination condition that 27 | will stop the recursion. 28 | - For example, in the below code, see the goodRecursion function 29 | to see how the problem with the badRecursion is fixed. 30 | */ 31 | void badRecursion(int startValue) { 32 | printf("I am starting at: %d\n", startValue); 33 | 34 | badRecursion(startValue+1); //recursive call with a new value 35 | } 36 | 37 | void goodRecursion(int startValue) { 38 | printf("I am starting at: %d\n", startValue); 39 | 40 | //Add a termination condtion - 25000 is just an arbitrary value 41 | if( startValue == 25000) { 42 | return; 43 | } else { 44 | goodRecursion(startValue+1); //recursive call with a new value 45 | } 46 | } 47 | 48 | 49 | int main(void) { 50 | /* Uncomment the badRecursion() call to see the program crash */ 51 | //badRecursion(1); 52 | 53 | //The following call will not crash due to infinite recursion 54 | goodRecursion(1); 55 | } -------------------------------------------------------------------------------- /HandsOnLabSessions/AdvancedConcepts/FunctionPointers.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | 4 | //Define a new type 5 | //The type can be explained as - 6 | // type's name is "ADDERFUNC" 7 | // It's an alias for a function pointer 8 | // The function on which this type can be 9 | // used is - 10 | // such a function that takes 1 int parameter 11 | // and return an int value 12 | // 13 | typedef int (*ADDERFUNC)(int); 14 | 15 | 16 | //Each of the functions below match 17 | //the ADDERFUNC type which needs a 18 | //function that takes 1 int and returns 19 | //1 int 20 | // 21 | //That's why, each of these functions can 22 | //be pointed to by the ADDERFUNC type's variable 23 | int oneAdder(int x) { 24 | return x + 1; 25 | } 26 | 27 | int tenAdder(int x) { 28 | return x + 10; 29 | } 30 | 31 | int hundredAdder(int x) { 32 | return x + 100; 33 | } 34 | 35 | int thousandAdder(int x) { 36 | return x + 1000; 37 | } 38 | 39 | 40 | int main(void) { 41 | //Use new type that we've defined. 42 | //Because its a function pointer type, 43 | //the variable can be the name of the function 44 | ADDERFUNC pOneAdderFunc = oneAdder; 45 | 46 | int input = 10; 47 | int result = pOneAdderFunc(input); 48 | printf("1 Adder func: input = %d, result= %d\n", input, result); 49 | 50 | 51 | //Another function pointer varible pointing to the function that adds 1000 52 | ADDERFUNC pThousandAdderFunc = thousandAdder; 53 | input = -1000; 54 | result = pThousandAdderFunc(input); 55 | printf("1000 Adder func: input = %d, result= %d\n", input, result); 56 | 57 | 58 | //Create a new type for the structure that stores all the function pointers 59 | //Here, we've created 2 new types 60 | // 1. ADDERFUNCS - a struct 61 | // 2. PADDERFUNC - Pointer to the struct adderFuncs 62 | typedef struct adderFuncs { 63 | ADDERFUNC oneAdder; 64 | ADDERFUNC hundredAdder; 65 | ADDERFUNC thousandAdder; 66 | } ADDERFUNCS, 67 | *PADDERFUNCS; 68 | 69 | struct adderFuncs allAdders = { oneAdder, hundredAdder, thousandAdder }; 70 | PADDERFUNCS pAdderFunctions = &allAdders; 71 | 72 | //Invoke each adder through a pointer type 73 | input = 999; 74 | result = pAdderFunctions->oneAdder(input); 75 | printf("1 Adder func: input = %d, result= %d\n", input, result); 76 | 77 | input = 999; 78 | result = pAdderFunctions->hundredAdder(input); 79 | printf("100 Adder func: input = %d, result= %d\n", input, result); 80 | 81 | input = 999; 82 | result = pAdderFunctions->thousandAdder(input); 83 | printf("1000 Adder func: input = %d, result= %d\n", input, result); 84 | 85 | 86 | //Done 87 | return 0; 88 | 89 | } -------------------------------------------------------------------------------- /HandsOnLabSessions/AdvancedConcepts/MultiFileCPrograms/ExplanatoryNotes.txt: -------------------------------------------------------------------------------- 1 | This is NOT a C Program. 2 | 3 | //Multi-file C Program. 4 | 5 | 6 | In large production system, any program is made up of multiple source 7 | files. For example, in case of C, writing all the C code for the program 8 | inside a single C source file - 9 | * will make the same source file very long 10 | * will make the code unreadable by others 11 | * will make it very difficult to maintain the code over a longer duration 12 | * if a single bug needs to be fixed, it will be very difficult to find out 13 | where it could be, because the file is too large 14 | 15 | Therefore, any reasonably sized production application will always be composed 16 | of multiple C files. 17 | 18 | Because now the C code is divided into multiple C files, many Qs arise - 19 | 1) How will each file get some common type names, #define values, any structs 20 | that may be required across the whole application? 21 | 2) How can we build such a program which is now comprised of multiple C files? 22 | 23 | 24 | For 1) - we can define one OR more application-specific header files which 25 | include the #defines/structs/typedefs that are required within each source file. 26 | The function prototypes/signatures of all the functions are also stored in such an 27 | app specific header file. 28 | This app-specific header file now can be included with #include "xxx" OR 29 | #include in each of the source files such that each source file can now see 30 | common elements that can be reused. 31 | 32 | For 2) - the GCC compiler (and all other compilers too) take in multiple C files 33 | on their command line. If it gets more than one C file to compile, it gladly compiles 34 | each C source files and links their object code to create a single executable that can 35 | then be executed. 36 | For example: If we've file1.c, file2.c, file3.c for our program, then, 37 | compile as : gcc -o MyProgram file1.c file2.c file3.c 38 | This command will now compile file1.c, file2.c and file3.c and the output will be 39 | stored into a file named "MyProgram" which would be an executable file, if no error 40 | occurred during the compilation process. 41 | 42 | 43 | ----- End of the Explanatory Note ------- -------------------------------------------------------------------------------- /HandsOnLabSessions/AdvancedConcepts/MultiFileCPrograms/File1.c: -------------------------------------------------------------------------------- 1 | //File1.c - 1st file in the multi-file C Program. 2 | #include //Standard header 3 | #include //C-String functions 4 | #include "MyHeader.h" //Application's own header file 5 | 6 | 7 | //Implementation of example1Func 8 | void example1Func(char* msg) { 9 | //This function is going to write it's own message and then 10 | //chain it to the example2Func 11 | 12 | //__FILE__ is a standard preprocessor macro that the compiler 13 | //understandard. It automatically replaces it with the name 14 | //of the file. For example, in this file, __FILE__ will be 15 | //replaced by "File1.c" which is the name of this file. 16 | 17 | printf("*** Inside %s: example1Func : {%s} ***\n", __FILE__, msg); 18 | 19 | //chain to example2Func (implemented in File2.c) 20 | example2Func(msg); 21 | 22 | //Done 23 | return; 24 | } 25 | 26 | //A test function which is called from code within File3.c 27 | void testFunc(char* msg) { 28 | printf("*** Inside %s: testFunc : {%s} ***\n", __FILE__, msg); 29 | 30 | //Done 31 | return; 32 | } 33 | 34 | 35 | //The overall main function 36 | //Only 1 function per C Program 37 | //But this C Program contains multiple C files (File1.c/File2.c/File3.c) 38 | //Code within each file is accessible to other code in other files and 39 | //the functions can call each other. 40 | int main(void) { 41 | 42 | //Get the ball rolling.. 43 | //Invoke example1Func (same file: File1.c) 44 | // -- it will invoke example2Func (in File2.c) 45 | // -- it fill invoke example3Func (in File3.c) 46 | // -- it will invoke testFunc(again implemented in this file: File1.c) 47 | example1Func("Message from the main() function."); 48 | 49 | 50 | //Use other items defined in the application header file 51 | printf("Weeks Per day are: %d\n", NUM_DAYS_PER_WEEK); 52 | 53 | struct RomanEmperor caligula; 54 | strcpy(caligula.fullName, "Gaius Caesar Germanicus"); 55 | caligula.emperorSeqNum = 4; 56 | caligula.regnalYearStart = 37; //37AD 57 | caligula.regnalYearEnd = 41; //41AD 58 | 59 | PROMANEMPEROR pEmperor = &caligula; 60 | printf("The Roman Emperor %s was %dth emperor who reigned between %d and %d\n", 61 | pEmperor->fullName, 62 | pEmperor->emperorSeqNum, 63 | pEmperor->regnalYearStart, 64 | pEmperor->regnalYearEnd); 65 | 66 | 67 | //Done 68 | return 0; 69 | } -------------------------------------------------------------------------------- /HandsOnLabSessions/AdvancedConcepts/MultiFileCPrograms/File2.c: -------------------------------------------------------------------------------- 1 | //File2.c - 2nd file in the multi-file C Program. 2 | #include //Standard header 3 | #include "MyHeader.h" //Application's own header file 4 | 5 | //Implementation of example2Func 6 | void example2Func(char* msg) { 7 | //This function is going to write it's own message and then 8 | //chain it to the example3Func 9 | 10 | printf("*** Inside %s: example2Func : {%s} ***\n", __FILE__, msg); 11 | 12 | //chain to example3Func (implemented in File3.c) 13 | example3Func(msg); 14 | 15 | //done 16 | return; 17 | } -------------------------------------------------------------------------------- /HandsOnLabSessions/AdvancedConcepts/MultiFileCPrograms/File3.c: -------------------------------------------------------------------------------- 1 | //File3.c - 3rd file in the multi-file C Program. 2 | #include //Standard header 3 | #include "MyHeader.h" //Application's own header file 4 | 5 | //Implementation of example3Func 6 | void example3Func(char* msg) { 7 | //This function is going to write it's own message. 8 | //But will invoke another test function implemented 9 | //within File1.c 10 | 11 | printf("*** Inside %s: example3Func : {%s} ***\n", __FILE__, msg); 12 | 13 | //call a function implemented inside File1.c 14 | testFunc(msg); 15 | 16 | //done 17 | return; 18 | } -------------------------------------------------------------------------------- /HandsOnLabSessions/AdvancedConcepts/MultiFileCPrograms/MyHeader.h: -------------------------------------------------------------------------------- 1 | #ifndef _MYHEADER_H_ 2 | #define _MYHEADER_H_ 3 | 4 | //Common definitions go in the application's header file 5 | //These common definitions are manifest constants (#defines), 6 | //struct types, typedef declarations, function prototypes 7 | 8 | 9 | //Manifest Constants 10 | #define MONDAY 1 11 | #define TUESDAY 2 12 | #define NUM_DAYS_PER_WEEK 7 13 | 14 | 15 | //Struct Definitions 16 | struct RomanEmperor { 17 | char fullName[35]; //full name of the emperor, 34 chars, 1 for NULL 18 | int emperorSeqNum; //emperor # 19 | int regnalYearStart; //when did his reign start? 20 | int regnalYearEnd; //when did his reign end? 21 | }; 22 | 23 | 24 | //Typedefs 25 | typedef struct RomanEmperor *PROMANEMPEROR; 26 | 27 | 28 | //Common function prototypes/signatures 29 | void example1Func(char*); //will be implemented in File1.c 30 | void example2Func(char*); //will be implemented in File2.c 31 | void example3Func(char*); //will be implemented in File3.c 32 | void testFunc(char*); //File1.c function 33 | 34 | 35 | 36 | #endif -------------------------------------------------------------------------------- /HandsOnLabSessions/AdvancedConcepts/RawBufferManipulation.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | //In this program, I want to show - 5 | // - given a buffer (array) of bytes, 6 | // how you can access it with a single 7 | // pointer. 8 | // - this pointer can be cast into other 9 | // pointer types 10 | // - When we access the memory block with 11 | // certain type of pointer, the bytes 12 | // that get read/written are based on 13 | // the pointer type. 14 | // 15 | // We start with a raw char buffer of [25]; 16 | // - Then, we store "Marcus Aurelius" (16 = 15 + 1 NULL byte) 17 | // - Then, we move our pointer past the NULL byte 18 | // and store at integer value 15 (4 bytes) 19 | // - Then we move past those 4 bytes and store 2 short 20 | // elements - 161 and 180, each taking 2 bytes. 21 | // - total of 25 bytes thus - 22 | // - 16 bytes name (with a NULL termination) 23 | // - 4 bytes (int) with the value 15 24 | // - 2 bytes (short) with the value of 161 25 | // - 2 bytes (short) with the value of 180 26 | // - 1 byte (char) set to 0xFF 27 | 28 | int main(void) { 29 | //Buffer pointer, initially does not point anywere, its NULL 30 | void* pMem = NULL; 31 | 32 | //Buffer of 25 bytes 33 | //Local var, contents all random 34 | char buffer[25]; 35 | 36 | //Point pointer to buffer. 37 | pMem = (void*)(&buffer[0]); 38 | 39 | //Step 1: Store "name" 40 | //We need to convert pMem to a char* and then do strcpy 41 | strcpy((char*)pMem, "Marcus Aurelius"); 42 | 43 | //Step 2: Move pMem to the byte that follows the NULL byte at the end of the name 44 | //Because we typecast a pointer to (char*) and then add 16, it will move by 45 | //16 * sizeof(char) = 16 bytes, thus pointing to a byte that follows the NULL termination 46 | pMem = (char*)pMem + 16; 47 | 48 | //Step 3: Now store an int value of 15 49 | //Now, the same pMem can be cast into an (int*). 50 | //When we now store the value of 15 with an int*, 4 bytes (sizeof int) will get affected 51 | *((int*)pMem) = 15; 52 | 53 | //Step 4: Move past an int 54 | pMem = (int*)pMem + 1; 55 | 56 | //Step 5: Now store 1st short value 57 | //Cast the pointer to short* so that only 2 bytes would get affected 58 | *((short*)pMem) = 161; 59 | 60 | //Step 6: Move ahead by 2 bytes 61 | pMem = (short*)pMem + 1; 62 | 63 | //Step 7: Store next short value 64 | //Same as Step 5 65 | *((short*)pMem) = 180; 66 | 67 | //Step 8: Move ahead by 2 bytes 68 | pMem = (short*)pMem + 1; 69 | 70 | //Step 9: We are at the last byte of our 25 byte buffer 71 | //Using (char*) store the byte 0xFF there 72 | *((char*)pMem) = 0xFF; 73 | 74 | 75 | //Step 10; 76 | //Use LLDB/GDB and monitor each step in which we use a single 77 | //pointer to keep updating the underlying memory block of bytes 78 | //affecting different sizes every time. 79 | 80 | 81 | //Done 82 | return 0; 83 | } -------------------------------------------------------------------------------- /HandsOnLabSessions/AdvancedConcepts/Typedefs.c: -------------------------------------------------------------------------------- 1 | /* Short explanatory note */ 2 | 3 | // Typedefs are "C"s way to create new types of short-hands 4 | // With built-in types, such as, int, float, double, char, short, 5 | // and their pointer variants, the C language allows you to create 6 | // your *own*, *custom* types. 7 | // 8 | // The keyword "typedef" must be used to achieve this. 9 | // 10 | // For example, in your program, you are dealing exclusively with 11 | // "unsigned short" types. Instead of declaring each variable with 12 | // unsigned short in front of it, you can define a new type called 13 | // USHORT as an alias (different name) to the unsigned short. 14 | // With that, each unsigned short declaration now could be changed 15 | // to simply USHORT 16 | // 17 | // C Syntax 18 | // typedef unsigned short USHORT; 19 | // 20 | // unsigned short myVar1; //OK 21 | // USHORT myVar2; //OK too, easy to understand and write 22 | // 23 | // USHORT above is still treated as an unsigned short by the compiler 24 | // 25 | // Another example: 26 | // In many GUI programming systems, Window objects can be created which 27 | // represent the rectangular shape on the screen. Most of these systems 28 | // work internally with some sort of data structure that represent Window's 29 | // size, its location on the screen, it's title, etc. 30 | // Instead of letting user to peep into this internal structure, the 31 | // GUI programming functions convert pointer to this internal structure into 32 | // an opaque type (int). C "typedef" can be used to create a new sensibly 33 | // named type called "HANDLE" to convey that meaning clearly 34 | // 35 | // C Syntax 36 | // typedef int HANDLE; 37 | // 38 | // /* Assume a fictitous function to create a window on the screen 39 | // that takes in title, (x, y) location and (w, h) size 40 | // */ 41 | // HANDLE windowHandle = createWindow("Learning C", 100, 100, 500, 500); 42 | // 43 | // HANDLE is a new type but internally compiler still treats it as an int 44 | // 45 | // ================= End of explanatory note on typedefs ================== 46 | 47 | #include 48 | 49 | typedef USHORT unsigned short; 50 | 51 | 52 | int main(void) { 53 | unsigned short myVar1 = 10; 54 | USHORT myVar2 = 20; 55 | 56 | //USHORT can be used as a type in place of "unsigned short" 57 | } 58 | 59 | -------------------------------------------------------------------------------- /HandsOnLabSessions/ArraysLab/Array2D.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(void) { 4 | //Step 1: Define a simple 2D array of ints 5 | int matrix[2][2]; 6 | 7 | //How are the elements accessed 8 | //2 indices that are indepdent of each other 9 | // | matrix[0][0] | matrix[0][1] | 10 | // | matrix[1][0] | matrix[1][1] | 11 | // 12 | 13 | //Step 2: Initialize the 2D array by accessing each element independently 14 | matrix[0][0] = 10; 15 | matrix[0][1] = 20; 16 | matrix[1][0] = 30; 17 | matrix[1][1] = 40; 18 | 19 | 20 | //Step 3: How are the elements accessed in the 2D array? 21 | // Use 2 separate indices 22 | printf("---- Accessing random elements in the 2D array ------\n"); 23 | printf(" Row 0, Col 1: %d\n", matrix[0][1]); 24 | printf(" Row 1, Col 0: %d\n", matrix[1][0]); 25 | 26 | //Step 4: How does the 2D Array look in memory? 27 | //How is it stored? 28 | // Are the rows stored first and then the columns OR 29 | // Are the columns stored first and then the rows? 30 | //This is important because computer memory is linear - bytes follow 31 | //each other. Its 1-dimensional. So how are the 2D arrays stored? 32 | printf("---- How the arrays are stored in memory?\n"); 33 | printf("Row 0 is at address: %p\n", &matrix[0]); 34 | printf("Row 0, Col 1 is at address: %p\n", &matrix[0][1]); 35 | printf("Row 1 is at address: %p\n", &matrix[1]); 36 | printf("Row 1, Col 1 is at address: %p\n", &matrix[1][1]); 37 | 38 | //Step 5: Understand the output of Step 4 from the printed addresses 39 | // Exercise: What do you think on how the array is stored? 40 | 41 | 42 | //Step 6: Verify your Step 5 answer with the help of GDB/LLDB 43 | // Take the array dump and ascertain your observations. 44 | 45 | //Step 7: How do you iterate such a 2D array? 46 | //ANSWER: Use 2 indices 47 | for(int i=0; i<2; i++) { 48 | for (int j=0; j<2; j++) { 49 | printf("[%d, %d] = %d\n", i, j, matrix[i][j]); 50 | } 51 | } 52 | 53 | 54 | //Done 55 | return 0; 56 | 57 | } -------------------------------------------------------------------------------- /HandsOnLabSessions/ArraysLab/ArrayIndexing.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(void) { 4 | //Step 1: Declare an int array 5 | int salaries[3] = { 1000, 2000, 3000 }; 6 | 7 | //Step 2: 8 | //What would happen if you use *invalid* array index? 9 | //You get a warning from the compiler 10 | //but compiler does not throw an error and not let you 11 | //proceed (unless you ask the compiler to treat any warning 12 | //as an error). 13 | //You can access it and the wrong value printed out 14 | printf("4th salary is: %d\n", salaries[3]); 15 | 16 | //Step 3: What is the compiler's view of the array 17 | //array indexing with 0, 1, 2, etc is for the programmer 18 | //convenience. What's happening under-the-hood? 19 | // 20 | //Verify - 21 | // Once the addresses are printed, 22 | // 1. Check if they are *contiguous* in memory 23 | // because arrays are contiguous 24 | // 2. Each address is 4-bytes apart. This is so 25 | // because this is an int-array. Each element is 4 bytes 26 | printf("--- Address of each element in the array ----\n"); 27 | printf("Index 0: %p\n", &salaries[0]); 28 | printf("Index 1: %p\n", &salaries[1]); 29 | printf("Index 2: %p\n", &salaries[2]); 30 | 31 | //Step 4: Can I access the array without index? 32 | //Sure. You know that the array is stored in memory 33 | //4-bytes apart. If you take a pointer to the 1st element 34 | //and increment it, we can move to the next element 35 | //Pointer Arithmetic: 36 | // When you apply arithmetic operations such as +, ++, 37 | // -, --, * etc, pointer is advanced or decremented based 38 | // on the type of the pointer 39 | //This allows us to access the array element with just a 40 | //pointer without indexing 41 | int *pElement; 42 | pElement = &salaries[0]; //Now we've set a pointer at the 1st array index 43 | printf("Using Pointer, 0th index has: %d\n", *pElement); 44 | 45 | //Increment the pointer 46 | //Because the pointer is an int*, ++ will move it ahead by 47 | //the sizeof(int) => 4 48 | pElement++; //We're now at index 1 49 | printf("Using Pointer, 1st index has: %d\n", *pElement); 50 | 51 | //Take the last using a similar method 52 | pElement++; //We're now at index 2 53 | printf("Using Pointer, 1st index has: %d\n", *pElement); 54 | 55 | //What would happen if we continue incrementing? 56 | //Obviosuly, the pointer does not know anything about array bounds. 57 | //It will merrily move ahead and read next 4 bytes and print 58 | //that data that does not really belong to the array. 59 | pElement++; //We're now at index 1 60 | printf("Using Pointer, 1st index has: %d\n", *pElement); 61 | 62 | 63 | //Done 64 | return 0; 65 | } -------------------------------------------------------------------------------- /HandsOnLabSessions/ArraysLab/ArrayModifying.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(void) { 4 | //Step 1. Define a simple array of 'chars' 5 | // Char is a 8-bit (1 byte) type 6 | // Initialize it immediately 7 | char name[4] = {'J', 'O', 'H', 'N'}; 8 | 9 | 10 | //Step 2. Print all characters in the name 11 | //NOTE : This is simply being treated as a bunch/arrat of charatcers 12 | // This is NOT a string (it's not NULL terminated) 13 | printf("----- Printing each char in the name array -----\n"); 14 | for(int i=0; i<4; i++) { 15 | printf("%c ", name[i]); //no newline and a space after each char 16 | } 17 | printf("\n"); 18 | 19 | 20 | //Step 3: Where is the array in memory? 21 | printf("name array is at %p in memory.\n", name); 22 | printf("name array's 1st element is at %p in memory.\n", &name[0]); 23 | 24 | 25 | //Step 4: Run GDB/LLDB and using the array address printed, 26 | // take a memory dump and make sure all chars ('J'...) are seen. 27 | //Refer: https://www.cs.cmu.edu/~pattis/15-1XX/common/handouts/ascii.html 28 | // Note the ASCII codes for 'J', 'O', 'H', 'N' and make sure you 29 | // can see them in memory (J = 74, O = 79, H = 72, N = 78). In Hex 30 | // these values will be 0x4A, 0x4F, 0x48, 0x4E 31 | // 32 | 33 | //Step 5: 34 | //How to modify each array element? 35 | //Problem - We want to convert 'JOHN' to 'john' 36 | //Solution - 37 | // Get the ASCII codes for 'j', 'o', 'h', 'n' 38 | // They are - 106, 111, 104, 110 39 | // Comapring with CAPS - 74, 79, 72, 78 40 | //What's the pattern? 41 | // Each smallcase letter is 32 away from his uppercase counterpart 42 | //Therefore, if we visit each array element and add 32 to it, we will get 43 | // the ASCII lowercase for that letter 44 | printf("-----Now, converting each character to its lowercase (by adding 32)----\n"); 45 | for(int i=0; i<4; i++) { 46 | name[i] += 32; //shorthand for name[i] = name[i] + 32; 47 | } 48 | printf("----After the conversion....---\n"); 49 | for(int i=0; i<4; i++) { 50 | printf("%c ", name[i]); 51 | } 52 | printf("\n"); 53 | 54 | 55 | //Step 6: Using GDB/LLDB, repeat step 4 56 | //The memory now show contain all lowercase letter codes 57 | // 'J' (0x4A or 74) should now be 'j' (106, 0x6A) and so on. 58 | 59 | //Done 60 | return 0; 61 | } -------------------------------------------------------------------------------- /HandsOnLabSessions/ArraysLab/ArrayOfStrings.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(void) { 4 | //Step 1: First, define a bunch of strings 5 | // All Strings are statically intialized. 6 | // That way, they get allocated in a "static" memory 7 | // block by the compiler 8 | char *str1 = "Julius Caesar "; 9 | char *str2 = "conqured Gaul "; 10 | char *str3 = "and became the first Roman Emperor "; 11 | char *str4 = "only to die 2 years later in 44BC."; 12 | 13 | 14 | 15 | //Step2 : Now define an array of char* (char pointers). 16 | // Make each element (char*) to point to the strings defined above. 17 | // ------ ---------------------------------------- 18 | // | 0 ----|-----> 'J', 'u', 'l', ..... '\0' | 19 | // | 1 ----|-----> 'c', 'o', 'n', 'q', ... '\0' | 20 | // | 2 ----|-----> 'a', 'n', 'd', ...... '\0' | 21 | // | 3 ----|-----> 'o', 'n', 'l', ....... '\0' | 22 | // ---------------------------------------------- 23 | char *stringArray[4] = { 24 | str1, 25 | str2, 26 | str3, 27 | str4 28 | }; 29 | 30 | printf("======================\n"); 31 | 32 | //Step 3: What happens if select a random legal index in the array? 33 | printf("3rd element (index 2): %s\n", stringArray[2]); 34 | 35 | printf("\n"); 36 | 37 | //Step 4: Visit every array element and print a string pointed to 38 | // by that char* 39 | for(int i=0; i<4; i++) { 40 | printf("Element %d (index %d): %s\n", i+1, i, stringArray[i]); 41 | } 42 | 43 | printf("======================\n"); 44 | 45 | 46 | //Done 47 | return 0; 48 | 49 | } -------------------------------------------------------------------------------- /HandsOnLabSessions/ArraysLab/ArraysBasic.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(void) { 4 | //Step 1. Simple int array of 5 elements, only declared 5 | int yearsOfInterest[5]; 6 | 7 | //Step 2. You can initialize array elements separately 8 | //array elements are accessed with their index position 9 | //In C, array indices are 0-relative. That is, the starting index is 0 10 | //and the last index is (size-of-the-array-1) 11 | //In this example, there are 5 elements in the array. 12 | //Valid indices are 0...4 13 | yearsOfInterest[0] = 1947; 14 | yearsOfInterest[1] = 1948; 15 | yearsOfInterest[2] = 1961; 16 | yearsOfInterest[3] = 1965; 17 | yearsOfInterest[4] = 1971; 18 | 19 | //Step 3, Simply print each element individually 20 | printf("------ Array element: individual access ------\n"); 21 | printf("Element 1, index 0 = %d\n", yearsOfInterest[0]); 22 | printf("Element 2, index 1 = %d\n", yearsOfInterest[1]); 23 | printf("Element 3, index 2 = %d\n", yearsOfInterest[2]); 24 | printf("Element 4, index 3 = %d\n", yearsOfInterest[3]); 25 | printf("Element 5, index 4 = %d\n", yearsOfInterest[4]); 26 | 27 | //Step 4, if you want process each array element, iterating 28 | //over the array is what you need. 29 | //You can use for/do..while/while loops to do it. 30 | printf("------- Iterating over the array with for loop --------\n"); 31 | for(int i=0; i<5; i++) { 32 | printf("Element %d, index %d = %d\n", i+1, i, yearsOfInterest[i]); 33 | } 34 | 35 | printf("-------- Observing Array in memory -------\n"); 36 | //Step 5, see the array in memory 37 | //Where is it located in the memory? 38 | //Two ways to do it!! 39 | // 1. Use only the array name 40 | // 2. Use the pointer to the first array element 41 | printf("Array is at address %p in memory.\n", yearsOfInterest); 42 | printf("Array is at address %p in memory.\n", &yearsOfInterest[0]); 43 | 44 | printf("------- Time to open the LLDB/GDB and check out the memory to locate it.\n"); 45 | //For Windows 46 | // prompt>gcc -g -o ArraysBasic.exe ArraysBasic.c 47 | // prompt>gdb ArrayBasic.exe 48 | // 49 | //For Mac 50 | // $>gcc -g -o ArraysBasic ArraysBasic.c 51 | // $>lldb ArrayBasic 52 | // ..... lldb commands ---- 53 | //Verify: 54 | // - All 5 elements in the array with the values 55 | // - Address of the 1st element 56 | // - Address of each element should be 4 apart because this is an int array 57 | // and each int takes 4 bytes 58 | 59 | 60 | //Done 61 | return 0; 62 | 63 | 64 | } -------------------------------------------------------------------------------- /HandsOnLabSessions/ArraysLab/CommandLineArgs.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | //We've always used main as a function that does not 4 | //take parameters 5 | //But in reality, there are 3 different ways in which it can be written 6 | // 7 | //1. No parameters - int main(void) { ..... } 8 | //2. argc, argv - int main(int argc, char* argv[]) { .... } 9 | //3. argc, argv, envp - int main(int argc, char *argv[], char* envp[]) {...} 10 | 11 | //NOTES - 12 | // Because main() is an *official* entry point and a mandatory function 13 | // that a C executable program (not a library program) must write, there 14 | // must be a way to pass in input parameters to the program when its run. 15 | // 16 | // For example - where a program that simply prints a hardcoded string 17 | // "hello world" runs, it only prints this hardcoded string and unless its 18 | // changed and program compiled, it will print the same string over and over 19 | // 20 | // How do you pass in any parameter to the main() function itself? 21 | // ANS: You can do this using a command line 22 | // 23 | // USAGE: executable_program param1 param2 param3..... 24 | // 25 | // Here, param1, param2, param3 are called the command line arguments 26 | // User can pass-in any number of these argument which are SPACE-separated. 27 | // 28 | // Inside the main function: 29 | // int argc - receives how many arguments are passed-in 30 | // char* argv[] - parameter recevies all these inputs parameters 31 | // - This array's indices will range from 0 to argc - 1 32 | // char* envp[] - receives all the ENV (environment) settings 33 | // and its rarely used 34 | 35 | 36 | int main(int argc, char* argv[], char* envp[]) { 37 | 38 | printf("=========================\n"); 39 | //Step 1: Use argc and see how many command line args were provided 40 | printf("I received %d arguments.\n", argc); 41 | 42 | //Step 2: Loop over all the command line args 43 | //argv[] array will start from 0...argc-1 44 | //0th argument is always the name of the program 45 | for(int i=0; i 2 | #include //For atoi() 3 | 4 | //SOME Best PRACTICES RELATED TO THE COMMAND LINE ARGS 5 | // Inside the main function - 6 | // Validate your program's input immediately by validating 7 | // your argc, argv 8 | // If the command line arguments are valid, proceed. Otherwise, 9 | // return an error code. 10 | // 11 | // CONVENTION: Return 0 from main to indicate SUCCESS 12 | // Return various -ve codes to indicates various types of ERRORS 13 | // 14 | // In this example - 15 | // We *expect* 2 arguments - 16 | // the first indicates a string to be printed 17 | // the second indicates a repeat count - these many times the string will be 18 | // printed. 19 | // 20 | // USAGE: CommandLineArgs2 hello 4 21 | // With this command line, program will output the string "hello" 4 times 22 | // 23 | int main(int argc, char* argv[]) { 24 | //Validate inputs 25 | //argc MUST be 3 (name of the progranm + string + repeatCount) 26 | //if we don't get that much, report a USAGE string and return an error -1 27 | 28 | if (argc != 3) { 29 | printf("USAGE: CommandLineArgs2 stringToPrint repeatCount\n"); 30 | return -1; //Indicates an error code which means something wrong with number 31 | //of arguments 32 | } 33 | 34 | //argc is 3 which is good 35 | //but now check if argv[2] - repeatCount is acually a number 36 | //When its passed-in, its a number but passed-in as string 37 | //Use atoi() stdlib function -> See #include above 38 | // 39 | //This function takes in a string and returns a number if conversion 40 | //was successful. Else it will return 0 41 | // 42 | //For example: atoi("3") will return 3 (int) 43 | // atoi("three") will return 0 (fail) 44 | // 45 | //Validate and return errorCode -2 to indicate this error 46 | 47 | int repeatCount = atoi(argv[2]); 48 | if (repeatCount == 0) { 49 | //We could not convert 50 | printf("Please provide a numeric repeatCount argument.\n"); 51 | return -2; 52 | } 53 | 54 | //All well. We have a string (argv[1]) and repeatCount. 55 | printf("===== Repeating %s %d times. =======\n", argv[1], repeatCount); 56 | for(int i=0; i 2 | 3 | int main(void) { 4 | //Step 1: Define a String and intialize individual chars 5 | char fname[5] = {'J', 'O', 'H', 'N', 0}; //place a NULL byte manually 6 | char lname[4] = {'D', 'O', 'E', 0}; //place a NULL byte manually 7 | 8 | 9 | //Step 2: Print the names and locate their location in memory 10 | printf("fname is %s at %p.\n", fname, &fname[0]); 11 | printf("lname is %s at %p\n", lname, &lname[0]); 12 | 13 | //Step 3: What can you derive from the addresses printed? 14 | //Because we didn't statically initialize the string as in - 15 | // char *fname = "John" 16 | //compiler now creates space of the string chars on local stack 17 | //Now, lname appears first and then fname in memory. 18 | // Verify this with GDB/LLDB 19 | // - Also verify NULL termination in memory. 20 | // - We manually put the NULL byte because we initialized the array element-wise. 21 | 22 | //Step 4: If we modify the 4th byte of lname which is NULL and then printf it, 23 | // what will happen? 24 | //IN this example's case, because fname follows lname in memory and lname's NULL 25 | // byte has gone, printf("%s"..) will search and print all chars until it finds 26 | // a NULL byte. Therefore, we should see lname and fname both printed. 27 | 28 | printf("Before NULL byte modified by *, lname is: %s\n", lname); 29 | lname[3] = '*'; 30 | printf("After NULL byte modified by *, lname is: %s\n", lname); 31 | 32 | //Step 5: 33 | // Student Exercise 34 | // Instead or progrmatically modifying the NULL byte, use GDB/LLDB 35 | // to modify it and try your program. 36 | 37 | 38 | //Done 39 | return 0; 40 | } -------------------------------------------------------------------------------- /HandsOnLabSessions/ArraysLab/StringsAsArrays.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(void) { 4 | //GENERAL: 5 | // C Strings have these 2 characteristics 6 | // 1. They are arrays of chars (8-bit elements) 7 | // 2. They are NULL termiated, 0-byte as the last element in the array. 8 | 9 | 10 | //Step 1. Define a static C string. 11 | //Compiler collects such statically intialized strings and puts 12 | //then together in a separate memory segment. 13 | //Such strings follow above mentioned 2 characteristics 14 | 15 | char *fname = "John"; 16 | char *lname = "Doe"; 17 | 18 | 19 | //Step 2. See where these strings are in memory. 20 | printf("fname is %s and is at %p in memory.\n", fname, &fname[0]); 21 | printf("lname is %s and is at %p in memory.\n", lname, &lname[0]); 22 | 23 | //Step 3. Now that we have printed the addresses, work with GDB/LLDB 24 | // and dump the memory to make sure we can locate these strings there. 25 | // --- Student Exercise ---- 26 | 27 | 28 | //Step 4. What do you find? 29 | // In this example, 2 static strings are placed by the compiler 30 | // in memory right next to each other 31 | //NOTE: 32 | // Make sure to see the NULL termination for each string 33 | // Without this NULL terminations, C strings would be just an 34 | // array of chars. Only NULL termination makes them strings truly. 35 | 36 | 37 | //Done 38 | return 0; 39 | } -------------------------------------------------------------------------------- /HandsOnLabSessions/DynamicMemoryLab/AllocatingStructs.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include //atoi 3 | #include //strcpy/strncpy 4 | 5 | struct Person { 6 | char fname[25]; 7 | char lname[25]; 8 | int age; 9 | }; 10 | 11 | 12 | //In this program, we want to create "n" 13 | //number of Person structs. "n" is the number 14 | //provided to the program via command line arguments. 15 | //For example: if it's 5, we will create 5 person 16 | //structs, each initialized with random fname, lname 17 | //and age 18 | // 19 | //Because this number is coming into the progra via 20 | //the command line, compiler won't have any idea about 21 | //how many Person structs does the program have. This 22 | //means that this is not a "static" allocation case but 23 | //needs a heap memory (dynamic memory). 24 | // 25 | int main(int argc, char* argv[]) { 26 | 27 | //Step 1: Verify we recevied argc as 2: 28 | // argv[0] -> Program name 29 | // argv[1] -> "n" - number of Person structs to create dynamically 30 | if (argc != 2) { 31 | fprintf(stderr, "USAGE: AllocatingStructs .\n"); 32 | return -1; 33 | } 34 | 35 | //argc check passed. 36 | //Convert argv[1] to an int 37 | int numPersonsToCreate = atoi(argv[1]); 38 | if(numPersonsToCreate == 0) { 39 | //atoi() could not covert "string" into an "int" 40 | fprintf(stderr, "specified number %s could not be converted into a number.\n", 41 | argv[1]); 42 | return -2; 43 | } 44 | 45 | //everything OK. 46 | //Allocate place for "n" Person structs on heap (ie. using malloc/calloc) 47 | 48 | //Total mem we need is -> sizeof the Person struct * number of such Person structs 49 | int numTotalBytesToAllocate = numPersonsToCreate * sizeof(struct Person); 50 | 51 | //Note the malloc() call here. 52 | //malloc returns a (void *) -> Pointer to a void 53 | //The expectation is that we *cast* the returned pointer into a suitable pointer type 54 | //Because we want to use this memory to store "n" Persons, we *cast* the 55 | //returned pointer into a (struct Person*) type. 56 | struct Person *pPerson = (struct Person*)malloc(numTotalBytesToAllocate); 57 | if (pPerson == NULL) { 58 | //malloc failed, we can't proceed. 59 | //Report an error and return/exit 60 | fprintf(stderr, "Could not allocate enough memory for %d Persons.\n", numPersonsToCreate); 61 | return -3; 62 | } 63 | 64 | //If here, it means that memory was allocated and we can use it 65 | //to store out information. 66 | // 67 | //First make a copy of the pPerson pointer so that the copy can be 68 | //used for the pointer arithmetic and original pPerson can be used 69 | //to free the memory once done. 70 | struct Person *pPersonCopy = pPerson; 71 | char fnameBuffer[25]; 72 | char lnameBuffer[25]; 73 | for(int i=0; ifname, fnameBuffer); 83 | strcpy(pPersonCopy->lname, lnameBuffer); 84 | //Set the age into the Person structure 85 | pPersonCopy->age = i + 20; 86 | 87 | //Done with one Person, move to the next one 88 | //using pointer arithmetic. 89 | //When we move pPersonCopy by one, it means 90 | //move in memory by sizeof(Person) because pPersonCopy 91 | //is Person* 92 | pPersonCopy++; 93 | } 94 | 95 | 96 | //Iterate over the allocated mem block of Person structures 97 | //and print the info we stored there. 98 | //pPersonCopy needs to be restored because it was incremented 99 | //in the earlier for loop. 100 | pPersonCopy = pPerson; 101 | for(int i=0; ifname); 104 | printf("\tlname: %s\n", pPersonCopy->lname); 105 | printf("\tage: %d\n", pPersonCopy->age); 106 | 107 | //Move to the next Person 108 | pPersonCopy++; 109 | } 110 | printf("\n======= DONE. =========\n\n"); 111 | 112 | 113 | //Our job done, free the memory block 114 | free(pPerson); 115 | 116 | //Done 117 | return 0; 118 | } -------------------------------------------------------------------------------- /HandsOnLabSessions/DynamicMemoryLab/DynamicMemSimple.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include //malloc/calloc/free 3 | 4 | 5 | int main(void) { 6 | //Step 1: A plain int* var pointing nowhere 7 | int* pArr = NULL; 8 | 9 | //Step 2: Because at this point, pArr is NULL (not pointing anywhere), 10 | // program can't dereference it. We want it to point it to a memory 11 | // which is dynamically allocated to store 5 elements and store 10, 20 12 | // 30, 40, 50 in it. 13 | pArr = (int*)malloc(20); //20 bytes needed from heap, 5 ints,4 bytes each 14 | 15 | //Step 3: malloc can fail. It will return NULL if that's the case 16 | //We simply exit the program 17 | if (pArr == NULL) { 18 | fprintf(stderr, "Memory allocation failed..exiting...\n"); 19 | return -1; 20 | } 21 | 22 | //Step 4: We got the memory. Use it to store 10, 20, 30, 40, 50 23 | //Then, print it and then because we're done, free it (give it back 24 | //to the heap) 25 | int startValue = 10; 26 | int* pArrCopy = pArr; //create a pointer copy because we need an original pointer 27 | //to be passed to free() 28 | for(int i=0; i<5; i++) { 29 | *pArrCopy = startValue; 30 | 31 | //increment startValue 32 | startValue += 10; 33 | 34 | //increment pointer so that it will point to the memory 4 bytea away 35 | pArrCopy++; 36 | } 37 | 38 | //Now print all the elements to make sure all is OK 39 | //No need to use pArrCopy 40 | //Also, pArr[i] can be used 41 | for(int i=0; i<5; i++) { 42 | printf("Index %d, Value: %d\n", i, pArr[i]); 43 | } 44 | 45 | //Done with using the dynamic memory, free it. 46 | //Pass it the original pointer returned by malloc 47 | free(pArr); 48 | 49 | 50 | //Step 5: RUN under LLDB.GDB 51 | // Observe - how pArr is NULL 52 | // how pArr is set to the ptr (address returned by malloc) 53 | // dump pArr and see if its initialized OR not 54 | 55 | 56 | //Done 57 | return 0; 58 | } -------------------------------------------------------------------------------- /HandsOnLabSessions/DynamicMemoryLab/DynamicStringCpy.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include //malloc/calloc/free 3 | #include //strlen/strcpy 4 | 5 | //Get argc and argv 6 | //Program will look into the command line arguments 7 | // - for each string specified on command line, it will be copied into a 8 | // dynamic memory segment 9 | // - specifying no parameter is fine. In that case, program should display 10 | // a message that no parameter was specified and it was exiting. 11 | 12 | int main(int argc, char* argv[]) { 13 | //Step 1: See if argc ==1 . If it is, no extra params were sent 14 | //Display the message and exit OR return 15 | if(argc == 1) { 16 | //Only the program name was specified, no parameters were specified 17 | printf("No parameters were specified, nothing to do..returning"); 18 | return -1; 19 | } 20 | 21 | //Step 2: For each command line arg, we need memory allocation 22 | //Therefore, we need to know how long each string is. 23 | int totalDynamicMemoryNeeded = 0; 24 | //Start with i = 1 because index 0 has only program name 25 | for(int i=1; i To store all these args, we will need a mem of %d bytes\n", totalDynamicMemoryNeeded); 36 | 37 | //Allocate the needed memory 38 | char *pMem = (char*)calloc(totalDynamicMemoryNeeded, sizeof(char)); 39 | 40 | //Return/Exit if calloc failed 41 | if (pMem == NULL) { 42 | fprintf(stderr, "Memory allocation failed. can't continue...exiting..."); 43 | exit(-2); 44 | } 45 | 46 | //All OK 47 | //store each arg into the allocated memory one at a time 48 | //Create a copy first 49 | char *pMemCpy = pMem; 50 | for(int i=1; i| arr[0] | arr[1] | arr[2] | arr[3] | arr[4] | 31 | ---------------------------------------------- 32 | offset: 0x0 0x04 0x08 0xC 0x10 0x14 33 | 34 | Consider, another typical C-declaration: 35 | char *name = "Julius Caesar"; 36 | 37 | - This is also an example of a static declaration which needs memory to be 38 | allocated at compile time. 39 | - Because this is a C-String declaration, compiler knows that it needs to 40 | take the value, append a NULL-termination and therefore, in case of the above 41 | example, it calculates the length as : length("Julius Caesar") + 1 byte for the 42 | NULL termination = 13 + 1 = 14 bytes 43 | - Memory layout: 44 | ============= 45 | &name[0] OR name |------ a NULL termination 46 | | v 47 | | ------------------------------------------------- 48 | -----> | J | u | l | i | u | s | |.....| r | 0 | 49 | ------------------------------------------------- 50 | offset: 0 1 2 3 4 5 ............ 51 | 52 | 53 | ======================== DYNAMIC MEMORY ================================ 54 | 55 | - In contrast to the static allocation, when compiler can't figure out how much memory 56 | a variable needs at 'compile-time', its the case of a dynamic memory allocation 57 | 58 | - For a C program (and true for almost all types of programs), there are 2 main areas of 59 | of memory 60 | (a) Stacks and 61 | (b) Heaps 62 | 63 | - Stack for a C Program is used for - 64 | (a) Passing parameters to the functions 65 | (b) Setting up call frames on the call-stack (note the word "stack" here) 66 | (c) Setting up local variables on the frame created in (b) above 67 | 68 | - Heap is a general-purpose memory area maintained by C-runtime library 69 | (a) The heap contains garbage and uninitialized memory 70 | (b) Using C library functions, you can ask the C-runtime to provide chunks of 71 | memory of the required size to your program 72 | (c) Because such an allocation happens when the C Program is running, its called as 73 | a dynamic (run-time) allocation as compared to the static allocation at compile 74 | time 75 | (d) Most important C-library functions for dynamic memory allocation are - 76 | (1) void* malloc(int numBytesNeeded); 77 | (2) void* calloc(int numItems, int size); 78 | (3) free(allocatedPtr); 79 | (e) Such dynamic memory allocations are carved out of the heap 80 | 81 | - Conside the code example below - 82 | char *str; 83 | 84 | (a) Its a pointer to char 85 | (b) But where is it pointing to? 86 | ANSWER: NOWHERE. Because we've only declared it, not initialized it. 87 | THIS IS CALLED AS A "DANGLING POINTER" 88 | (c) Dangling pointers are BAD and if dereferenced without initializing them, 89 | they will make your programs crash 90 | (d) So how do we initialize it? 91 | OPTION 1: If you've a static string: such as char *name = "Julius Caesar", 92 | you can use: 93 | str = name; OR str = &name[0]; 94 | 95 | OPTION 2: You point it to a memory chunk which is initialized at run-time. 96 | That is, you allocate some memory and make "str" point to it. 97 | 98 | str ----> [....dynamically allocated mem chunk on heap.....] 99 | 100 | - malloc/calloc/free 101 | (a) Use #include which contains the prototyps/signatures of these functions 102 | (b) malloc 103 | - You need to pass in numBytesNeeded parameter to this function 104 | - The function will then look into the heap and see if that much memory is 105 | available. 106 | - If not, it will return NULL 107 | - If it's available, it will return you a pointer to the allocated block 108 | - NOTE: RETURNED VALUE TYPE IS: void* (pointer to a void) 109 | THIS MUST BE CONVERTED/CAST INTO A REQUIRED TYPE 110 | 111 | (b-1) Example of malloc 112 | 113 | //note the cast from (void*) to (char*) of the returned pointer from malloc 114 | char* str = (char*)malloc(20); //we ask for 20 bytes on heap 115 | 116 | if( str == NULL) { 117 | //malloc failed. 118 | //CAN'T use str; its NULL 119 | } else { 120 | //str is now pointing to a memory of 20 bytes. 121 | //Use it as you please. 122 | 123 | strcpy(str, "20-bytes only"); //can't store more than 20 124 | 125 | //MOST IMP: FREE THE MEMORY BLOCK ONCE DONE 126 | //FAILURE TO DO SO IS - 127 | // * BAD PROGRAMMING 128 | // * HEAP MEMORY WILL BE EXHAUSTED 129 | // * MEMORY ALLOCATIONS WILL START FAILING ONCE EXHAUSTED 130 | 131 | //free the memory (give it back to the heap) 132 | free(str); 133 | } 134 | 135 | (b-2) malloc() allocates memory BUT DOES NOT INITIALIZE IT. All the memory 136 | contents will be *random* 137 | 138 | (c) calloc 139 | - calloc is another function to allocate dynamic heap memory 140 | - is similar to malloc() but takes 2 parameters 141 | - numItems 142 | - sizeOfEachElement 143 | - is also returns NULL (on failure) OR pointer to a new memory block (if OK) 144 | - IMP DIFFERENCE: memory() allocated by calloc() is set to all 0 bytes (all initialized) as compared to malloc() 145 | 146 | (c-1) Example of calloc: 147 | 148 | char* str = (char*)calloc(20, sizeof(char)); //total 20, sizeof(char) is 1 149 | if(str == NULL) { 150 | //can't use str, its NULL because calloc failed 151 | 152 | } else { 153 | //use str, its pointing to a newly allocated block 154 | strcpy(str, "dynamic memory"); //only 20 bytes available. 155 | 156 | //free once done 157 | free(str); 158 | } 159 | 160 | (d) free 161 | - Memory allocated on heap using malloc() and calloc() MUST BE freed 162 | - To free the dynamic memory, call the free(ptr) function. The parameter passed 163 | in is what the functions returned 164 | - Failure to free the memory - 165 | * IS A BAD PROGRAMMING PRACTICE 166 | * HEAP MEMORY WILL BE EXHAUSTED 167 | * YOUR PROGRAM MAY CRASH IF YOU GO OUT OF HEAP MEMORY 168 | 169 | (e) You don't necessarily have to allocate only char-data (1-byte). For example, 170 | what if I want to 171 | - create an array of 5 integers dynamically and store 10, 20, 30, 40, 50? 172 | 173 | //Note the var type: its int*, not char* 174 | int* pArr = NULL; //explcit NULL init, can't use NULL pointer unless it 175 | //points to a proper memory 176 | 177 | //Casting is necessary from void* to whatever is our type 178 | pArr = (int*)malloc(20); //We have 5 ints, each 4 bytes 179 | --OR-- 180 | pArr = (int*)calloc(5, 4); //calloc: 5 ints, sizeof each 4 181 | 182 | //See if malloc()/calloc() successful 183 | //if successful, store elements and print all of them 184 | if (pArr != NULL) { 185 | //pArr is pointing to the first int of the newly allocated block 186 | *pArr = 10; 187 | 188 | //Increment pArr; because its an int*, it will be incremented by 4 bytes 189 | pArr++; 190 | *pArr = 20; 191 | 192 | //...store 30, 40, 50 .... 193 | 194 | //using for loop, print all our array elements 195 | .... 196 | 197 | //Done with the dynamic memory, must be freed 198 | free(pArr); 199 | } else { 200 | //malloc/calloc failed 201 | fprintf(stderr, "Could not allocated memory for an array...."); 202 | exit(-1); //terminate the program 203 | } 204 | 205 | 206 | //=========== END OF NOTES ON STATIC Vs DYNAMIC MEMORY ===================== -------------------------------------------------------------------------------- /HandsOnLabSessions/FileHandlingLab/ExplanatoryNotes.txt: -------------------------------------------------------------------------------- 1 | This is NOT a C Program. 2 | 3 | //File handling in C 4 | 5 | It's a common need in programs to read existing files on disk and/or 6 | create new files. This can be done for storing program's information 7 | or program's output. 8 | 9 | For example - Consider, your favorite editor application. This application 10 | allows you write your Code OR create slides/presentation, allows you to create 11 | spreadsheets or your drawings. When you're are done with the application, you would 12 | want to save your work in form of a disk file. When you click on application's Save 13 | or Save As.. menu options, all these applications allow you to select a folder where 14 | the information would be saved and also to name a file in which you want to save your 15 | work. 16 | 17 | Another example - when you compile your C program by using a GCC (or any compiler for that 18 | matter), if the program is valid, the compiler now must create an EXE file and store all the 19 | compiled machine code there. The compiler, therfore, would need to create a disk file and store 20 | the EXE information in there. 21 | 22 | In short, creating disk files is a normal work that all non-trivial applications must do. 23 | 24 | Type of files 25 | ============= 26 | On most OSs(Mac, Linux, Windows), directories (folders) and disk files are both considered 27 | as files. These OSs provide system-level calls to create directories and files which the applications 28 | in turn use and get the work of creating a file/folder, renaming, writing/reading to the file, 29 | deleteing folder/file and all related work. 30 | 31 | Also, on all the OSs, contents of the disk file also determine their file type. These types are 32 | (a) Text Files and (b) Binary files. 33 | 34 | --- Text Files --- 35 | ============= 36 | - When you save your C file via the IDE/Editor, its saved in a text file named as you chose. The 37 | contents of these files are human-readable (ASCII/Extended-ASCII/UTF8 or UTD16 encoded Unicode). 38 | 39 | - You can open these files into editors or cat/display their contents on the screen and read/understand 40 | it without any problem. 41 | 42 | - Example of the text files - your C programs on disk, .txt file saved by Notepad, .HTML files for the 43 | Web application, .py python files and so on. 44 | 45 | 46 | -- Binary Files --- 47 | =============== 48 | - Complex data structures which donot contain only the textual data but also numeric data can also 49 | be stored in the files. These files are not readable and when displayed, would display funny/ 50 | unprintable characters. 51 | 52 | - Example - .EXE file created by the compiler. .PNG/.JPG image files created by the paint/edit 53 | applications, .PDF file, etc. 54 | 55 | - To view such files and understand their contents, you would need a binary editor such as an HexEdit 56 | 57 | 58 | -- File Handling in C ---- 59 | ====================== 60 | 61 | There are many functions defined in stdio.h. Therfore, for handling files in your C Program, 62 | you must include stdio.h - #include 63 | 64 | The key abstraction provided by the C-Runtime library for the files is the FILE structure. File 65 | handling programs typically perform the following steps - 66 | - Open the File (fopen) 67 | - The open operation may fail (for example: no space on disk OR opening a wrong/non-existent file) 68 | - File can be opened for READ/WRITE/APPEND operations - "r", "w", "a" mode parameter 69 | - You can also add "b" to the above when dealing with the binary files - "rb", "wb".... 70 | - If file was opened successfully - 71 | - the fopen returns a pointer to the FILE structure. (FILE*) 72 | - Programmer's need not worry about the FILE structure itself 73 | - Instead, they should use it and pass it to other file handling functions 74 | - To read the contents of the file, there are many functions 75 | - fgets/fscanf/fgetc 76 | - To write new contents to the file, you can use - 77 | - fputs/fprintf/fputc .. 78 | - Once the file operations are done, the file MUST be closed 79 | - fclose(FILE*) 80 | - Forgetting to close the file may corrupt the file contents OR the contents may get lost 81 | - You can use functions such as "fflush" to flush the memory contents to the disk file 82 | - Else (if the file was not openeded successfully) 83 | - Program should handle the error, display some useful message and exit 84 | 85 | This explanation is only a preliminary text. More can be found online. 86 | -------------------------------------------------------------------------------- /HandsOnLabSessions/FileHandlingLab/FileReadSimple.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | //We want the user to specify only one arguent 4 | // - the name of the file to read and display 5 | //Therefore, we expect argc = 2 (program name + file name) 6 | int main(int argc, char* argv[]) { 7 | if (argc != 2) { 8 | //Correct set of command line args were not provided. 9 | //Display the usage and return/exit 10 | fprintf(stderr, "USAGE: FileReadSimple \n"); 11 | return -1; 12 | } 13 | 14 | //Command Line args OK. 15 | //argv[0] -> name of the program (FileReadSimple) 16 | //argv[1] -> full path of the file to read 17 | printf("\n=====================\n"); 18 | printf("File to read: %s\n", argv[1]); 19 | printf("=================\n"); 20 | 21 | //Step 1: Open the file for the "r"ead operation 22 | FILE* pFile = fopen(argv[1], "r"); 23 | 24 | //Step 2: fopen can fail. Handle the error correctly 25 | if (pFile != NULL) { 26 | //File was opended successfully, proceed. 27 | //Process the file one character at a time 28 | 29 | //2 ways to read the file 30 | // character by characer - SLOW 31 | // line by line - FASTER 32 | 33 | #if 0 34 | //feof() functions detects if eof(End-of-File) is reached 35 | while(!feof(pFile)) { 36 | char c = fgetc(pFile); 37 | printf("%c", c); 38 | } 39 | #else 40 | //We must have a line buffer for fgets() to store the line into 41 | //fgets() returns NULL when all lines finished/read 42 | char lineBuffer[100]; 43 | while( fgets(&lineBuffer[0], //store the line into this buffer 44 | 100, //how big our buffer is 45 | pFile //File pointer 46 | )) { 47 | printf("%s", lineBuffer); 48 | } 49 | #endif 50 | 51 | printf("\n======= Done =========\n"); 52 | //Done with the processing, MUST close the file 53 | fclose(pFile); 54 | } else { 55 | //File Opening failed, display and error and return/finish 56 | fprintf(stderr, "(%s) could not be opened...Exiting...\n", argv[1]); 57 | return -2; //or exit(-2); 58 | } 59 | 60 | //Done 61 | return 0; 62 | } -------------------------------------------------------------------------------- /HandsOnLabSessions/FileHandlingLab/FileWriteBinary.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | //Define a struct for Student 4 | //storing his/her name, totalMarks, totalOutOf 5 | struct Student { 6 | char name[50]; //49 chars + 1 NULL 7 | int totalMarks; 8 | int totalOutOf; 9 | }; 10 | 11 | 12 | int main(void) { 13 | //Step 1: Create 3 Students 14 | struct Student student1 = { 15 | "John Doe", 16 | 500, 17 | 600 18 | }; 19 | 20 | struct Student student2 = { 21 | "Jane Doe", 22 | 200, 23 | 600, 24 | }; 25 | 26 | struct Student student3 = { 27 | "Superman", 28 | 600, 29 | 600, 30 | }; 31 | 32 | 33 | //Step 2: Create a new file 34 | //We plan to store 3 students directly to the file 35 | //Create a binary file is good because we want to 36 | //dump the memory contents to the file. 37 | //For the mode, we use "w"rite and "b"inary 38 | FILE* pFile = fopen("students.bin", "wb"); 39 | if (pFile != NULL) { 40 | // File Opened successfully 41 | // Write each student 42 | // fread/fwrite functions are low level functions that read/write 43 | // memory contents to the file 44 | fwrite(&student1, sizeof(struct Student), 1, pFile); 45 | fwrite(&student2, sizeof(struct Student), 1, pFile); 46 | fwrite(&student3, sizeof(struct Student), 1, pFile); 47 | 48 | 49 | //Done with writing, close the file 50 | fclose(pFile); 51 | } else { 52 | printf("Binary file: students.bin could not be opened for writing.\n"); 53 | return -2; 54 | } 55 | 56 | //Done 57 | return 0; 58 | } -------------------------------------------------------------------------------- /HandsOnLabSessions/FileHandlingLab/FileWriteSimple.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | //We want the program to accept one command line argument 4 | // - filename 5 | //The program will create that file as a text file 6 | //and store some sample textual data in it. 7 | 8 | 9 | //Sample text data, an array of 10-strings. 10 | //Each string statically initialized. Compiler will put 11 | //only such strings in a separate memory block and put 12 | //a NULL byte at the end of each string 13 | #define NUM_PROVERBS 12 14 | 15 | char *proverbs[NUM_PROVERBS] = { 16 | "===== Some English Proverbs =====\n", 17 | " 1. Honesty is the Best Policy.\n", 18 | " 2. Don't judge a book by it's cover.\n", 19 | " 3. An apple a day keeps Doctor Away.\n", 20 | " 4. Better late than never.\n", 21 | " 5. Rome wasn't built in a day.\n", 22 | " 6. Actions speak louder than words.\n", 23 | " 7. Out of sight, out of mind.\n", 24 | " 8. It’s the tip of the iceberg.\n", 25 | " 9. Learn to walk before you run.\n", 26 | " 10. First things first.\n", 27 | "========= End of Proverbs =========\n" 28 | }; 29 | 30 | int main(int argc, char* argv[]) { 31 | //make sure we got correct number of arguments 32 | if (argc != 2) { 33 | fprintf(stderr, "USAGE: FileWriteSimple \n"); 34 | return -1; 35 | } 36 | 37 | //Got correct set of arguments. 38 | //argv[1] -> file to create 39 | //We will create a text file. Mode used "w"rite 40 | FILE* pFile = fopen(argv[1], "w"); 41 | 42 | if( pFile != NULL) { 43 | //File opened successfully. 44 | 45 | for(int i=0; i 2 | 3 | int main(void) { 4 | //Step 1 5 | //Define a simple int variable and store 100 in it 6 | int score = 100; 7 | 8 | //Step 2 9 | //Create a pointer to score 10 | //Score is an int, thefore, pointer to it would be int* 11 | int* pScore = &score; 12 | 13 | //Step 3 14 | //Create one more pointer to score 15 | //Score is an int, thefore, pointer to it would be int* 16 | int *pScoreAnother = &score; 17 | 18 | //Where is score in memory? 19 | //Because pScore and pScoreAnother are pointing to the same "score", 20 | //their values must be same as the address of "score" 21 | printf("score's mem address: %p\n", &score); 22 | printf("pScore: %p\n", pScore); 23 | printf("pScoreAnother: %p\n", pScoreAnother); 24 | 25 | //Step 4: 26 | //Check the result of Step 3. 27 | //pScore and pScoreAnother point to "score" 28 | //Thefore &score should be the same as values in pScore and pScoreAnother 29 | 30 | 31 | //Step 5: Now, you can modify score in 3 ways 32 | // 1. Direct modification: for example: score = 200; 33 | // 2. Indirect using pScore OR 34 | // 3. Indirect using pScoreAnother 35 | //Also, 36 | // 1. You can modify(write) the variable with pointer 37 | // 2. Read back using the direct reference 38 | 39 | printf("-----------------\n"); 40 | 41 | //Direct reference 42 | score = 200; 43 | printf("New Score is: %d\n", score); 44 | 45 | //Indirect change through pScore 46 | *pScore = 999; //*pScore means dereference pScore and store 999 there 47 | //becuase pScore is pointing to "score", this will set 48 | //"score" to 999 indirectly 49 | printf("Modified with pScore but reading directly: %d\n", score); 50 | 51 | //Indirect change through pScoreAnother 52 | //Same explanation as above 53 | *pScoreAnother = 0; 54 | printf("Modified with pScoreAnother but reading directly: %d\n", score); 55 | 56 | 57 | //Step 6: LLDB/GDB Verification 58 | //Exercise for the students 59 | 60 | 61 | //Done 62 | return 0; 63 | } -------------------------------------------------------------------------------- /HandsOnLabSessions/PointersLab/PointerBasic.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(void) { 4 | //Step 1: Declare an int variable intialized to 10. 5 | int x = 10; 6 | 7 | //Step 2: 8 | //Where is x in memory? 9 | //Use & (address-of) operator to find that out 10 | printf("Address of x is: %p\n", &x); 11 | 12 | //Step 3: 13 | //Use GDB/LLDB to see the location of x in memory 14 | // These for for Mac LLDB - 15 | // > gcc -g -o PointerBasic PointerBasic.c 16 | // > lldb ./PointerBasic 17 | // - b main (break at main) 18 | // - r (run the program that would break at main) 19 | // - s (step) 20 | // - memory read --size 4 --format Y --count 16 &x 21 | // OR 22 | // - memory read --size 4 --format Y --count 16 23 | 24 | //Step 4 25 | //Verify: 26 | // Whatever is the address printed on line 10 by the printf() 27 | // If you take a memory dump at that address, 28 | // - you should value 10. This is what the "x" contains 29 | // - 10 would be shown as 0xA or b00001010 (binary) 30 | 31 | 32 | //Done 33 | return 0; 34 | } -------------------------------------------------------------------------------- /HandsOnLabSessions/PointersLab/PointerDerefRead.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(void) { 4 | //Step 1: Declare a simple int variable and store 45 in it. 5 | int x = 45; 6 | 7 | //Step 2: Declare a pointer value that points to x 8 | //That is - take an address of x and store it into a pointer variable 9 | // - If we take an address of an "int" variable, resulting type would be int* 10 | // - If we take an address of a "char" variable, resulting type would be char* 11 | // - If we take an address of a "float" variable, resulting tyoe woukd float* 12 | // NOTE: 13 | // LHS type must match RHS type 14 | // In the followign code - 15 | // LHS type : int* (pointer to an integer) 16 | // RHS type: int* (address of an int which results in int*) 17 | int* pX = &x; 18 | 19 | //Step 3; 20 | // - Deref the pointer to read the data 21 | // - Here, pX points to X 22 | // - Therefore dereferencing it would result in reading from where its points 23 | // Means, a read will occur from the location where x is stored (because pX point to X) 24 | // - This dereference is a READ operation on the pointer 25 | 26 | //NOTE - 27 | // (1) *pX below means -> dereference pX, read from where it points to and store 28 | // the resulting value in the LHS 29 | // (2) When you deref an int*, it results in int. 30 | // When you deref a char*, it results in char 31 | // When you deref a short*, it result in short 32 | // and so on 33 | // In the declaration below, *pX would result in int because we deref an int* 34 | // LHS type is an int and RHS type is also an int 35 | // LHS type == RHS type 36 | int contentsOfX = *pX; 37 | printf("contentsOfX = %d\n", contentsOfX); 38 | 39 | 40 | //Step 4: Verify - 1 41 | // - Make sure that the printf() outputs value = 45 42 | 43 | //Step 4 - Verify - 2 44 | //Use GDB/LLDB to verify that &x (address of x where 45 is stored) is 45 | //the same as the value of pX (pX point to X -> pX = &x) 46 | //Steps - 47 | // $gcc -g -o PointerDerefRead PointerDerefReaed.c 48 | // $lldb PointerDerRead 49 | // - b main 50 | // - r 51 | // - s (3-4 times to complete the execution of printf) 52 | // - print pX (this and the following must print the same value) 53 | // - print &x 54 | // - memory read --size 4 --format d --count 4 pX 55 | // OR 56 | // -- memory read --size 4 --format d --count 4 &x 57 | 58 | 59 | 60 | 61 | } -------------------------------------------------------------------------------- /HandsOnLabSessions/PointersLab/PointerDerefWrite.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(void) { 4 | //Step 1: Declare a simple int variable 5 | // On most architectures, it would take 4 bytes in memory 6 | // You can use sizeof(int) in your program to find out the architecture specific int-size 7 | int age = 26; 8 | 9 | //Step 2 - Where is age stored in memory? 10 | //Use ampersand (&) operator to get its address and print it. 11 | //%p format specifier is for dealing with the pointers 12 | printf("Age's address in memory: %p\n", &age); 13 | 14 | //Step 2 - Create a pointer variable to point to age. 15 | // LHS and the RHS of the expression must match 16 | // pAge is a pointer-to-int -> LHS 17 | // &age - is taking an address of an int (therefore an int*) -> RHS 18 | // Therfore, LHS = RHS 19 | int *pAge = &age; 20 | 21 | //Step 3- You can always modify (write) age directly by setting its new value 22 | //How would you do it *indirectly* using the pointer (pAge)? 23 | // For example: Double the age - i.e. multiply the current age by 2 24 | //What does the expression mean? 25 | // LHS: *pAge -> Dereference pAge (note that the Deref is on the LeftSide => Write Operation) 26 | // *= -> Multiply and Assign shorthand. 27 | // RHS = 2 28 | // This shorthand is equivalent to *pAge = *pAge * 2 29 | // In plain word, it mean -> read an int where pAge is pointing to, multiply by 2 and store the result 30 | // back into the location where pAge is pointing to 31 | // 32 | // Because pAge is pointing to an "age", all above will modify the "age" indirectly (through a pointer) 33 | // 34 | *pAge *= 2; //*pAge = *pAge * 2 35 | 36 | //Step 4 - Read the modified "age" back. 37 | // You can use direct "age" OR 38 | // You get get to it "indirectly" via "pAge" 39 | printf("Getting age directly: %d\n", age); 40 | printf("Getting age indirectly: %d\n", *pAge); 41 | 42 | 43 | //Step 5 - Use GDB/LLDB to verify the memory contents 44 | // $gcc -g -o PointerDerefWrite PointerDerefWrite.s 45 | // $lldb PointerDerefWrite 46 | // - b main 47 | // - r 48 | // - s 2-3 times until you reach *pAge *=2 statement 49 | // - memory read --size=1 --format Y --count 16 &age 50 | // OR 51 | // - memory-read --size=1 --format Y -- count 16 pAge 52 | // - Make sure to see dump showing 0x1a (26 decimal) where age is stored and 53 | // where pAge is pointing 54 | // - s (execure the *pAge *= 2 statement) (26 * 2 = 52) 55 | // - memory-read --size=1 --format Y -- count 16 pAge (same command again) 56 | // The memory dump now show show 52 (0x34) at the same location where age is stored 57 | 58 | 59 | 60 | //Done 61 | return 0; 62 | } -------------------------------------------------------------------------------- /HandsOnLabSessions/PointersLab/PointerMath.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(void) { 4 | //Step 1: Declare a simple int array 5 | int ages[3] = { 18, 45, 87 }; 6 | 7 | //Step 2: Set a pointer to the 1st element of the array 8 | //Because the indexes are relative, we use index 0 9 | int* pAge = &ages[0]; 10 | 11 | printf("Age at index 0 is: %d\n", *pAge); //Deref the pointer 12 | 13 | //Step 3: What happens when we apply arithemtic operator such as + 14 | //to the pointer. 15 | //ANSWER : Pointer is incremented/decremented by (units * sizeof(pointer type)) 16 | //e.g 17 | //If we say: pAge += 1; OR pAge++, 18 | //Pointer will be incremented by (1 * sizeof(int)) = 4 bytes 19 | //If we say: pAge += 2, 20 | //Pointer will be incremented by (2 * sizeof(int)) = 8 bytes 21 | 22 | pAge++; //simply move to pointer by pointer-type and 1-unit 23 | printf("Pointer incremented by 1 unit: Age at index 1 is: %d\n", *pAge); 24 | 25 | //Now Decrement by 1 (we will move backward by 4-bytes) 26 | pAge--; 27 | printf("Pointer decremented by 1 unit, Age at index 0 is: %d\n", *pAge); 28 | 29 | //Can we go back still? It will going before the 0th index. 30 | //This is not part of declared ages array but with pointer, you can do 31 | //anything 32 | pAge--; 33 | printf("Pointer decremented by 1 unit, Age at index -1 (INVALID) is: %d\n", *pAge); 34 | 35 | //From here, how can move directly to ages[2] (the last element) 36 | pAge += 3 ; 37 | printf("Pointerd multipled by 3 units, Age at this index is: %d\n", *pAge); 38 | 39 | 40 | //Done 41 | return 0; 42 | } -------------------------------------------------------------------------------- /HandsOnLabSessions/PointersLab/PointerMath2.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(void) { 4 | //Step 1. Declare a char array and initialize it statically. 5 | char *day = "A long string for the demo purpose."; 6 | 7 | //Step 2. We want a separate pointer variable 8 | //to point to the same string, declare it and make it 9 | //point to the string 10 | char *pStr = &day[0]; //OR pStr = day; 11 | 12 | //Step 3. Print the string using pStr to make sure its what we think it is! 13 | printf("==================================\n"); 14 | printf("Original string: %s\n", day); 15 | printf("Strings via pointer: %s\n", pStr); 16 | printf("==================================\n"); 17 | 18 | //Step 4: Now using the pointer, we want to visit every character in the string 19 | //until we reach the end of it. 20 | //How do we know where the string ends? - NULL byte. 21 | while(*pStr != '\0') { 22 | printf("%c[%d] ", *pStr, *pStr); //Write the char and then a space 23 | //Pointer math: 24 | //Incrementing the pointer will move it ahead by (1*sizeof(char)) 25 | //That is - ahead by 1 byte OR 1 char in the string 26 | pStr++; 27 | } 28 | printf("\n"); 29 | printf("===============================\n"); 30 | 31 | 32 | 33 | //Exercise: Write a simialr looping code and find out 34 | //the length of the string given a pointer to some char. 35 | 36 | 37 | //Done 38 | return 0; 39 | } -------------------------------------------------------------------------------- /HandsOnLabSessions/StringsLab/MyStrcpy.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | //How can you write your own version of strcpy()? 4 | //Write a function that 5 | // - takes a char* (pointer to a dest string - where to copy) 6 | // - and takes a char* (pointer to a source string - from where to copy) 7 | //and returns a pointer to the copied string (dest pointer) 8 | // 9 | 10 | //Logical Steps to achieve this - 11 | // - C Strings are (a) array of chars and (b) NULL terminated 12 | // - Make a copy of the dest pointer (because we will increment it) 13 | // and we need to return the it (after the copy is done) 14 | // - Until a src pointer reaches the NULL byte - 15 | // - Copy char pointed to by src to the location pointed to by the dest 16 | // - Finish off the copy by storing a NULL byte at the end of the dest (so that 17 | // it becomes a proper NULL terminated C String) 18 | // - Return the copy of the dest pointer (original dest was changed for the copy op.) 19 | 20 | // Function: 21 | // Parameters: 22 | // char* pDest -> Destination string pointer (copied chars will be placed here) 23 | // char* pSrc -> Source string pointer (chars will be copied from here) 24 | // Return: 25 | // char* pDest -> The pointer to the copied string 26 | char* myStrcpy(char* pDest, char* pSrc) { 27 | //Make a copy of the dest 28 | char *pDestCopy = pDest; 29 | 30 | //Until a NULL byte is reached in the source, keep copying 31 | //from src to dest, one char at a time. Every time, increment 32 | //both the src and dest pointers. 33 | while (*pSrc != '\0') { 34 | //Copy 35 | *pDest = *pSrc; 36 | 37 | //Increment 38 | pDest++; 39 | pSrc++; 40 | } 41 | 42 | //Make sure to place a NULL termination byte at the end of the dest 43 | *pDest = '\0'; 44 | 45 | //Return the dest pointer. Here, we return the saved dest pointer because 46 | //the passed-in pointer was incremented during the copy operation 47 | return pDestCopy; 48 | } 49 | 50 | 51 | 52 | //Test Your own Code 53 | int main(void) { 54 | //Step 1: Test Setup 55 | char *pSrcString1 = "Test String 1"; 56 | char destString1[20]; //Litter more size of the array than the size of pSrcString1 57 | 58 | char *pSrcString2 = "0123456789ABCDEF"; 59 | char destString2[17]; //16 chars + 1 NULL byte 60 | 61 | char* pResult = myStrcpy(destString1, pSrcString1); 62 | printf("Test 1: Source String: %s, Dest String (after copy): %s\n", 63 | pSrcString1, destString1); 64 | 65 | pResult = myStrcpy(destString2, pSrcString2); 66 | printf("Test 2: Source String: %s, Dest String (after copy): %s\n", 67 | pSrcString2, destString2); 68 | 69 | //Done 70 | return 0; 71 | 72 | } -------------------------------------------------------------------------------- /HandsOnLabSessions/StringsLab/MyStrlen.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | 4 | //How can you write your own version of strlen()? 5 | //Write a function that takes a char* (pointer to a starting char) 6 | //and return the length of the string. 7 | // 8 | //Solution: Because we know that the C String in (a) array of characters and 9 | // (b) its NULL terminated, we can devise the following steps to 10 | // find out how long the string is! 11 | // - Initialize a local integer count to ZERO 12 | // - Starting with the character pointed to by the input parameter, 13 | // - see, if that character is a NULL byte (termination) 14 | // - If it is, we're done. RETURN the counter 15 | // - Else, increment the counter (because we found a non-NULL char) 16 | // - Move the char* ahead, When we do char* increment, it will go ahead 17 | // by 1-byte. Thus, moving to the next character in the string 18 | // - Jump Back to (see if a NULL byte) step. 19 | // 20 | // In the function prototype/signature below - 21 | // Parameters - 22 | // pStr -> Input char* which points to the starting of the string 23 | // Return - 24 | // int -> The length of the string 25 | int myStrlen(char* pStr) { 26 | //Initialize the local length counter 27 | int lengthCounter = 0; 28 | 29 | //Loop over all the characters in the string until a NULL byte is found 30 | while(*pStr != '\0') { 31 | //increment the counter 32 | lengthCounter++; 33 | 34 | //Move the pointer to the next char 35 | pStr++; 36 | } 37 | 38 | //When here, we got a NULL byte and the lengthCounter 39 | //contains the length we calculated. 40 | return lengthCounter; 41 | } 42 | 43 | 44 | //A Utility function to display the test result 45 | //Parameters: 46 | // char* str -> String under test 47 | // int expectedResult -> What as the expected result? 48 | // int actualResult -> What was actually returned? 49 | //Return: 50 | // void (none) 51 | void displayTestResult(char *str, int expectedResult, int actualResult) { 52 | if (actualResult != expectedResult) { 53 | fprintf(stderr, "Test1 Failed: str: %s, exepected length: %d, actual: %d\n", 54 | str, expectedResult, actualResult); 55 | } else { 56 | fprintf(stdout, "%s: Test Passed.\n", str); 57 | } 58 | 59 | //Done 60 | return; 61 | } 62 | 63 | 64 | 65 | //Test your own function 66 | //NOTE: 67 | // The use of fprintf() calls 68 | // This is similar to printf() but you can pass standard file handles or your own file handles. 69 | // stdin -> standard input (for keyboard) 70 | // stdout -> standard output (for the display) 71 | // stderr -> standard error (connected to the display) 72 | // 73 | int main(void) { 74 | //Step 1: Setup test strings 75 | char str1[6] = {'H', 'e', 'l', 'l', 'o', '\0'}; //"Hello" 76 | char *str2 = "Just Do it!"; //Static string 77 | char str3[10] = { '\0' }; //Empty string: "" 78 | char *str4 = ""; //Another empty string, statically init 79 | 80 | //Test your function 81 | int expectedRet = 5; //Length of "Hello" 82 | int actualRet = myStrlen(str1); 83 | displayTestResult(str1, expectedRet, actualRet); 84 | 85 | 86 | expectedRet = 11; //Length of "Just Do it!" 87 | actualRet = myStrlen(str2); 88 | displayTestResult(str2, expectedRet, actualRet); 89 | 90 | expectedRet = 0; //Length of str3 -> an empty string 91 | actualRet = myStrlen(str3); 92 | displayTestResult(str3, expectedRet, actualRet); 93 | 94 | expectedRet = 0; //Length of str4 -> an empty string 95 | actualRet = myStrlen(str4); 96 | displayTestResult(str4, expectedRet, actualRet); 97 | 98 | 99 | 100 | //Done 101 | return 0; 102 | 103 | } -------------------------------------------------------------------------------- /HandsOnLabSessions/StringsLab/StringCpy.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include //For strcpy 3 | 4 | int main(void) { 5 | //Step 1: Define a statically initialized string 6 | char *str = "I am being copied into another string."; 7 | 8 | //Step 2: Define another string where we copy the above string 9 | //We are reserving a space to store 10 characters 10 | //This is a static allocation (ie. size known at the compile time) 11 | // 12 | //We can use this array as a string...why? 13 | // Because -- string is just an array of characers AND 14 | // ends with a NULL byte 15 | // 16 | //These 50 bytes are in local memory and could contain any junk 17 | //when declared. If the array contains less space than the str 18 | //its a *BUG*. We can't write more bytes into the array of less size 19 | char dest[50]; 20 | 21 | 22 | //Step 3: Use strcpy() to copy 23 | //strcpy will copy each char (a byte) from str into dest until 24 | //it finds a NULL byte in the "str". It stops then and puts a NULL 25 | //byte into the dest's next byte to finish the copy operation 26 | 27 | strcpy(dest, str); //copy str into dest 28 | 29 | //Step 4. Print "dest" 30 | printf("After copying: dest is --> %s\n", dest); 31 | 32 | 33 | //Now, we will do strcpy() and copy the same string 34 | //but purposely keep the size of the dest array less than the length 35 | //of the source string 36 | //THIS IS BAD CODE AND ONLY FOR THE LEARNING PURPOSE TO SEE 37 | //WHAT HAPPENS IF WE HAVE LESS SIZE IN MEMORY AND TRY TO PUT IN MORE BYTES. 38 | // 39 | //This will corrupt sections of memory and would make program crash or 40 | //otherwise buggy 41 | // 42 | //Uncomment the strcpy() to see the bad effect -> program crashes 43 | //Run it under LLDB/GDB to see how the memory corruption takes place. 44 | // 45 | char smallerBuf[10]; 46 | int tmp = 1234; 47 | strcpy(smallerBuf, str); 48 | printf("SmallerBuf ---> %s\n", smallerBuf); 49 | 50 | 51 | 52 | //Done 53 | return 0; 54 | 55 | 56 | } -------------------------------------------------------------------------------- /HandsOnLabSessions/StringsLab/StringLen.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include //For the String functions 3 | 4 | //Demo code the strlen() 5 | int main(void) { 6 | //Step 1 : Define a static string 7 | // NULL termination is automatic 8 | char *pStr1 = "I am a statically initialized string."; 9 | 10 | //Step 2: Define a manually intiailized string 11 | //See how laborious and error prone the code gets! 12 | //"I am a string2" - NULL terminate is done manually 13 | char string2[15]; 14 | string2[0] = 'I'; 15 | string2[1] = ' '; 16 | string2[2] = 'a'; 17 | string2[3] = 'm'; 18 | string2[4] = ' '; 19 | string2[5] = 'a'; 20 | string2[6] = ' '; 21 | string2[7] = 's'; 22 | string2[8] = 't'; 23 | string2[9] = 'r'; 24 | string2[10] = 'i'; 25 | string2[11] = 'n'; 26 | string2[12] = 'g'; 27 | string2[13] = '2'; 28 | string2[14] = '\0'; 29 | 30 | //strlen() is a standard C function 31 | //It takes a char* (string) input parameter and return the string's length 32 | //Because this is a C-String, its NULL terminated and that's how strlen() 33 | //can determine its length. 34 | printf("pStr1's length is: %lu\n", strlen(pStr1)); 35 | printf("string2's length is: %lu\n", strlen(string2)); 36 | 37 | 38 | //Exercise: WHAT WILL HAPPEN IF WE REMOVE THE NULL TERMINATION 39 | // at string2[14]??? 40 | printf("Before removing NULL, string2's is now: %s.\n", string2); 41 | string2[14] = '?'; //NULL is gone, therefore the string is now NOT NULL terminated 42 | printf("Removed NULL, string2's length is now: %lu.\n", strlen(string2)); 43 | 44 | //Done 45 | return 0; 46 | } -------------------------------------------------------------------------------- /HandsOnLabSessions/StringsLab/StringNCpy.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include //For strncpy() 3 | 4 | 5 | int main(void) { 6 | //Ref to the ending portin of StringCpy.c 7 | // 8 | //We copied more bytes into a smaller array of chars 9 | //This is disastrous because of the memory corruption that occurred. 10 | // 11 | //strcpy() copies *indiscriminately* all the bytes from src to dest 12 | //until it finds a NULL byte in the src. 13 | // 14 | //strncpy() on the other hand must be provided a "n" parameter to say 15 | //how many bytes should be copied. This will safeguard the program 16 | //from buffer overruns that would be otherwise caused by strcpy. 17 | 18 | //Step 1: Define a source string 19 | char *str = "123456789ABCDEF"; 20 | 21 | //Step 2: Dest buffer: purposely small, say 10 bytes 22 | char smallBuf[10]; 23 | 24 | //Step 3: Now copy with strncpy() and limit the copy operation to 25 | //the size of the buffer available. 26 | strncpy(smallBuf, str, 9); //Keep 10th byte for storing NULL 27 | 28 | //Step 4: See the result 29 | printf("After strncpy --> %s\n", smallBuf); 30 | 31 | //Done 32 | return 0; 33 | } -------------------------------------------------------------------------------- /HandsOnLabSessions/StructsLab/ArrayOfStructs.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | //Step 1: Define a struct for Computer 4 | struct Computer { 5 | char brand[25]; //Brand Name 6 | char type[10]; //type ; "laptop", "desktop", "mobile", "iot" 7 | int price; //Price in Rupees 8 | int monthsOld; //How old? in months; 9 | }; 10 | 11 | 12 | int main(void) { 13 | //Step 2: Create 3 computers 14 | struct Computer myLaptop1 = { 15 | "Dell Inspiron", 16 | "laptop", 17 | 34567, 18 | 48 19 | }; 20 | 21 | struct Computer myLaptop2 = { 22 | "Macbook Air", 23 | "laptop", 24 | 145000, 25 | 15 26 | }; 27 | 28 | struct Computer myMobile = { 29 | "Samsung Galaxy S10", 30 | "mobile", 31 | 57000, 32 | 36, 33 | }; 34 | 35 | 36 | //Step 3: Create array of all these 3 computers 37 | struct Computer myDevices[3] = { 38 | myLaptop1, 39 | myLaptop2, 40 | myMobile 41 | }; 42 | 43 | 44 | 45 | //Step 4: Loop through all the array computers and print some info 46 | for(int i=0; i<3; i++) { 47 | printf("Brand: %s\n", myDevices[i].brand); 48 | printf("Price: Rs.%d\n", myDevices[i].price); 49 | printf("~~~~~~~~~~~~~\n"); 50 | } 51 | 52 | 53 | //Done 54 | return 0; 55 | 56 | 57 | } -------------------------------------------------------------------------------- /HandsOnLabSessions/StructsLab/ExplanatoryNotes.txt: -------------------------------------------------------------------------------- 1 | This is NOT a C program 2 | 3 | //Notes of C structs 4 | 5 | In C, structs and unions are discussed together. Structs are more widely 6 | used than the unions. Here, we discuss *only* the structs 7 | 8 | Structs are short-name for "structures". So far, in almost all the C programs, 9 | we've used basic/primitive data types - such as - ints, char, short, float, double, 10 | long and collection types of these basic types such as arrays and strings. 11 | 12 | Its common in all types of programming to create higher-order data structures by 13 | combining the primitive types together and treat it as a wholesome structure. Its 14 | also convenient to create a group that contains multiple fields and then access 15 | those fields as part of the larger data structure. 16 | 17 | 18 | For example: if our program needs to deal with "Person"s? What's a Person? A person 19 | will have his/her firstName, lastName and age, for example. Of course, it can have many 20 | more fields such as likes, dislikes, gender and so on. But to keep it simple and straight, 21 | let's conside fname, lname and age as the fields of this Person!! 22 | 23 | How to we represent such a collection of fields and name it as a Person type? In C, 24 | this is done using the keyword "struct". The "struct" keyword allows us to create a structure 25 | that contains multiple fields - For example - in C, this is the way, we define a struct - 26 | 27 | struct Person { 28 | char fname[15]; //fname can't be more than 14 chars, 1 for NULL 29 | char lname[25]; //lname can't be more than 24 chars, 1 for NULL 30 | int age; //a 4-byte quantity to store person's age 31 | }; 32 | 33 | With this declaration, we've created a named data type called Person. Just like standard C 34 | types int, char, short, etc., this is a new "user-defined data type". Given this type, how can we create different people? We call this process as creating instances of the structures 35 | 36 | For example, this is the C syntax to create variables of the "Person" type 37 | struct Person me; 38 | struct Person alex; 39 | struct Person him; 40 | 41 | Each of the declarations above creates one variable of the "Person" type. Because we've defined a variable of the Person type, compiler must assign it enough memory to hold information about each Person. 42 | 43 | How much memory our Person instance needs? 44 | fname is 15 chars (15 * 1 = 15 bytes), 45 | lname is 25 chars (25 * 1 = 25 bytes) and 46 | age int an int (4 bytes) 47 | ======================= Total of 44 bytes for each Person ==================== 48 | 49 | 50 | So, every time we crete a var (instance of the Person type), we get the 44-bytes of memory 51 | assigned for that variable. See below - 52 | 53 | ============ ============== ============= 54 | | me | | alex | | him | 55 | ============ ============== ============= 56 | | fname(15)| | fname(15) | | fname(15)| 57 | ------------ -------------- ------------- 58 | | lname(25)| | lname(25) | | lname(25)| 59 | ------------ -------------- ------------- 60 | | age(4) | | age(4) | | age(4) | 61 | ------------ -------------- ------------- 62 | 63 | How do you manipulate/read/write each of the struct fields? 64 | 65 | There are 2 ways in which the struct fields are accessed. 66 | 67 | 1) When you only have a struct variable, you access each field with a dot (.) notation 68 | me.age = 23; 69 | alex.age = 89; 70 | him.age = 12; 71 | 72 | 2) When you have a pointer variable, you access each field with a -> notation 73 | for example: 74 | struct Person *pPerson = &me; 75 | strcpy(pPerson->fname, "John"); 76 | strcpy(pPerson->lname, "Doe"); 77 | pPerson->age = 23; 78 | 79 | 80 | SOME BEST PRACTICES RELATED TO THE STRUCTS 81 | =========================================== 82 | 83 | 1) Because a struct stores multiple types of data, the memory it needs can vary. When the 84 | required memory gets allocated, its fields may gets aligned in memory at un-natural boundaries. For example: an int field may get assigned to an address which is not 4-byte aligned. Therfore, program's performance mat get impacted. Consider, using #pragma C Preprocessor directive to pack the fields 85 | 86 | 2) When passing struct across to functions, its better to use a reference (pointer) semantic. 87 | Pass-by-value needs a copy operation which is costly. Therefore, passing struct by reference is alwaya a good idea. 88 | 89 | 3) After having created a struct-type, always write 2-3 functions to get/set fields within 90 | the structure. Have these functions receive the pointer to the struct as the first parameter. 91 | This will lead to better data protection/encapsulation. Only 2-3 such functions can get/set the field data and other function can access the struct via these accessor functions. This makes the program less buggy and when bugs appear, you can debug the program easily. 92 | 93 | 94 | 95 | -------------------------------------------------------------------------------- /HandsOnLabSessions/StructsLab/StructBetter.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include //strcpy, etc. 3 | 4 | #define TITLE_LENGTH 25 5 | #define AUTHORS_LENGTH 40 6 | 7 | //Step 1: Define a global struct type 8 | struct Book { 9 | char title[TITLE_LENGTH]; //24 chars + 1 NULL to store the title 10 | char authors[AUTHORS_LENGTH]; //one or more authors separated by comma 11 | int yearPublished; //a 4-byte int 12 | int priceInRupees; //a 4-byte int 13 | }; 14 | 15 | //Step 2: Write wrapper functions that work on struct fields - get/set 16 | // (2-a) a ConstrctBook function 17 | // Parameters - 18 | // Pointer to the Book struct that needs to be constructed/initialized 19 | // title - title of the new book 20 | // authors - authors of the new book 21 | // year - publication year of the new book 22 | // price - price of the new book in Rupees 23 | // Returns - 24 | // None (void) 25 | void ConstructBook(struct Book* pBook, char* title, char* authors, int year, int price) { 26 | //Copy the passed title parameter into the title field 27 | //We use strncpy to make sure we copy only those many bytes as our title field has 28 | //If user passes a string which has more chars, we will not overflow our buffer 29 | strncpy(pBook->title, title, TITLE_LENGTH-1); //-1 because we need 1 more to store NULL 30 | 31 | //Copy the passed authors parameter into the authors field 32 | strncpy(pBook->authors, authors, AUTHORS_LENGTH-1); 33 | 34 | //Set the year and price fields to the parameters passed in 35 | pBook->yearPublished = year; 36 | pBook->priceInRupees = price; 37 | 38 | //Done 39 | return; 40 | } 41 | 42 | //(2-b) Change the Book's title to a new title 43 | // Parameters - 44 | // pBook - pointer to the book, the title of which needs to be modified 45 | // newTitle - book's new title 46 | // Returns 47 | // None 48 | void setBookTitle(struct Book* pBook, char* newTitle) { 49 | //Only change the title field 50 | strncpy(pBook->title, newTitle, TITLE_LENGTH-1); 51 | 52 | //Done 53 | return; 54 | } 55 | 56 | //(2-C) Get the Book's title 57 | // Parameters - 58 | // pBook - pointer to the book, the title of which needs to be modified 59 | // Returns 60 | // Pointer to the Book's title 61 | char* getBookTitle(struct Book* pBook) { 62 | return pBook->title; 63 | } 64 | 65 | 66 | //------- Similar functions can be written for other Book fields --------- 67 | 68 | 69 | 70 | //(2-D) Get the whole book - copy the information into a new Book 71 | // Parameters - 72 | // pSrcBook -> Book from where the information is extracted 73 | // pDestBook -> Book to where the information is to be copied 74 | // Returns - 75 | // None 76 | void copyBookInfo(struct Book* pSrcBook, struct Book* pDestBook) { 77 | 78 | strncpy(pDestBook->title, pSrcBook->title, TITLE_LENGTH-1); 79 | strncpy(pDestBook->authors, pSrcBook->authors, AUTHORS_LENGTH-1); 80 | pDestBook->yearPublished = pSrcBook->yearPublished; 81 | pDestBook->priceInRupees = pSrcBook->priceInRupees; 82 | 83 | //Done 84 | return; 85 | } 86 | 87 | 88 | //(2-E) Print the details of the given book 89 | // Parameters 90 | // pBook - Pointer to the book struct, info for which is to be printed 91 | // Returns 92 | // None 93 | void printBook(struct Book* pBook) { 94 | printf("============ Book Info ==========\n"); 95 | printf("\tTitle: %s\n", pBook->title); 96 | printf("\tAuthor(s): %s\n", pBook->authors); 97 | printf("\tPublished In: %d\n", pBook->yearPublished); 98 | printf("\tPrice: Rs.%d\n", pBook->priceInRupees); 99 | 100 | //Done 101 | return; 102 | } 103 | 104 | int main(void) { 105 | 106 | //Step 3: 107 | //Create 2 Books 108 | struct Book book1; //memory is allocated but the fields are not initialized 109 | struct Book book2; // ------ same as above ---------- 110 | 111 | //Step 4: 112 | //Initialize both the books 113 | 114 | //1st book 115 | ConstructBook(&book1, 116 | "Twelve Casears", 117 | "Suetonius", 118 | 135, 119 | 675 120 | ); 121 | //2nd book 122 | ConstructBook(&book2, 123 | "Kublai Khan", 124 | "John Man", 125 | 2017, 126 | 987 127 | ); 128 | 129 | //Print Both the books 130 | printBook(&book1); 131 | printBook(&book2); 132 | 133 | 134 | printf("==============================\n"); 135 | 136 | //What's the title of Book1? 137 | //I can access book1.title directly too 138 | //but using an accessor method creates a better code encapsulation 139 | printf("Book 1's title: %s\n", getBookTitle(&book1)); 140 | 141 | //What's the price of Book2? 142 | //I am using a direct field accessor but see above for the better practice 143 | printf("Book 2's price: Rs. %d\n", book2.priceInRupees); 144 | 145 | 146 | printf("======= Book CopyInfo ==============\n"); 147 | //Get all the fields of book1 into a completely new book 148 | struct Book aNewBook; //mem is allocated but fields have random data 149 | copyBookInfo(&book1, &aNewBook); //copy book1's info into aNewBook 150 | 151 | //Now print the newBook 152 | printBook(&aNewBook); 153 | 154 | 155 | //Done 156 | return 0; 157 | } -------------------------------------------------------------------------------- /HandsOnLabSessions/StructsLab/StructSimple.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(void) { 4 | //Step 1: First define a new struct type 5 | struct Person { 6 | char fname[15]; 7 | char lname[25]; 8 | int age; 9 | }; 10 | 11 | 12 | //Step 2: Create a new Person 13 | //Declare and literal syntax to initialize the new Person 14 | struct Person johnDoe = { 15 | "John", 16 | "Doe", 17 | 44, 18 | }; 19 | 20 | //Step 3: Field access: struct variable 21 | //Because johnDoe is a struct, we use a dot (.) notation to 22 | //access each field. 23 | printf("Fname: %s\n", johnDoe.fname); 24 | printf("Lname: %s\n", johnDoe.lname); 25 | printf("Age: %d\n", johnDoe.age); 26 | 27 | 28 | //Step 4: Field access: struct pointer variable 29 | //When you have a struct pointer, use an arrow (->) notation 30 | //to access each field 31 | struct Person *pJohnDoe = &johnDoe; 32 | printf("Fname via pointer: %s\n", pJohnDoe->fname); 33 | printf("Lname via pointer: %s\n", pJohnDoe->lname); 34 | printf("Age via pointer: %d\n", pJohnDoe->age); 35 | 36 | 37 | //Done 38 | return 0; 39 | } -------------------------------------------------------------------------------- /Presentation/C Syntax Overview.key: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rahulek/ProgrammingFoundationCourse/989572cf58ea8773197b07c4e05bc7754b6114a1/Presentation/C Syntax Overview.key -------------------------------------------------------------------------------- /Presentation/C Syntax Overview.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rahulek/ProgrammingFoundationCourse/989572cf58ea8773197b07c4e05bc7754b6114a1/Presentation/C Syntax Overview.pdf -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ProgrammingFoundationCourse 2 | Course Files for the FY Programming Foundations Course 3 | 4 | --------------------------------------------------------------------------------