<h1>Chapter 3 - Functions</h1> Function return type has to be defined beforehand. C program always has a minimum of one function called `main`, which is called on program run. Function syntax: ``` return_type function_name(parameter_list) { function body } ``` Void in place of `parameter_list` indicates the function takes no parameters. Example of a function that returns an integer: ```clike= #include <stdio.h> /* Declaration of subfunction */ int addition(int num1, int num2); int main(void) { printf("Program starts.\n"); /* Defining variables of type int */ int num1 = 3; int num2 = 5; /* Calling function and giving num1 & num2 as parameters. Saving result in sum */ int sum = addition(num1, num2); printf("%d + %d = %d", num1, num2, sum); printf("\nProgram ends."); return(0); } /* Initializing the subfunction with code */ int addition(int num1, int num2) { int sum = num1 + num2; return(sum); } ``` **Program run** ```txt Program starts. 3 + 5 = 8 Program ends. ``` Functions can either have "value parameters" or "variable parameters". Previous function example used value parameters meaning modification of the values was not possible in the subfunction. Example of "variable parameters". ```clike= #include <stdio.h> void modifyValue(int *pValue); int main(void) { int iNum=1; /* Making pointer for iNum */ int *pNum; /* Setting pointer to point to address of iNum */ pNum = &iNum; printf("pNum points to address %p, where value is: %d.\n", pNum, *pNum); modifyValue(pNum); printf("pNum points to address %p, where value is now: %d.\n", pNum, *pNum); return(0); } void modifyValue(int *pValue) { *pValue = 1000; } ``` **Program run** ```stdio pNum points to address 0000008A823FFAD4, where value is: 1. pNum points to address 0000008A823FFAD4, where value is now: 1000. ``` Example breakdown: ``pNum`` is a pointer to the variable ``iNum``. When we pass ``pNum`` to the function ``modifyValue``, the function modifies the value of ``iNum`` by accessing the value at the memory address and assigning a new value to it. # Writing a file ```clike= #include <stdio.h> #include <string.h> int main(void){ char row[50]; // name doesn't need to be "row" FILE *my_file; my_file = fopen("my_file.txt", "w"); printf("What do you want to write to the? (max 48 characters)\n"); fgets(row, 50, stdin); row[strlen(row)-1] = '\0'; fprintf(my_file, "%s\n", row); fclose(my_file); return(0); } ``` We open the file with 'w' to write to it. We write one line and close the file. **Program run** ``` What do you want to write to the? (max 48 characters) cat says meow ``` # Reading the file Continuing the last example we will now read the contents of "my_file.txt" ```clike= #include <stdio.h> int main(void) { char row[50]; FILE *my_file; // File handler ? my_file = fopen("my_file.txt", "r"); printf("File contents:\n"); while (fgets(row, 50, my_file) != NULL) { printf("%s", row); } fclose(my_file); return(0); } ``` **Program run** ``` File contents: cat says meow ``` fgets reads and returns one row from the file. This is limited to 48 chars + \n + \0. There are multiple functions for file reading. # File opening error handling In C if the function returns an error code we should handle it accordingly. These error codes are found in the `man` pages of the function in question. However in pointer errors the typical return is NULL. Opening the file is the most common point of error and so it should be checked allways. You could use `printf` to inform the user of the error but we have `perror` for this. In combination with `perror` we should also exit the program with the `exit` function that is found in `stdlib.h`. ```clike= /* example error handling */ if ((my_file = fopen("my_file.txt", "w")) == NULL) { perror("File opening failed"); exit(0); } ``` # Binary filehandling The basic principles of filehandling like opening, error handling and closing the handle still apply but binarydata has its own functions. The normal parameters for mode "r" and "w" in fopen change to "rb" and "wb" (read- and write binary). ## Writing into a binary ```clike= #include <stdio.h> int main(void) { int i, number; FILE *my_binary; printf("Program starting\n"); my_binary = fopen("my_binary.bin", "wb+"); //Read and write for (i=0; i < 10; i++) { fwrite(&i, sizeof(i), 1, my_binary); } printf("Writing succesfull!\n"); rewind(my_binary); // returning the filehandle pointer to start of file /* Alternatively could use fseek(my_binary,0,SEEK_SET); or could close the filehandle and open it again If this is not done pointer will read undefined memory */ for (i=0; i < 10; i++) { fread(&number, sizeof(number), 1, my_binary); printf("%d ", number); } printf("\nProgram ends\n"); fclose(my_binary); return(0); } ``` **Program run** ```txt Program starting Writing succesfull! 0 1 2 3 4 5 6 7 8 9 Program ends ``` # Other stream related functions File open syntax: ```clike stream = fopen(filename, mode) ``` * Creates `FILE` object, which represents the file stream * Returns pointer `*` to the `FILE` object * stream should be `FILE*` pointer variable * First parameter expects filename as a character array e.g., `"file.txt"` * File object modes are: `r`,`w`,`a`,`r+`,`w+`,`a+` * More information about them with command `man fopen` ```clike= // Closes the stream fclose(stream) // Reads the stream fscanf(stream,formatedstring,variablelist) // Writes to the stream fprintf(stream,formatedstring,variablelist) // Reads a line with your defined amount - 1 and writes it in your pointer fgets(*str,max number of characters, stream) // Writes your string to the stream fputs(*str, stream) // These are for binary file handling // Reads from stream in chunks for certain amount of times then stores in your variable fread(&str, size, amount, stream) // Reverse of read stores your variable in chunks to the stream fwrite(&str, size, amount, stream) ``` Other useful functions, more info on them on the `man` pages * fgetc(stream) * fputc(char, stream) * rewind(stream) * ftell(stream) * fseek(stream,location,start) * feof(stream) * remove(file) * rename(name, new_name)