/* Copyright (c) 2023 : Ognjen 'xolatile' Milan Robovic Xhartae is free software! You will redistribute it or modify it under the terms of the GNU General Public License by Free Software Foundation. And when you do redistribute it or modify it, it will use either version 3 of the License, or (at yours truly opinion) any later version. It is distributed in the hope that it will be useful or harmful, it really depends... But no warranty what so ever, seriously. See GNU/GPLv3. */ #ifndef CHAPTER_2_HEADER #define CHAPTER_2_HEADER /* This is probably the first program new programmers write in language they're learning. It simply prints text to standard output. As C is very old programming language, you have a lot of ways to do it, so we'll simply put all of them into array of function pointers, and call them sequentially in loop in our main function. */ #include // We need this header file for functions 'puts' and 'printf'. #include // And in this header file we have write system call. /* Lets talk about function declaration. In C, sometimes you need to declare functions that you'll use before they're called. I prefer to always declare functions except when I'm quickly prototyping something out. Function declarations are property of old programming languages, you don't have to use them if you're calling every function after it's defined (but not declared, there's a difference), but if that function is only used in one file, you should use 'static' keyword like in the example below. Keyword 'extern' tells the compiler that your function will be used in other files, or if you're compiling your files separately, it won't add the function address (in some cases). In any case, function declarations should be in C header file, and function definitions should be in C source file. @C // Function declaration: // # // Output: // Input: extern void function_0 (void); // 0 // undefined (none) // undefined (none) extern float function_1 (double a); // 1 // ieee754_32b // ieee754_64b extern int function_2 (int a, int b); // 2 // integer_32b // integer_32b, integer_32b static unsigned int function_3 (char * a, int b, char c); // 3 // natural_32b // integer_8b_pointer, integer_32b, integer_8b static char * function_4 (char * a); // 4 // integer_8b_pointer // integer_8b_pointer static void * function_5 (struct s * a, void * b); // 5 // undefined_pointer // [structure]_pointer, undefined_pointer extern unsigned long int * PLEASE_NO (const signed short int, void *, const char const * *, long double []); @ So, lets go through those 6 normal functions, and then lets see what's wrong with 'PLEASE_NO' function. 0) This is the simplest form of function, which can be used to make the code more clear (sometimes) by breaking down long functions into smaller ones, and calling them in certain order. However, don't abuse it for code refactoring, because most of the time, procedural code is the easiest to read, write and debug. 1) In C (and in non-embedded environment, also known as your normal post-2012 laptop or computer), keyword 'float' is (almost) real number encoded in IEEE754, 32 bits or 4 bytes long, while keyword 'double' is same as float, but enlarged to 64 bits or 8 bytes. They are pain to use in some cases because of limited precision. 2) Functions can have multiple arguments (input variables), but only one return value (output "variable"). In C, you can name the arguments, but not the return value, also keep in mind that you should choose descriptive names (in most cases) for function arguments, not name them a, b, c, d like in these examples. 3) If you really want the compiler to verify that some return value is of type 'unsigned int' aka 'uint32_t', you can do so, I prefer 'int' since all number literals in C are integers by default ('int'). We'll use terms like 'int / integer/ int32_t' interchangably, so keep that in mind in future chapters. 4) Also, type 'char *' can be pointer to (address of) of some 'char' typed variable or array of type 'char'. We'll talk more about arrays in future chapters, but know that we could use 'char a []' in functions 3 and 4 as well, it's same. You could use them both in order to know which is array and which is pointer. 5) Lastly, this function returns pointer to any type, hence compiler won't do type-checking, it's necesary to this sometimes. It also accepts pointer to (previously defined) structure 's', and if you used 'typedef' with it, you could just say 's * a', we'll talk more about those. Now, lets talk very briefly about what's wrong with 'PLEASE_NO': - Don't use long types 'unsigned long int', if you really type with those characteristics, use 'size_t : stdlib', 'uint64_t : stdint' or 'unsigned long' in worst case. - Don't use 'const' keyword at all, that's my honest advice, it won't catch that many potential bugs in C, it'll just cause useless compiler warnings. Live unsafe, think more. - Don't use 'signed', since it's default for any integer type, as well as you shouldn't use keywords 'register', 'volatile', 'auto', but more on that later... - You can use '[]' in function declarations, but it doesn't mean much since array is passed as pointer to first element in C (array decay), so '*' is enough. - Keep in mind that newer (non-ANSI) C standards allow some of the things that I'm not using here, but I don't personally like newer C standards, I'll mention that a lot. - Last one is tricky, you should name function agruments in function declarations, but some linters will warn you not to do it, since some compiler don't check them. */ extern void hello_world_0 (void); // All 4 functions take no agruments, and return nothing. They just execute the code that's defined in them. extern void hello_world_1 (void); extern void hello_world_2 (void); extern void hello_world_3 (void); extern void (* hello_world [4]) (void); // External (global) variable with name 'hello_world' of type 'array of 4 function pointers with no input/output'. #endif