@@ -345,7 +345,7 @@ char * string_realign (char * string, int amount, char character) { // | |||
} | |||
/* | |||
Ignore what next two functions do, it's about memory management that we'll cover in later chapters. | |||
Ignore what next two functions do, it's about memory stuff that we'll cover in later chapters. | |||
*/ | |||
int memory_compare (void * destination, void * source, int length) { | |||
@@ -387,8 +387,7 @@ void memory_copy (void * destination, void * source, int length) { | |||
} | |||
/* | |||
Again, please consider these 'terminal_*' functions black magic, as well as 'number_to_string' and 'format_to_string' as they are more complex to cover them at this point, we'll | |||
talk more about them later... For now, just take a look at how I format the code in them. | |||
Again, please consider these 'terminal_*' functions some form of black magic... For now, just take a look at how I format the code in them. | |||
*/ | |||
void terminal_clear (void) { | |||
@@ -174,8 +174,9 @@ int file_size (char * name) { | |||
int size = -1; // Lets just assume that everything is wrong, everything falls apart... | |||
int file = -1; // Everything is just -1 around us... | |||
file = file_open (name, O_RDONLY); // We open a file to read it. | |||
size = (int) lseek (file, 0, SEEK_END); // We set the offset to the end of the file. | |||
file = file_open (name, O_RDONLY); // We open a file to read it. | |||
size = (int) lseek (file, 0, SEEK_END); // We set the offset to the end of the file. | |||
fatal_failure (size == -1, "file_size: Failed to get size of file, invalid file size."); // Again, error of 'lseek' would be -1, so we check for that... | |||
@@ -114,8 +114,8 @@ goes for functions declared in "chapter_2.h", but there's another trick to this. | |||
@C | |||
// Option A: | |||
static void * my_function (char * name, int size); | |||
static int my_function (char * name, int size); | |||
// ... | |||
int my_function (char * name, int size) { | |||
int data = 0; | |||
@@ -136,7 +136,7 @@ static int my_function (char * name, int size) { | |||
Okay, external variables you see lower (without 'static') are all integers. Lets briefly see what types are the internal variables. | |||
- curses_format / curses_format: Array of characters (also known as string!), and their size is already known at the compile time, it's inside square braces. | |||
- curses_format / curses_cursor: Array of characters (also known as string!), and their size is already known at the compile time, it's inside square braces. | |||
- curses_screen: Pointer to character, we'll allocate memory for it later, so it'll be a dynamic array of characters, its' size can change. | |||
- curses_activator: Pointer to integer, we'll also allocate memory for it, so we can't use square braces for those. Remember that for later. | |||
- curses_action: Strictly speaking, pointer to another pointer to function of signature 'void SOMETHING (void);', but ignore it for now, don't get confused. | |||
@@ -255,13 +255,15 @@ comes from top of my head is 'echo' function from chapter zero. It'd be boring t | |||
string literals to terminal in most cases. But that doesn't mean that 'out' is useless, as you'll see, we'll use it in 'curses_synchronize' funcion. Cons are, if you really have | |||
repeating code all over your program, if there's a bug in one of them, there's a bug in all of them. Also, if you'll use some extracted function (also refered to as refactored in | |||
some cases) only once, it's not a bad thing. We've extracted some code into 'curses_initialize' function, and we call it only once, inside 'curses_configure' function, but the | |||
intention behind what it does is clear. However, I still believe that procedural code, that's executed line by line, from top to bottom, is best. | |||
intention behind what it does is clear. However, I still believe that procedural code, that's executed line by line, from top to bottom, is best. You can use 'curses_configure' | |||
and main loop with 'curses_synchronize' inside more functions, and it'll clean itself after the program exits. | |||
Now, we could also implement some error checking functions for 'curses_*' functions. Good idea, when something is wrong, like we want to render a character out of the screen, we | |||
just print an error message to terminal, right? Well, no. Remember, we're using terminal as a framebuffer (about which we'll talk about a lot more in ray tracing and rasterization | |||
chapters), so if we just print message there, it'll corrupt our framebuffer. So, we could just write them to some log file. How about binding them, whenever something is wrong, | |||
we don't just abort the program or stop rendering, but continue running it, while also writing error messages to that same file. Then, when we exit the program, it'll print all | |||
error messages (if any) normally. Here's how it would look like: | |||
error messages (if any) normally. It could be done like something below, but since I know what I'm doing (for the most part), I'll just comment them out inside functions, and | |||
leave 'log_in' and 'log_out' unimplemented. | |||
@C | |||
// Enumeration for log type. | |||
@@ -428,6 +430,7 @@ void curses_render_character (char character, int colour, int effect, int x, int | |||
if ((x < 0) || (x > curses_screen_width - 1) || (y < 0) || (y > curses_screen_height - 1)) { // If any of these are true, we don't render. | |||
return; | |||
} | |||
// Again, lets show some code formatting examples: | |||
// if ((x < 0) | |||
// || (y < 0) | |||
@@ -9,7 +9,7 @@ It is distributed in the hope that it will be useful or harmful, it really depen | |||
#ifndef CHAPTER_2_HEADER | |||
#define CHAPTER_2_HEADER | |||
#include <stdio.h> // We need this header file for functions 'puts' and 'printf'. | |||
#include <stdio.h> // We need this header file for functions 'puts' and 'printf' in 'hello_world' functions below. | |||
#include <unistd.h> // And in this header file we have write system call. | |||
/* | |||
@@ -17,7 +17,8 @@ Lets talk about function declaration. In C, sometimes you need to declare functi | |||
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. | |||
declarations should be in C header file, and function definitions should be in C source file. You can see how I declare functions in '.h' files, and define them in '.c' files, | |||
unless I don't want to make some function external in some '.c' file, I use 'static' there. | |||
@C | |||
// Function declaration: // # // Output: // Input: | |||
@@ -55,7 +56,8 @@ Now, lets talk very briefly about what's wrong with 'PLEASE_NO': | |||
- 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. | |||
Very soon, you'll be able to write your own small C programs, so prepare for it. | |||
Very soon, you'll be able to write your own small C programs, so prepare for it. Also, the most important chemical element for organisms is, you guessed it, C (carbon). It's | |||
the same in programming too, C is the most important language that a good programmer should know. Other languages are just extra. Now: | |||
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. | |||
@@ -116,7 +118,8 @@ These below are external variables, they can be accessed and modified in any fil | |||
output object files (.o), and then we can link them together (most compilers are also linkers, this is nothing out of ordinary) into final executable. We can, for example, use | |||
functions declared in "chapter_2.h" and defined in "chapter_2.c" in completely separate file "chapter_3.c", if we have included it with '#include "chapter_2.h". Keep in mind that | |||
header files can be included recursively, that's why we use those header guards, '#ifndef SOMETHING', '#define SOMETHING' and '#endif' at the end. That way, they'll be included | |||
only once, so compiler won't be confused and spit out errors. | |||
only once, so compiler won't be confused and spit out errors. You can see how I use those chapters to create new programs, and all that is in 'program/' folder. You can also just | |||
open some 'compile.sh' file or 'makefile' and see how I compile and link. | |||
Now, lets talk about ASCII escape sequences. Most terminals support some special combination of characters, that aren't printed like you'd normally expect. We call them escape | |||
sequences because they all start with character literal '\033', '\x1b' or '\e', which is same as decimal 27, and in our character enumeration 'CHARACTER_ESCAPE'. They are somewhat | |||