forked from xolatile/xhartae
Initial testing upload... A lot will change...
This commit is contained in:
parent
9b0e2e36d4
commit
6ac2d0d414
90
chapters/chapter_0.c
Normal file
90
chapters/chapter_0.c
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
/*
|
||||||
|
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_0_SOURCE
|
||||||
|
#define CHAPTER_0_SOURCE
|
||||||
|
|
||||||
|
#include "chapter_0.h"
|
||||||
|
|
||||||
|
void in (void * data, int size) {
|
||||||
|
fatal_failure (data == NULL, "in: Failed to read from standard input, data is null pointer.");
|
||||||
|
fatal_failure (size == 0, "in: Failed to read from standard input, size is zero.");
|
||||||
|
|
||||||
|
(void) read (STDIN_FILENO, data, (unsigned long int) size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void out (void * data, int size) {
|
||||||
|
fatal_failure (data == NULL, "out: Failed to write to standard output, data is null pointer.");
|
||||||
|
fatal_failure (size == 0, "out: Failed to write to standard output, size is zero.");
|
||||||
|
|
||||||
|
(void) write (STDOUT_FILENO, data, (unsigned long int) size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void echo (char * string) {
|
||||||
|
out (string, string_length (string));
|
||||||
|
}
|
||||||
|
|
||||||
|
void fatal_failure (int condition, char * message) {
|
||||||
|
if (condition != 0) {
|
||||||
|
echo ("[\033[1;31mExiting\033[0m] ");
|
||||||
|
echo (message);
|
||||||
|
echo ("\n");
|
||||||
|
|
||||||
|
exit (EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void limit (int * value, int minimum, int maximum) {
|
||||||
|
if (value == NULL) { /* We shouldn't dereference null pointer, but also don't want to abort the program for small mistake. */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (* value <= minimum) {
|
||||||
|
* value = minimum;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (* value >= maximum) {
|
||||||
|
* value = maximum;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void * allocate (int size) {
|
||||||
|
char * data = NULL;
|
||||||
|
|
||||||
|
if (size <= 0) {
|
||||||
|
return (NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
data = calloc ((unsigned long int) size, sizeof (* data));
|
||||||
|
|
||||||
|
fatal_failure (data == NULL, "standard : allocate : Failed to allocate memory, internal function 'calloc' returned null pointer.");
|
||||||
|
|
||||||
|
return ((void *) data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void * reallocate (void * data, int size) {
|
||||||
|
if (size <= 0) {
|
||||||
|
return (data);
|
||||||
|
}
|
||||||
|
|
||||||
|
data = realloc (data, (unsigned long int) size);
|
||||||
|
|
||||||
|
fatal_failure (data == NULL, "standard : reallocate: Failed to reallocate memory, internal function 'realloc' returned null pointer.");
|
||||||
|
|
||||||
|
return (data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void * deallocate (void * data) {
|
||||||
|
if (data != NULL) {
|
||||||
|
free (data);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
200
chapters/chapter_0.h
Normal file
200
chapters/chapter_0.h
Normal file
@ -0,0 +1,200 @@
|
|||||||
|
/*
|
||||||
|
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_0_HEADER
|
||||||
|
#define CHAPTER_0_HEADER
|
||||||
|
|
||||||
|
/*
|
||||||
|
It's very important that your code is aligned, readable and safe. So, we'll talk about it even before the first chapter, the so-called "zero chapter". I write assembly and Ada
|
||||||
|
beside C, but still, 90% of my projects are written in C programming language! Why? Because C is like a girl that's a bit older than you, quiet but very smart and respectable, and
|
||||||
|
you don't want to piss her off. Ada is more like nerdy girl who doesn't know what she wants, while assembly is ghost girl in your closet that you pretend not to see at night. So,
|
||||||
|
this Ada girl was my first serious love, and she had a book "Ada Coding Standard", which is like a bible to her. It's a bit fanatical, but it has some good points which I'll
|
||||||
|
discuss below without copy/paste, and apply to C.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
Lets see some good examples of C code (in my opinion obviously), I'll provide few of them, and note that I use the first style in them. Needless to say, as this book is aimed to
|
||||||
|
people who don't know C programming language, or are in phase of learning it, you should (and eventually will) develop your own style of writing it, but you shouldn't force it at
|
||||||
|
the start, it should happen over time, naturally. Just be sure to always be consistent, and to avoid anything that'll generate compiler warnings.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
General code formatting should follow these rules in C programming language:
|
||||||
|
|
||||||
|
- Always use tabs for indentation, and spaces for alignment. You'll hear people say the otherwise, don't trust them, it's pure cancer.
|
||||||
|
- Separate most (not all!) operators with space before and after it. We'll provide exceptional cases for this rule below.
|
||||||
|
- Align most (not all!) operators horizontally, and logical units vertically. Again, examples are the best showcase.
|
||||||
|
- Always use curly braces and round braces, even when they're not required by the compiler. Most linters warn about this.
|
||||||
|
- Avoid writing a lot of comments, or avoid them completely, except for license notice, this book is an exception!
|
||||||
|
- Think very carefully if you'll limit the line length, it depends on your workflow and attention span... For me, 180 characters per line.
|
||||||
|
- If you want, use pagination, but I don't recommend it when working with C. I use it in Ada, because comments there start with "--".
|
||||||
|
- Always use snake case (this_naming_style), for enumerations and macros (THIS_STYLE), and never put underscore before or after the identifier.
|
||||||
|
|
||||||
|
Spacing:
|
||||||
|
|
||||||
|
- Before and after:
|
||||||
|
|
||||||
|
A: B: C: D: E: F:
|
||||||
|
+ += & &= && ==
|
||||||
|
- -= | |= || !=
|
||||||
|
* *= ^ ^= ! <=
|
||||||
|
/ /= << <<= ~ >=
|
||||||
|
% %= >> >>= ? >
|
||||||
|
= : <
|
||||||
|
|
||||||
|
- Before:
|
||||||
|
|
||||||
|
( { [
|
||||||
|
- After:
|
||||||
|
|
||||||
|
) } ] , ;
|
||||||
|
|
||||||
|
- None (unary + and -):
|
||||||
|
|
||||||
|
. -> " ' ++ -- + -
|
||||||
|
|
||||||
|
Exceptions:
|
||||||
|
|
||||||
|
- Merge consecutive opened or closed braces, for example, like when you're nesting function calls.
|
||||||
|
- When declaring functions, align return types and open braces, and by taste, argument types for logical units.
|
||||||
|
- When declaring variables, align types and asignment operators, and by taste, pointer or array operators.
|
||||||
|
|
||||||
|
Naming:
|
||||||
|
|
||||||
|
- Have you ever wondered, what the hell is stdio, unistd, stdint, termios, errno, strcat, strchr, strndupa, memcpy, atoi, fgetc, puts, lseek...? I did wonder.
|
||||||
|
- Are identifiers such as string_concatenate, memory_copy, error_id, integer, boolean, constant, string_to_integer, file_offset too long to type...? I think not.
|
||||||
|
|
||||||
|
All that aside, C is an old programming language, and people who have made it knew a lot more about how computers work at their time than most people know nowdays. Hardware got
|
||||||
|
faster and cheaper, but also more complex. We have enough memory today to edit our text files with word-wrapping enabled, highlight the syntax, and to store large files due to
|
||||||
|
very long identifiers, 8-space-wide indentation instead of a single tab and overly complicated OOP code. If you can handle those names above, feel free to use them, but be
|
||||||
|
consistent. I can't handle them, so I tolerate them with C keywords, because I'd actually prefer "constant / integer / character" over "const / int / char"... You'll notice that
|
||||||
|
C has mixed them in keywords, and most standard library headers mix them too...
|
||||||
|
|
||||||
|
- Keywords: register double short signed break while / int char extern auto enum struct
|
||||||
|
- Headers: string memory limits time complex threads / stdio stdlib stdint unistd fcntl termios
|
||||||
|
- Functions: read write open close exit clock / strncmp memcpy atoi fputc tcsetattr usleep
|
||||||
|
|
||||||
|
About that, I don't even know where to begin... But lets ignore that for now and focus on code formatting...
|
||||||
|
|
||||||
|
There's not much to say here, we'll talk about function declarations more in later chapters, as they are optional, but I like to use them. However, you should avoid to use too
|
||||||
|
many function agruments, and always place them in consistent and logical order.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
// Internal and external function declaration:
|
||||||
|
|
||||||
|
static int string_copy_limit (char * string_0, char * string_1, int limit);
|
||||||
|
|
||||||
|
extern int32_t string_copy (char * string_a,
|
||||||
|
char * string_b,
|
||||||
|
int32_t count);
|
||||||
|
|
||||||
|
extern size_t copy_string_n (
|
||||||
|
string_t a,
|
||||||
|
string_t b,
|
||||||
|
size_t n);
|
||||||
|
**/
|
||||||
|
|
||||||
|
/*
|
||||||
|
Internal ('static') and external ('extern') variable declarations are different from function declarations, because you need to initialize internal variables to some value, I like
|
||||||
|
to provide either "zero" value for them, with the corresponding type (0, 0.0, '\0', NULL...), or an invalid value (-1, EOF...). Some C compilers and linters will warn you about
|
||||||
|
redeclaring them, some will warn you that they're already initialized to zero, so when you some across those warnings, you'll know how to fix them. I hate this part of C...
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
// Internal and external variable declaration:
|
||||||
|
|
||||||
|
static int curses_active = 0;
|
||||||
|
static char curses_signal = '\0';
|
||||||
|
static char * curses_screen = NULL;
|
||||||
|
|
||||||
|
extern unsigned int positive;
|
||||||
|
extern int negative;
|
||||||
|
extern int * * coordinate_system;
|
||||||
|
|
||||||
|
static void (* encode_type_0 [TYPE_0_COUNT]) (void) = { NULL };
|
||||||
|
static char * (* encode_type_1 [TYPE_1_COUNT]) (int l) = { NULL };
|
||||||
|
static int (* encode_type_2 [TYPE_2_COUNT]) (int l, int r) = { NULL };
|
||||||
|
|
||||||
|
extern xcb_window_t blesses_window;
|
||||||
|
extern xcb_gcontext_t blesses_context;
|
||||||
|
extern xcb_pixmap_t blesses_pixmap;
|
||||||
|
extern xcb_connection_t * blesses_connection;
|
||||||
|
extern xcb_screen_t * blesses_screen;
|
||||||
|
extern xcb_image_t * blesses_image;
|
||||||
|
**/
|
||||||
|
|
||||||
|
/*
|
||||||
|
Only for 'main' function, you shouldn't add 'static' or 'extern' keywords before it. Every C file needs to have exactly one 'main' function, it's the programs' entry point. Also,
|
||||||
|
some people like to define their own types, with keyword 'typedef', I'm not a fan of it, since in C types are also keywords unlike in Ada. Again, it'll become more clean in future
|
||||||
|
chapters why I dislike user-defined types, unions and structures.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
// Main function:
|
||||||
|
|
||||||
|
int main (int argc, char * * argv) {
|
||||||
|
|
||||||
|
int32_t main (int32_t argc,
|
||||||
|
char * argv []) {
|
||||||
|
|
||||||
|
number_t main (
|
||||||
|
number_t argc,
|
||||||
|
string_t argv []) {
|
||||||
|
**/
|
||||||
|
|
||||||
|
/*
|
||||||
|
Now, I'll write some basic functions that'll be used later in the program, so you can see that code formatting I've spent whole hour writing. Don't mind what they do, just keep an
|
||||||
|
eye on how are they aligned and named. I'll reimplement some standard functions, and you can revisit them after reading few more chapters, it'll be more understandable then.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#define SIGNAL_ARROW_UP (0X415B1B)
|
||||||
|
#define SIGNAL_ARROW_DOWN (0X425B1B)
|
||||||
|
#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. */
|
||||||
|
COLOUR_GREY, COLOUR_RED, COLOUR_GREEN, COLOUR_YELLOW, COLOUR_BLUE, COLOUR_PINK, COLOUR_CYAN, COLOUR_WHITE,
|
||||||
|
COLOUR_COUNT
|
||||||
|
};
|
||||||
|
|
||||||
|
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. */
|
||||||
|
CHARACTER_NULL, CHARACTER_START_HEADER, CHARACTER_START_TEXT, CHARACTER_END_TEXT,
|
||||||
|
CHARACTER_END_TRANSMISSION, CHARACTER_ENQUIRY, CHARACTER_ACKNOWLEDGE, CHARACTER_BELL,
|
||||||
|
CHARACTER_BACKSPACE, CHARACTER_TAB_HORIZONTAL, CHARACTER_LINE_FEED, CHARACTER_TAB_VERTICAL,
|
||||||
|
CHARACTER_FORM_FEED, CHARACTER_CARRIAGE_RETURN, CHARACTER_SHIFT_OUT, CHARACTER_SHIFT_IN,
|
||||||
|
CHARACTER_DATA_LINK_ESCAPE, CHARACTER_DEVICE_CONTROL_1, CHARACTER_DEVICE_CONTROL_2, CHARACTER_DEVICE_CONTROL_3,
|
||||||
|
CHARACTER_DEVICE_CONTROL_4, CHARACTER_NOT_ACKNOWLEDGE, CHARACTER_SYNCHRONOUS_IDLE, CHARACTER_END_TRANSMISSION_BLOCK,
|
||||||
|
CHARACTER_CANCEL, CHARACTER_END_MEDIUM, CHARACTER_SUBSTITUTE, CHARACTER_ESCAPE,
|
||||||
|
CHARACTER_FILE_SEPARATOR, CHARACTER_GROUP_SEPARATOR, CHARACTER_RECORD_SEPARATOR, CHARACTER_UNIT_SEPARATOR,
|
||||||
|
CHARACTER_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);
|
||||||
|
|
||||||
|
extern void echo (char * data); /* Function 'echo' behaves similar to 'out', but it's easier to use because it doesn't require size. */
|
||||||
|
|
||||||
|
extern void fatal_failure (int condition, char * message); /* Some functions fail, and sometimes we want to abort everything, crash and leak memory, we just don't care. */
|
||||||
|
|
||||||
|
extern void limit (int * value, int minimum, int maximum);
|
||||||
|
|
||||||
|
extern void * allocate ( int size); /* Core memory management functions with some minimal error checking. */
|
||||||
|
extern void * reallocate (void * data, int size);
|
||||||
|
extern void * deallocate (void * data );
|
||||||
|
|
||||||
|
#endif
|
12
chapters/chapter_1.c
Normal file
12
chapters/chapter_1.c
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
/*
|
||||||
|
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_1_SOURCE
|
||||||
|
#define CHAPTER_1_SOURCE
|
||||||
|
|
||||||
|
#endif
|
12
chapters/chapter_1.h
Normal file
12
chapters/chapter_1.h
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
/*
|
||||||
|
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_1_HEADER
|
||||||
|
#define CHAPTER_1_HEADER
|
||||||
|
|
||||||
|
#endif
|
81
chapters/chapter_2.c
Normal file
81
chapters/chapter_2.c
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
/*
|
||||||
|
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_SOURCE
|
||||||
|
#define CHAPTER_2_SOURCE
|
||||||
|
|
||||||
|
#include "chapter_2.h" /* We're copying function and variable declarations from this file here. This shouldn't be copied twice, more on that later... */
|
||||||
|
|
||||||
|
/*
|
||||||
|
Function 'hello_world_0' uses (rougly said) a system call instead of a function, but I don't even know what will different compilers do to that line of code. What's a system call?
|
||||||
|
Well, when you're writing assembly, you have some general purpose registers, on mine and your machine, there's 16 of them, and you move some data in them, then use 'syscall'
|
||||||
|
instruction, which magically does something, like open or close a file descriptor, read or write to it, but more on that later! A lot more... Important thing to know is that when
|
||||||
|
you use keyword 'sizeof' on a string, it'll count null terminator, so it'll have value 14, and type 'size_t'. It's provided in <unistd.h> header file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
write ( // mov rax 1 ; Literal of Linux write system call.
|
||||||
|
STDOUT_FILENO, // mov rdi 1 ; Literal of standard output file descriptor.
|
||||||
|
"Hello world!\n", // mov rsi X ; Address of our string.
|
||||||
|
sizeof ("Hello world!\n") // mov rdx [Y] ; Literal of size of our string.
|
||||||
|
); // syscall ; Ask kernel to do some work.
|
||||||
|
**/
|
||||||
|
|
||||||
|
/*
|
||||||
|
We'll talk about assembly a lot in future chapters, but you should filter that out of your brain until you learn C well...
|
||||||
|
*/
|
||||||
|
|
||||||
|
void hello_world_0 (void) {
|
||||||
|
write (STDOUT_FILENO, "Hello world!\n", sizeof ("Hello world!\n"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
In function 'hello_world_1' we're using function 'puts' this time, which is considered unsafe because it'll fail when input string isn't null terminated. Keep in mind that it also
|
||||||
|
prints line feed (new line) on the end. Why I call it line feed? Some terminals and text editors distinguish carriage return ('\r' / 13 / CR) and line feed ('\n' / 10 / LF), and
|
||||||
|
some of them use both, like old teleprinters used to do, where the name 'tty' on UNIX-based operating systems comes from.
|
||||||
|
*/
|
||||||
|
|
||||||
|
void hello_world_1 (void) {
|
||||||
|
puts ("Hello world!");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Now, in function 'hello_world_2' and function 'hello_world_3', we're using 'printf' function, which is in my opinion important for debugging. It has many variations, so we'll have
|
||||||
|
a separate chapter only about it. Know that it's a variadic function, which means that it can take more than one input argument, and later we'll learn to make our own variadic
|
||||||
|
functions as well. It's also a bit unsafe, if you're not careful how you use it, but nothing drastic can happen, most compilers catch those kinds of errors.
|
||||||
|
*/
|
||||||
|
|
||||||
|
void hello_world_2 (void) {
|
||||||
|
printf ("Hello world!\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
It's also worth noting that we're using it's arguments in function 'hello_world_3', function 'printf' will print characters one by one from its' format, until it encounters '%',
|
||||||
|
if the next character is '%', it prints one '%', if it's 's', then it'll print a string, which we provide in the next argument. When compiler optimizations are turned on, it will
|
||||||
|
change all sorts of things here, since it's obvious that we're using simple string in this example.
|
||||||
|
*/
|
||||||
|
|
||||||
|
void hello_world_3 (void) {
|
||||||
|
printf ("%s %s!\n", "Hello", "world");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Lastly, we have a global variable, some people like to call it that, and consider it evil. It's not evil if you know the pros and cons of using them, which causes intellectual
|
||||||
|
overhead for certain sort of people. Pros, makes your code shorter and easier to refactor. Cons, it can potentially make your code less safe if you don't know what you're doing.
|
||||||
|
You should use them, external variables as I like to call them, when you're working on your programs or in a small group, but they can lead to bad things if you're working with a
|
||||||
|
lot of people, someone could change it in some bad place, and cause a bug that's difficult to track.
|
||||||
|
*/
|
||||||
|
|
||||||
|
void (* hello_world [4]) (void) = {
|
||||||
|
hello_world_0,
|
||||||
|
hello_world_1,
|
||||||
|
hello_world_2,
|
||||||
|
hello_world_3
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
65
chapters/chapter_2.h
Normal file
65
chapters/chapter_2.h
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
/*
|
||||||
|
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 <stdio.h> /* We need this header file for functions 'puts' and 'printf'. */
|
||||||
|
#include <unistd.h> /* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
// Function declaration: Output: Input:
|
||||||
|
extern void function_0 (void); // undefined (none) // undefined (none)
|
||||||
|
extern float function_1 (double a); // ieee754_32b // ieee754_64b
|
||||||
|
extern int function_2 (int a, int b); // integer_32b // integer_32b, integer_32b
|
||||||
|
static unsigned int function_3 (char * a, int b, char c); // natural_32b // integer_8b_pointer, integer_32b, integer_8b
|
||||||
|
static char * function_4 (char * a); // integer_8b_pointer // integer_8b_pointer
|
||||||
|
static void * function_5 (struct structure * a, void * b); // 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 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)
|
||||||
|
3)
|
||||||
|
4)
|
||||||
|
5)
|
||||||
|
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'.
|
||||||
|
- Don't use 'const' keyword at all, that's my honest advice, it won't catch bugs in C, it'll 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, 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
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
|
7
compile.sh
Normal file
7
compile.sh
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -xe
|
||||||
|
|
||||||
|
gcc -ansi -Wall -Wextra -Wpedantic -Werror -Ofast -o xhartae xhartae.c
|
||||||
|
|
||||||
|
exit
|
7
install.sh
Normal file
7
install.sh
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -xe
|
||||||
|
|
||||||
|
cp xhartae /usr/bin/xhartae
|
||||||
|
|
||||||
|
exit
|
75
xhartae.c
Normal file
75
xhartae.c
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
/*
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
Maybe title:
|
||||||
|
- The Great C & Practical Sailing
|
||||||
|
- The Ultimate C Programming Language Guide
|
||||||
|
- You Will Never Be A Book
|
||||||
|
- C: Disengage All Safety Protocols
|
||||||
|
|
||||||
|
About this "book":
|
||||||
|
|
||||||
|
This is the ultimate C programming language guide, brought to you by Ognjen 'xolatile' Milan Robovic. I'm not a native English speaker nor a real programmer, only a hobbyist, so
|
||||||
|
this "book" will be full of grammatical mistakes, but not compiler warnings. Please be patient, C is a small language, even for the time when it was made, so if you ignore my
|
||||||
|
rambling and focus on what's written outside of the comments, you'll easily learn it. Good luck and have fun...
|
||||||
|
|
||||||
|
Why should you learn or use C programming language in 2023?
|
||||||
|
- C was inspiration for many newer programming languages for good reasons.
|
||||||
|
- C can interface with huge variety of other distinct programming languages.
|
||||||
|
- C can be a lot more readable, faster and easier if used well.
|
||||||
|
|
||||||
|
One sane C program should have the following structure:
|
||||||
|
0) Optional file, author or license information in comment.
|
||||||
|
1) Header guards and implementation definitions.
|
||||||
|
2) System header files then project header files.
|
||||||
|
3) Macro definitions.
|
||||||
|
4) Internal function then variable declarations.
|
||||||
|
5) External function then variable declarations.
|
||||||
|
6) Internal function then variable definition.
|
||||||
|
7) External function then variable definition.
|
||||||
|
8) Main function.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
In C language, we have C source files with the extension '.c', and C header files with the extension '.h'. Both of those are just plain text files, and please use 7-bit ASCII
|
||||||
|
encoding, since it's common sense, UTF is cancer, and 8-bit ASCII is for enlightened people like Terrence Andrew Davis. C language is completely separate (on some C compilers)
|
||||||
|
from its' preprocessor, whose directives start with '#' character, continue on '\' character and break on '\n' (read: LINE FEED) character.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
#include <path/to/file/file_name.h> // Copy the entire file from '/usr/include/' directory into this file, on the place where it was specified.
|
||||||
|
#include "path/to/file/file_name.h" // Copy the entire file from current directory into this file, again on the place where it was specified.
|
||||||
|
|
||||||
|
#define SOMETHING // This will add additional information to the preprocessor about this file, it's mostly used for flags and header-guards.
|
||||||
|
#undef SOMETHING // This will remove that additional information you've provided...
|
||||||
|
|
||||||
|
#if SOMETHING //
|
||||||
|
#ifdef SOMETHING //
|
||||||
|
#ifndef SOMETHING //
|
||||||
|
#else //
|
||||||
|
#endif //
|
||||||
|
**/
|
||||||
|
|
||||||
|
#include <stdlib.h> /* This header is for functions malloc, calloc, realloc, free. */
|
||||||
|
#include <unistd.h> /* And this header if for functions (system calls) read, write, open, close, exit... */
|
||||||
|
|
||||||
|
#include "chapters/chapter_2.c"
|
||||||
|
|
||||||
|
int main (int argc, char * * argv) {
|
||||||
|
int i;
|
||||||
|
|
||||||
|
(void) argc;
|
||||||
|
(void) argv;
|
||||||
|
|
||||||
|
for (i = 0; i != (int) (sizeof (hello_world) / sizeof (* hello_world)); ++i) {
|
||||||
|
hello_world [i] ();
|
||||||
|
}
|
||||||
|
|
||||||
|
return (EXIT_SUCCESS);
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user