HISTORY, BASIC SYNTAX
Introduction
1 2
What is C? Why C?
C is a general-purpose programming It’s still popular and widely used language initially developed by Dennis – Available on almost all platforms Ritchie between 1969 and 1973 at – Lots of libraries for different tasks AT&T Bell Labs Provides a relatively low-level (or mid-level) access to the It is an imperative procedural language operating system and hardware intended for system software – System-level programming, embedded systems – Strong ties with UNIX operating system – Can lead to better performance It has influenced many other programming languages – C++, C#, ObjC, Java, JavaScript, Go, csh, ...
3 4 Standards Look and feel
First standard by ANSI in 1989, known as “ANSI C” or C89 /* Computing the factorial of an specified (by the user) number */ #include
bounds-checked functions, ... /* This function returns the factorial of the specified integer (n) */ int fact(int n) { ...
5 6
Basic syntax Formatting
Code is case sensitive /* example function */ Free format Statements terminate with ; float foo(int bar) { int i, c = 0; – Whitespace, newlines, layout etc. do not matter … {} enclose blocks float x; x = 0.1; There are two ways to comment: /* example function */ for (i = 0; i < bar; i++) { to the computer ! float foo(int bar) { x += i*3.14 + c; int i, c=0; c = i + 2; /* Single or float x; } /* same example function? */ Multiple lines */ return x; float x = 0.1; } foo(/*float f,*/int bar) { int i,c=0; float x; for (i=0; i 7 8 Basic data types Basic datatypes in C are: char character DATATYPES, VARIABLES, ASSIGNMENT AND OPERATORS int integer number long long integer number float floating point number double double precision float Integers can be signed (default) or unsigned C has also pointer types There is also a special type void 9 10 Variables Variable assignment Variable data types are static // variable declaration Assign a value to a variable: // examples of assignment int i; count = 10; Declare variables before float f, g; variable = value; i = j = 0; double total = 1.9; using them Both should have the same k = i*j + 1; // valid names – data type // assign at declaration C90 requires that the int i3, myINT, I_o; int i = 4; declarations are before any float o3Gf_ry9; – Implicit conversion between other statements // invalid names compatible types // typecast from int to float int 33, 9a, i-o; int i; Valid variable names in C: float o3.Gf; – Explicit conversion (typecast) float f; . Upper and lower case // data type matters syntax: i = 5 * 21; char c; f = (float) i; letters, underline and float f; (type) var numbers are allowed // watch out for operator order f = 1.234; (float) i/5 != (float) (i/5) c = f; // ERROR! // wrong data type 11 12 Arithmetics Compound assignment Operators: addition C has a short-hand notation + // example of compound assignment – subtraction for combined arithmetic int count = 10; count += 5; // now count is 15 * multiplication operation and assignment count /= 3; // and now count is 5 / division Given an operator 13 14 Arithmetics and assignment examples Other operators Logical operators Bit-wise operators // addition, substraction // grouping with ()s i = 5 + 2; b = a * (1.3 + (25%3)); i = 5 – 2; && AND >> shift right i += 1; // i = i + 1 // watch out for precedence! || OR << shift left f -= 3.4; f = 1 + q / (1 - q); > larger than & AND i++; // i = i + 1 f = (1 + q) / (1 - q); < less than | OR i--; >= larger or equal ^ XOR // also function calls use () <= less or equal ~ complement // multiplication, division f = r * sin(theta); i = 5 * 2; f = (r + p)*sin(theta); == equal i = 5 / 2; // integer division != unequal f *= 3.0 / 4.2; ! negation // modulus m = 25 % 3; 15 16 Basic I/O printf prints formatted text to the screen Format of printf call is printf( {, 17 18 Basic I/O Basic I/O example Special characters that can be used in the template string: #include int main(int argc, char *argv[]) { \n newline \t horizontal tab printf("The answer is %d.\n", 42); \" double quote \\ literal backslash printf("Pi equals to %.2f", 3.14159265); printf(" ...at least to the %dnd decimal.\n", 2); return 0; Placeholders are marked with % followed by formatting and } type information //output: – For now, we will just use the following formattings: // The answer is 42. %d integer value // Pi equals to 3.14 ...at least to the 2nd decimal. %f float value %s string 19 20 Summary Short history and different standards Basic syntax Variables and their type, assignment of values Arithmetic operations Basic IO (printf) 21 POINTERS, ARRAYS AND FUNCTIONS Getting started 22 23 Pointers Pointers On the hardware level a variable is actually a reference to Pointer variables are defined by adding * into the a memory location definition, for example a pointer named ptr of type int The contents of the memory block determine the value is defined as int *ptr; of a variable The address of a variable can be obtained using & – The size of the memory block depends on the type of the operator variable int a; Memory addresses can be stored (and manipulated) Memory using pointers Address Pointer variables can be considered as variables that hold the address of a element with given datatype (int, int *p = &a; char, ...) 24 25 Arrays Array examples // int array of size 10 Static arrays can be introduced using []: int array[10]; // Set value of third element to 1 array[2] = 1; char str[20]; Array of 20 characters // Print the value double values[10]; Array of 10 doubles printf(“a[2]=%d\n”, array[2]); // type of array is equivalent to // type (int*) Elements of array can be accessed using the same [] int *ptr; operator. The value inside brackets is interpreted as an array[0] = 3; ptr = array; offset from the beginning of the array printf(“*ptr=%d\n”, *ptr); – Indices always start from 0 – Last element of an array A of size n is A[n-1] 26 27 Functions Functions are the only subroutine type in C – But functions do not have to return anything Function definitions are of form FUNCTIONS type func-name(param-list) { /* body of function */ } Here type is the return type of the function – void means that function does not return anything 28 29 main function Function example Every C program has the main function with definition This function returns the sum of two integer arguments: #include 30 31 Variable scoping Arguments Variable scoping in C is void funcB(float a) { All arguments are passed as void funcB(int a) { local unless defined int counter; values a += 10; ... } otherwise } Not accessible from funcA – Copy of the value is passed to – Variables declared in int funcA(void) { the function int funcA(void) { the function definition float value_1, value2; – Functions can not change the int var = 0; funcB(value_1); funcB(var); are only visible inside ... value of a variable in the // var is still 0! the function body } calling scope ... – Variables of calling } Not accessible from funcB Pointers can be used to pass a scope are not visible reference to a variable as an inside function body argument! 32 33 C preprocessor The line #include 34 35 Preprocessor macros and #define #define can be used to define “objects” or macros It has the form of #define identifier replacement-list new-line COMPILING AND LINKING, MATH ROUTINES After the definition, all instances of identifier are replaced with replacement-list // Example #define ONE 1 printf(“Value is %d\n”, ONE); // Result: Value is 1 36 37 Transition from source code to program Let's try it out! Compiling: Write the classic first program in C: #include – Transforming the C source void main() { #include 38 39 Math routines library Compiler flags Math routines are defined in #include 40 41 Linking objects and libraries Summary In complex projects: Functions – Compile each source code file (.c) into an object file (.o) Pointers $ gcc -c main.c Compiling $ gcc -c sub.c Arrays – Link object files into a binary and execute the binary Scoping $ gcc -o foo main.o sub.o -lm $ ./foo Link with math routines library! 42 43 LOGICAL COMPARISONS, BRANCHING Control structures 44 45 Boolean datatype Recap: logical operators C90 does not have a logical datatype Operators Precedence (high to low) – Comparison operators (>,>=,...) return integer 0 for FALSE and 1 for TRUE > larger than * / % + - C99 has boolean type bool defined in header < less than < <= > >= stdbool.h >= larger or equal == != <= less or equal && – Also defines constants true and false as integers with == equal || values 1 and 0 respectively != unequal = += -= *= /= %= – Backwards compatible with C90 definition && AND || OR ! NOT 46 47 Control structures // if – else Control structures // if – else Code execution can be branched according to a logical test using if statement: // simple if-else // complex if-elseif-else if (x > 1.2) { if ((x > 1.2) && (i != 0)) y = 4 * x * r; // TRUE { if (...) {...} else {...} } else { y = x * i; // 1st TRUE conditional y = 0.0; // FALSE } else if ((x < 1.0) && c) TRUE FALSE } { y = -x; // 1st FALSE TRUE block is executed if the conditional evaluates to // else is optional // 2nd TRUE non zero, otherwise the (optional) FALSE block is if (x || y) { } else { z += x + y; y = 0.0; // 1st, 2nd executed } FALSE } 48 49 Control structures // switch Control structures // switch condition switch (...) { // Simple switch based on the // value of integer i case ... : ... test value switch (i) { break; end of branch case 1: printf("one\n"); default: ... } default branch executed break; Condition: if nothing else matches case 2: printf("two\n"); – single variable or expression break; default: Branch(=case) with matching value chosen printf("many\n"); break; break stops branch } good style to break even the last branch! 50 51 Control structures // switch Ternary operator A switch (i) { B switch (i) { Ternary conditional is an expression that can be used as a case 1: default: printf("one\n"); printf("many\n"); short-hand if-else in assignments: break; case 1: case 2: printf("one\n"); printf("two\n"); break; break; case 2: (conditional) ? value-if-true : value-if-false default: printf("two\n"); printf("many\n"); break; } } // ternary conditional in use total += found ? count : 0 C switch (i) { case 1: // is equal to this printf("one\n"); if (found) case 2: In each case, what would total += count; printf("two\n"); be printed on the screen if else default: total += 0; printf("many\n"); i is 1, 2, or 3? } 52 53 Loops // for Looping over a block of code can be accomplished using for statement: LOOPS for ( 54 55 Loops // while Loops // while while (...) {...} // loop using a for-statement // i is incremented after each iteration for (i = 0; i < bar; i++) { condition code block x += i*3.14 + c; c = i + 2; Code block executes repeatedly as long as condition is } TRUE // the same loop but with a while-statement i = 0; Condition evaluated before iteration while (i < bar) { x += i*3.14 + c; c = i + 2; i++; } 56 57 do while Special for loops The condition for while is // Execute the loop at least All statements in the for loop construct are optional evaluated before the block // one time counter = 0; So all following loops are valid C code (with variables is executed correctly defined): do { – Sometimes it is desirable to printf("in loop"); start the loop first and then } while (counter != 0) for (; a < treshold; a += 1e-3) {...} check the condition for (; still_valid;) {...} This can be done using for (;;) {...} do {...} while () loop Try to avoid using these use the while instead 58 59 Jump statements Summary break // jump out of a loop Boolean values, comparisons // at first iteration – end a loop (for, while, do) for (i = 0; i < 10; i++) { if-then-else break; or a switch statement printf("in loop"); switch-case } continue printf("i=%d ", i); // output: i=0 Loops – continue with the next iteration of a loop // jump to the next loop // iteration Note that these apply to for (i = 0; i < 10; i++) { continue; the smallest enclosing printf("in loop"); } iteration statement printf("i=%d", i); // output: i=10 60 61 USING POINTERS Pointers and dynamic memory 62 63 Recap Address operator & Pointer variables hold a reference to a memory location Operator & returns the int i_value = 0; reference to the operand double d_value; Pointers have type (int, float, ...) int *ptr; Pointer variables are defined with *, for example Type of the pointer int *a; double *d; matches with the type of // This is ok ptr = &i_value; Address of a variable can be obtained using address the operand operator & (same symbol as bit-wise AND) – That is, reference to an // Types have to match! int is of type pointer to ptr = &d_value; an integer, (int *) 64 65 Dereferencing pointers with * Functions with pointer arguments Dereferencing means int a = 10; All function arguments are passed “by value” int *ptr; accessing the value from – When function changes the values of arguments the the address that pointer // ptr points to a changes are not visible to the caller (no side effects) points to ptr = &a; When a pointer is passed as a function argument the Dereferencing is done function can not change the value of the pointer itself, // Let’s do some arithmetics using * operator (*ptr)++; but it can modify the referenced value Precedence can be changed with parentheses // and print the value of a () printf(“a=%d\n”, a); // Result: a=11 66 67 Pointer arguments example Pointer arithmetics #include 68 69 Dereferencing pointers with [] Special pointers For arrays it is more convenient to derefence the values Pointers to type void can be cast back and forth to any using [] operator other type – This was already used with static sized arrays – Provides a mechanism to implement type generic routines Definition of the [] operator is that – Several standard library routines use void pointers E1[E2] equals to (*(E1+(E2)), for example Pointers of any type can be assigned to value NULL – Can be used to check if a pointer is associated or not b=3*arr[n+1] is same as b=3*(*(arr+(n+1))) – Pointers are not initialized to NULL by default 70 71 Pointers to pointers Pointer to a pointer example It is possible to have pointer references to pointers #include printf(“a=%d\n”, **int_ptr_ptr); return 0; } // Result: a=5 72 73 Memory management in C C manages memory statically, automatically and dynamically – Static allocations are determined during compile time and MALLOC, REALLOC AND FREE required memory allocation is done when execution starts – Automatic memory management is done for e.g. variables defined inside a function body – Dynamic memory management is controlled by the program logic at runtime 74 75 Dynamic memory management malloc In most cases the exact size of all data structures is not malloc function is defined in header file stdlib.h: known at compile time because they depend on the void *malloc(size_t size); input data malloc returns a pointer of type void to a memory Dynamic memory management is accomplished by using location with allocated space of size size bytes pointers and controlling memory (de)allocation manually – If allocation fails, malloc returns NULL! Relevant functions are malloc(), realloc(), free() The memory area returned by the malloc is uninitialized! 76 77 How to determine the size for allocation? realloc The only argument for malloc function is the size of the realloc function changes the size of allocation, its object in bytes definition is The sizes of different objects can be determined using void *realloc(void *ptr, size_t size); sizeof operator which returns the size of the argument The argument ptr has to point to a previously allocated in bytes object – Return type of the sizeof operator is size_t – If ptr is NULL, realloc is equivalent to malloc Example: Contents of the allocated memory area returned by int *ptr = (int *) malloc(sizeof(int)); realloc is equal up to the lesser of old and new sizes float **ptr = (float **) malloc(sizeof(float *)); 78 79 free Summary free deallocates previous allocated object Pointers are references to memory locations void free(void *ptr); Operators *, & and [] After freeing you should not try to access any part of the Functions with pointer arguments can manipulate the allocation values of arguments Calling free with a pointer that does not point to a Pointers to pointers allocated memory can crash the code Dynamic memory management – Calling free twice is a common mistake! – malloc and free 80 81 STRINGS Structures and strings 82 83 Strings String manipulation Strings in C are arrays of type char that end with value 0 Concatenate: (not character 0!) char *strncat(char *s1, char *s2, size_t n); – This definition is problematic and has caused many critical Compare: bugs especially in operating systems int strncmp(char *s1, char *s2, size_t n); String manipulation routines are defined in header Copy: 84 85 Character manipulation String examples // two string buffers • Defined in header printf(“tolower(A) = %c\n”, tolower(‘A’)); // output: tolower(A) = a 86 87 Defining a structure Structures are defined using struct keyword: struct tag_name { type member1; type member2; STRUCTURES … }; Example: struct book { char author[100]; char title[100]; char publisher[100]; int year; }; 88 89 Declaring a structure variable Structures - why? Structure declarations are usually given at the beginning Structures can be considered as a collection of variables of the file before function definitions with (possibly) different datatypes – Accessible by all functions in the same file Code can be made more readable when all related Variables of a given structure type are declared using the information is passed between function using just a one struct keyword, for example argument of relevant structure – For example the information of a book in a library struct book item; Also implementing abstract datatypes, such as lists and binary trees, is much more convenient with structures declares a variable item of type struct book 90 91 Accessing structure members Pointer to structure Structure members can be accessed using ., for example There is a short-hand notation -> for accessing elements setting the year and printing the title for a book defined of a pointer to a structure: previously: struct book item; struct book item; struct book *ptr; ... ptr = &item; item.year = 1984; (*ptr).year = 2015; ... ptr->year = 2015; printf(“Author: %s\n”, item.author); 92 93 Pointer to structure Pointer to structure Structures can have struct node { int value; Example of ADT: binary tree other structures as struct node *next; members }; struct node { – Also a pointer to the struct node first, second; integer value; 7 structure itself! first.next = &second; struct node *left, *right; second.next = NULL; }; 6 9 – Abstract datatypes can be nicely defined using 2 8 11 structures with pointers first second 1 4 10 13 value value 3 5 12 *next *next NULL 94 95 Summary Strings in C always end with 0 – Remember the safety implications of string operations Structure is datatype that is a collection of members – Packing relevant data to a single unit – Abstract data types 96 Dynamic arrays malloc can be used to allocate space for arrays too When allocating an array just multiply the size of each element in the array by the number of elements – malloc returns a pointer to the beginning of the array – Elements can be accessed with normal pointer syntax Dynamic memory management 97 98 Dynamic arrays Dynamic multi-dimensional arrays Doable, but becomes complicated int n_elems = 32; float *prices; No real multi-dimensional arrays in C, so really just arrays // allocate memory for the required amount of floats of arrays prices = malloc(n_elems*sizeof(float)); for (i = 0; i < n_elems; i++) { – Two dimensional array maps to a variable that is a pointer prices[i] = i*1.23; to a pointer } // add space for one more float Memory management by hand prices = realloc(prices, sizeof(float)*(n_elems+1)); prices[n_elems] = 0.91; – There are at least two different ways to do the allocation // de-allocate the memory block free(prices); – Easy to make mistakes, beware here lieth dragons! 99 100 Allocating row-by-row, not recommended Dynamic multi-dimensional arrays int i; matrix[i] Dynamic 2D array in contiguous memory: int rows = 4, cols = 8; First, allocate memory for pointers to the first element of float **matrix; matrix each row /* allocate memory */ matrix[i][j] Second, allocate memory for all elements matrix = malloc(rows * sizeof(float *)); for (i = 0; i < rows; i++) Third, point each row at the first element of that row matrix[i] = malloc(cols * sizeof(float)); // start using the 2D array matrix[0][2] = 3.14; 101 102 Dynamic multi-dimensional arrays Memory layout /* allocate memory */ matrix = malloc(rows * sizeof(float *)); Allocating space for the whole array using a single malloc matrix[0] = malloc(rows * cols * sizeof(float)); call is the recommended way /* point the beginning of each row at the correct address – Number of expensive malloc calls is minimized */ for (i = 1; i < rows; i++) – Array is represented as one contiguous block in memory matrix[i] = matrix[0] + i * cols; – It can be copied without looping over rows // start using the 2D array – Most IO and numerical libraries assume contiguous storage matrix[0][2] = 3.14; matrix[i][j] matrix matrix[i] 103 104 Freeing multi-dimensional arrays Summary /* free each row first */ After using a dynamic Multidimensional arrays are a bit tricky and there are for (i = 0; i < rows; i++) { several different implementations free(matrix[i]); multi-dimensional } – Malloc are usually time consuming /* only after that, we can free the array, remember to main matrix */ – Memory layout matters free(matrix); free each array inside the main array OR /* alternatively, when using contiguous memory */ free(matrix[0]); free(matrix); 105 106 I/O – Introduction Common I/O design alternatives – databases – data format libraries – standard C libraries Standard C library stdio.h – Reading from keyboard and writing to display I/O – Reading and writing files 107 108 I/O – Reading from keyboard and I/O – Standard C library stdio.h writing to display To use: printf() Print formatted data to stdout. #include 109 110 I/O – Reading from keyboard and I/O – printf writing to display int printf(const char *format, ...) \a alarm (beep) character \0NN character code in octal printf("The answer is %d.\n", 42); \p backspace \xNN character code in hex printf("Pi equals to %.2f", 3.14159265); \f formfeed \0 null character printf("...at least to the %dnd decimal.\n", 2); \n newline \r carriage return /* output: \t horizontal tab * The answer is 42. \v vertical tab * Pi equals to 3.14 ...at least to the 2nd decimal. \\ backslash */ \? question mark \’ single quote \” double quote 111 112 I/O – printf I/O – printf The format specifier: %d decimal integer %g use %e or %f, %c character whichever is shorter %[parameter][flags][width][.precision][length]type %s string %u unsigned decimal n$ : which parameter [0-9] : left-pad w/ spaces l L : long int / float %e floating-point integer + : always show +/- ll : long long int number in %o unsigned octal - : left-align # : alternate form [0-9] : min. width / precision exponential notation integer 0 : pad w/ zeros %f floating-point %x unsigned hex integer * : width / precision given type: as a parameter number in d, i int x hexadecimal decimal notation f, e, g float / double o octal s string c char % literal % 113 114 I/O – Reading from keyboard and I/O – scanf writing to display int scanf(const char *format, ...) printf("Enter a number: "); When can you assume that the input is well-formatted? scanf("%d", &number); Never. printf("You entered %d.\n", number); Good article :“Things to Avoid in C/C++” at /* output: www.gidnetwork.com for discussion about gets() and * Enter a number: 9 * You entered 9. scanf(). */ 115 116 I/O – Reading from keyboard and I/O – Reading from keyboard and writing to display writing to display printf() Print formatted data to stdout. char *fgets(char *str, int num, FILE *stream) scanf() Read formatted data from stdin. printf("Enter some text\n"); putchar() Print a single character to stdout. fgets(string, 100, stdin); getchar() Read a single character from stdin. printf("You entered:\n"); printf("%s", string); puts() Print a string to stdout. /* output: fgets() Read a string from stdin or a file. * Enter some text: * Reading and validating input is difficult. * You entered: stdin = keyboard * Reading and validating input is difficult. stdout = display */ 117 118 I/O – fgets I/O – Reading and writing files fgets() FILE *fopen(const char *filename, const char *str \n char *str \0 char *mode) Input r, w, a read, write, append r+, w+, a+ read+write stream Input \0 \0 \0 stream rb, wb, ... binary mode int fclose(FILE *stream) int char float int fflush(FILE *stream) 119 120 I/O – fopen, fclose I/O – Reading and writing files char filename[] = "file.txt"; fprintf() Print formatted data to file. FILE *myfile; myfile = fopen(filename, "r"); fscanf() Read formatted data from file. if (myfile == NULL) { fputc() Print a single character to file. printf("Can’t open %s. Check that it ", filename); printf("exists and the permissions.\n"); fgetc() Read a single character from file. return EXIT_FAILURE; } fputs() Print a string to file. fclose(myfile); fgets() Read a string from file. /* output (on error): * Can’t open file.txt. Check that it exists and the * permissions. fwrite() Write binary data to file */ fread() Read binary data from file 121 122 I/O – Reading and writing files I/O – Reading and writing files int fgetc(FILE *stream) size_t fwrite(const void *ptr, size_t size, size_t count, FILE *stream) int c; char filename[] = "file.txt"; FILE *myfile; char filename[] = "file.dat"; myfile = fopen(filename, "r"); FILE *mybinary; c = fgetc(myfile); mybinary = fopen(filename, "wb"); while (c != EOF) { fwrite(&number, sizeof(int), 1, mybinary); printf("%c", (char)c); fwrite(array, sizeof(float), 1000, mybinary); c = fgetc(myfile); } fclose(myfile); 123 124 I/O – Reading and writing files I/O – Final words size_t fread(const void *ptr, size_t size, Standard C libraries size_t count, FILE *stream) – text or binary? Alternative solutions char filename[] = "file.dat"; FILE *mybinary; – databases mybinary = fopen(filename, "rb"); – data format libraries fread(&number, sizeof(int), 1, mybinary); fread(array, sizeof(float), 1000, mybinary); 125 126 C PREPROCESSOR Code structuring 127 128 Preprocessing directives Directives C preprocessor is a part of the compiler that does initial Preprocessor directives start with #, which has to be first text substitution, manipulation, inclusion and other token on a line activities before the actual translation is done Directives are limited to one line – We have already used #include, which includes a file and – Line can be continued using \ #define for macros Directives are not statements, do not end the line with ; C relies heavily on preprocessor to accomplish – Portability of code // These are ok #define one – Source code control #define two – Debugging // Not the first token - WRONG! int i; #define one 129 130 Conditional inclusion with #if Example Conditionals can be used to control if part of the code is #include 131 132 Definitions on compiler command line It is also possible to set preprocessor definitions on the compiler command line Most compilers accept option -D for this purpose: COMPILING WITH SEVERAL FILES gcc -DONE=1 -DUSE_FEATURE is equivalent with #define ONE 1 #define USE_FEATURE 133 134 Compilation: working with several files Function prototypes Advantages: Functions, like variables, have to be defined (declared) – Code structure is easier to understand when related parts before use are in same file Program execution starts from main() . Scoping of variables can be controlled more strictly – If function definitions are in the same file and definitions – Changing a file, only that file needs to be re-compiled to are always before usage then compiler can find everything rebuild the program it needs UNIX ‘make’ can be very useful tool for this! – Otherwise we have to introduce the functions using – Most IDEs also provide a tools for building this kind of function prototypes compilation 135 136 Function prototypes Working with several files Function prototypes are like // Function prototype We can use header files // we implement the function ‘add’ variable declarations: they int funcA(int, int); to define functions that int add(int first, int second) // Function can now be used { introduce the name of the we can use later return first + second; int main(void) { } function, its arguments and ... Making .h files for your File: add.c return value val = funcA(arg1, arg2); functions allows you to ... Note that only types of } ‘include’ them in your // we define the function ‘add’ #ifndef ADD_H arguments are needed // Function definition code #define ADD_H int funcA(int a, int b) int add(int first, int second); Function prototype declaration #endif is a statement so you have to { return a * b; File: add.h finish it with ; } 137 138 Compilation: working with several files Compilation: working with several files Another .c file can then use the function add() by So, this is how headers work: including the header file: – main() would be in one file, the others will contain functions // If we use #include "", the file is searched from – Headers usually only contain definitions of data types, // current directory #include "add.h" function prototypes and C preprocessor commands – We include the header into the C files int example(int x) { – We compile the different files and the compiler calls the return add(x, add(x,x)); } header file 139 140 Global variables, extern Example Scoping of variables depend on the place where the variable is declared file main.c: file utilities.c: – Variables declared inside a function are visible only in the ...... scope of that function // File scope variable for // We want to use the same // status // structure in this file Variables defined in a file have a scope of that file struct status global_status; extern struct status global_status; ...... If a truly global value is needed (not generally int main(void) { int set_error_status(int stat) { recommended practice), one has to use extern keyword ...... – global_status.error_code = stat; Only one definition in file scope ... – Other files refer this value with extern } 141 142 Summary C preprocessor – Macros, conditional compiling Working with several files – Function prototypes – Compiling and linking 143 BACKGROUND Programming practices 144 145 What and why? Software quality Coding practices are an informal set of rules that Several definitions and metrics, but some common community has learned by experience desirable attributes are – Purpose is to help improve the quality of software – Maintainability . How easy the code is to modify and extend? Some practices may seem unimportant if you are just – Reliability starting to write a small ad-hoc application for yourself . Does program work correctly with different inputs? – Software lifetime is hard to predict—small application can – Efficiency grow and become important . Is the program fast enough for the purpose it was made for? – Wrong choices are much harder to fix when project grows – Correctness and more developers start to contribute . Does the program implement the given specifications correctly? 146 147 Keep it simple Code should be simple, do not use complicated logic to implement simple things – Complicated algoritms are of course difficult, but individual CODING STANDARDS parts of code should not be! Complicated code filled with tricks is difficult to understand, fix and modify – After couple of years it will be hard even for the person who wrote it 148 149 Comments Code formatting Easily overlooked, but // Do not comment like this As mentioned already in the // GNU very important for new beginning the C code for (i = 0; i < n; i++) // Increment i by one { developers working on i++; formatting is very flexible printf(“Hi!\n”); old code – There are many common } Comment at least the // This is more useful styles: K&R, GNU, Linux, ... print(“Finished\n”); purpose of different data Rule of thumb: be consistent! // Linux // Increment i so that for (i = 0; i < n; i++) { structures and things that // the residual will be – It is much easier to read printf(“Hi!\n”); use complicated logic // computed correctly code that has all blocks } i++; formatted similarly print(“Finished\n”); Use automatic tools: indent 150 151 Naming conventions Version control There are several conventions on naming, but again, If you do any development you should already be using choose one and use it version control In any case the name of variables and fuctions should be – It is helpful even in projects with just one developer descriptive – ... but it is critical tool for projects with larger teams Preprocessor macros and definitions should always be Without VCS tracking bug fixes and changes becomes uppercase! almost impossible // This Common version control systems include git, svn, d = s * t; mercurial, cvs, perforce,... // ... or this distance = speed * time_interval; 152 153 Build tools When the size of the software project grows compiling becomes more and more complicated – Recompiling everything from the beginning becomes too slow and handling the dependencies can be complicated DEBUGGING Build tools handle the dependencies Most common tool in UNIX-like systems is Make IDEs also provide build tools 154 155 Debuggers Debugging demo Debuggers are tools that are used to examine the Demo on tracking down the cause of segmentation fault program execution – Check the values of variables during execution without adding any extra I/O calls – Execute the program code step-by-step – Stop the program execution when a given condition is met – Examine the call tree and function call arguments 156 157 Common pitfalls 1 Segmentation faults Arrays in C are not protected – Trying to access a wrong element can in principle have any COMMON PITFALLS effect – Most difficult cases to debug are the ones where value of some other variable is modified by accident – Segmentation faults are usually easier to find – Some compilers have memory debugging tools integrated (e.g. address sanitizer in Clang and gcc) 158 159 Common pitfalls 2 Common pitfalls 3 Using = in comparison in stead of == Manual memory management is error prone int val = 2; Make sure that the allocations are always done before if (val = 1) trying to dereference pointers printf(“val is equal to one”); Memory leak: This will print that the value is one! – Releasing the pointer to an allocation before free – The assignment operator returns the assigned value 1 which is nonzero and interpreted as true One way to avoid this is to use the constant on left, compiler will complain about the invalid assignment 160 161 Common pitfalls 4 Summary Remember the implicit type conversions Coding standards help to develop programs For example integer division: – Based on long time experience double third = 1/3; Debuggers help when you track down errors in your This will set the value of third to zero programs – Result of the integer division is 0 and that value is then casted into double precision 0.0 162 163