diff --git a/chapters/chapter_0.c b/chapters/chapter_0.c index 2db04a3..2bf053e 100644 --- a/chapters/chapter_0.c +++ b/chapters/chapter_0.c @@ -50,7 +50,7 @@ void echo (char * string) { } void fatal_failure (int condition, char * message) { // We use this function to abort the program if condition is met and to print the message. - if (condition != 0) { // If the variable 'condition' is not equal to 0, we execute the code in curly braces. + if (condition == FALSE) { // If the variable 'condition' is not equal to 0, we execute the code in curly braces. echo ("[\033[1;31mExiting\033[0m] "); // Simply printing the message using our 'echo' function, but we also use some colours, more on that later. echo (message); // Also, notice how "this or that" is implicity 'char *' type... Maybe it's too early to explain it at this point. echo ("\n"); // This will only print a new line, we'll see how to use it later. @@ -79,10 +79,10 @@ Some examples of using them directly (not wrapping them like I like to do) are: @C char * data = NULL; -data = malloc (20 * sizeof (* data)); // Allocates 20 bytes of memory for 'data'. -data = calloc (20, sizeof (* data)); // Allocates 20 bytes also, but initializes them to 0 value. +data = malloc (20 * sizeof (* data)); // Allocates 20 bytes of memory for 'data'. +data = calloc (20, sizeof (* data)); // Allocates 20 bytes also, but initializes them to 0 value. data = realloc (data, 20 * sizeof (* data)); // When 'data' is null pointer, it will be same as 'malloc', else it will reallocate more memory (for correct usage). - + // Also, it's best to just use 'calloc', but it complicates some other tasks. free (data); // Deallocates memory, we'll talk about "double free" later. @ */ @@ -120,36 +120,79 @@ void * deallocate (void * data) { return (NULL); } +/* +This program is intended to be a book-like guide for this source code, which is also a book. We'll deal with strings a lot, and they're a good example of code formatting which is +the main topic of chapter zero. In function 'string_length' we have for loop without a body, some people prefer to put '{}' or ';' in same or next line, to express the intention +that the loop shouldn't have a body (code block {}). I just put ';' on the same line. Also, functions 'string_*' could depend on functions 'string_*_limit', but we won't do that +now, and since we've already declared them in header file "chapter_0.h" we can define them and call them in whatever order we want. Nice. + +@C +// Simple example of how we could make it dependable on 'string_*_limit' function... + +int string_compare (char * string_0, char * string_1) { + return (string_0, string_1, string_length (string_0)); +} +@ +*/ + int string_length (char * string) { int length; - if (string == NULL) { - return (0); - } + fatal_failure (string == NULL, "string_length: String is null pointer."); - for (length = 0; string [length] != '\0'; ++length); + for (length = 0; string [length] != CHARACTER_NULL; ++length); // Since in C, strings are null terminated, looping until we see null character is strings' length. return (length); } +/* +Now, I've implemented "unlimited" versions of string comparison, copying and concatenation different from "limited" versions. They correspond with standard library functions +'strcmp', 'strcpy', 'strcat', 'strncmp', 'strncpy' and 'strncat' found in header file . In "unlimited" versions, I rely on the fact that we want to apply the operation +on entire strings, that those strings are null terminated and I used that in my advantage. For example, function 'string_compare' could be something like this: + +@C int string_compare (char * string_0, char * string_1) { - if (string_length (string_0) != string_length (string_1)) { - return (0); - } else { - return (string_compare_limit (string_0, string_1, string_length (string_0))); + int offset; + + fatal_failure (string_0 == NULL, "string_compare: Destination string is null pointer."); + fatal_failure (string_1 == NULL, "string_compare: Source string is null pointer."); + + for (offset = 0; (string_0 [offset] != CHARACTER_NULL) && (string_1 [offset] != CHARACTER_NULL); ++offset) { + if (string_0 [offset] != string_1 [offset]) { + return (FALSE); + } } + + return (TRUE); +} +@ + +And I used this approach below to show that you can solve the problem using different solutions... +*/ + +int string_compare (char * string_0, char * string_1) { + fatal_failure (string_0 == NULL, "string_compare: Destination string is null pointer."); + fatal_failure (string_1 == NULL, "string_compare: Source string is null pointer."); + + for (; (* string_0 != CHARACTER_NULL) && (* string_1 != CHARACTER_NULL); ++string_0, ++string_1) { + if (* string_0 != * string_1) { + return (FALSE); + } + } + + return (TRUE); } char * string_copy (char * string_0, char * string_1) { - int i = 0; - fatal_failure (string_0 == NULL, "string_copy: Destination string is null pointer."); fatal_failure (string_1 == NULL, "string_copy: Source string is null pointer."); - for (i = 0; i != string_length (string_1) + 1; ++i) { - string_0 [i] = string_1 [i]; + for (; * string_1 != CHARACTER_NULL; ++string_0, ++string_1) { + * string_0 = * string_1; } + * string_0 = * string_1; // Copying null termination, since the loop stopped on that condition. + return (string_0); } @@ -159,66 +202,60 @@ char * string_concatenate (char * string_0, char * string_1) { string_0 += string_length (string_0); - while (* string_1 != '\0') { - * string_0++ = * string_1++; - /*++string_0; - ++string_1;*/ + for (; * string_1 != CHARACTER_NULL; ++string_0, ++string_1) { + * string_0 = * string_1; } - * string_0 = '\0'; + * string_0 = CHARACTER_NULL; return (string_0); } int string_compare_limit (char * string_0, char * string_1, int limit) { - int i = 0; + int offset; fatal_failure (string_0 == NULL, "string_compare_limit: Destination string is null pointer."); fatal_failure (string_1 == NULL, "string_compare_limit: Source string is null pointer."); - for (i = 0; i != limit; ++i) { - if (string_0 [i] != string_1 [i]) { - return (0); + for (offset = 0; offset != limit; ++offset) { + if (string_0 [offset] != string_1 [offset]) { + return (FALSE); } } - return (1); + return (TRUE); } char * string_copy_limit (char * string_0, char * string_1, int limit) { - int i = 0; + int offset; fatal_failure (string_0 == NULL, "string_copy_limit: Destination string is null pointer."); - fatal_failure (string_1 == NULL, "string_copy_limit: Source string is null pointer."); - if (limit <= 0) { + if ((limit <= 0) || (string_1 == NULL)) { return (string_0); } - for (i = 0; i != limit; ++i) { - string_0 [i] = string_1 [i]; + for (offset = 0; offset != limit; ++offset) { + string_0 [offset] = string_1 [offset]; } return (string_0); } char * string_concatenate_limit (char * string_0, char * string_1, int limit) { - int i = 0; - int length_0 = 0; - int length_1 = 0; + int offset, length_0, length_1; fatal_failure (string_0 == NULL, "string_concatenate_limit: Destination string is null pointer."); - fatal_failure (string_1 == NULL, "string_concatenate_limit: Source string is null pointer."); - if (limit <= 0) { + if ((limit <= 0) || (string_1 == NULL)) { return (string_0); } length_0 = string_length (string_0); length_1 = string_length (string_1); - for (i = 0; (i != length_1) && (i != limit); ++i) { - string_0 [length_0 + i] = string_1 [i]; + for (offset = 0; (offset != length_1) && (offset != limit); ++offset) { + string_0 [length_0 + offset] = string_1 [offset]; } return (string_0); diff --git a/chapters/chapter_0.h b/chapters/chapter_0.h index 41f5ea1..8f477ea 100644 --- a/chapters/chapter_0.h +++ b/chapters/chapter_0.h @@ -167,14 +167,9 @@ eye on how are they aligned and named. I'll reimplement some standard functions, #define SIGNAL_ARROW_RIGHT (0X435B1B) #define SIGNAL_ARROW_LEFT (0X445B1B) -enum { // I like to align enumerations with 10, 20 or 40 characters per name, and optionally use NAME_ as prefix and NAME_COUNT as last element. - EFFECT_NORMAL, EFFECT_BOLD, EFFECT_ITALIC, EFFECT_UNDERLINE, EFFECT_BLINK, EFFECT_REVERSE, - EFFECT_COUNT -}; - -enum { // Because of text auto-completition, it's always easy to find what I want, or to use NAME_COUNT in arrays. In some cases, order matters! - COLOUR_GREY, COLOUR_RED, COLOUR_GREEN, COLOUR_YELLOW, COLOUR_BLUE, COLOUR_PINK, COLOUR_CYAN, COLOUR_WHITE, - COLOUR_COUNT +enum { // This is completely unnecesary, but I don't care, it's a good showcase how boolean type can work, true is 1 and false is 0. + FALSE, + TRUE }; enum { // This is also one of my preferences, to use CHARACTER_NULL or CHARACTER_LINE_FEED instead of '\0' or '\n' in special (non-string) cases. @@ -189,6 +184,16 @@ enum { // This is also one of my preferences, to use CHARACTER_NULL or CHARACTER CHARACTER_COUNT }; +enum { // I like to align enumerations with 10, 20 or 40 characters per name, and optionally use NAME_ as prefix and NAME_COUNT as last element. + EFFECT_NORMAL, EFFECT_BOLD, EFFECT_ITALIC, EFFECT_UNDERLINE, EFFECT_BLINK, EFFECT_REVERSE, + EFFECT_COUNT +}; + +enum { // Because of text auto-completition, it's always easy to find what I want, or to use NAME_COUNT in arrays. In some cases, order matters! + COLOUR_GREY, COLOUR_RED, COLOUR_GREEN, COLOUR_YELLOW, COLOUR_BLUE, COLOUR_PINK, COLOUR_CYAN, COLOUR_WHITE, + COLOUR_COUNT +}; + extern void in (void * data, int size); // We'll use these functions later as core standard input/output functions. extern void out (void * data, int size);