If you are completely new to C consider to start here:
Or listen to C Programming for Beginners | Full Course - about 6 hours - YT
Is the C programming language still worth learning? - Jacob Sorber - YT - 🫶
Keynote: Linus Torvalds, Creator of Linux & Git, in Conversation with Dirk Hohndel - The Linux Foundation - YT - 🫶
About
Preface
AI
1 • Linux Ubuntu (Mac Windows etc.) install and adjust - editor, gcc - terminal and bash - cd "c_folder" etc.
2 • best C WSs books YTs etc. - FO tips
3 • Hello World .c - compile - run - explain
4 • header.h location - explain C headers and own headers etc. - directives macros etc. #if #elif #ifdef #pragma
5 • comments
6 • program structure - functions - expressions - conditionals
7 • types - conversion %
8 • variables, constants & strings - scope global local
9 • operators
10 • functions part I - expressions - tokens
11 • conditionals - if else elif etc.
12 • loops - while - do while - for - break - continue - switch etc.
also
13 • arrays
14 • pointers part I
15 • structures & unions
16 • files - write read append
17 • functions part II
18 • recursion
19 • pointers part II
20 • binary - assembly
21 • standard library
22 • gui etc.
23 • animation - blender etc.
24 • arduino - raspberrypi etc.
25 • drones - robots - trains - ships - spaceships
26 • errors & debugging
27 • basic examples - small and medium sized programs of all kinds
MC
Libraries
Editors
Lisp
GTK
Graphics
Difference between C and C++
CGP
Raspberri Pi
Notes
Links
♡ Listen to The Tallis Scholars, Amsterdam 01 May 2016: Arvo Pärt, Tallis, Allegri, Lotti, Sheppard, Mouton ♡♡♡ ♡ ♡♡♡
by Feroniba Ramin Hassani - GitHub -
Twitter - started on
C 26.6.2016 in London, UK
Best C Books of All Time - Extreme C - #9! ♥♡♥ -
Amazon $31.19 - Push the limits of what C - and you - can do, with this high-intensity guide to the most advanced capabilities of C!
♡ Listen to Arvo Pärt - «Virgencita» / THE TALLIS SCHOLARS (Live) ♡♡♡ ♡ ♡♡♡
... coming soon :-))
♡ Understanding the For Loop (examples in C) - Jacob Sorber - YT - ♡♡♡ ♡ ♡♡♡
♡ Listen to somafm.com ♡♡♡ ♡ ♡♡♡
Is the C programming language still worth learning? - 6:00 Rust - Jacob Sorber - YT
♡ Listen to somafm.com ♡♡♡ ♡ ♡♡♡
Artificial Intelligence C Programming - aiblog.co.za
Best Programming Language for AI Development in 2024 - hackr.io
AI Code Tools: The Ultimate Guide in 2024 - codesubmit.io
AI or ML in C? Is it possible? - reddit.com
elementsofai.com - free online course
#include <stdio.h>
int main(void) {
printf("hello, world\n");
return 0;
}
Links:
Linux kernel - WP - now 5.000-6.000 development members - In March 1994, Linux 1.0.0 was released with 176,250 lines of code - Linux 2.6 about 10,000,000 lines of code - 12/2021 Linux 5.15 about 35,000,000 lines of code
Minimal requirements to compile the Kernel - kernel.org
Linux kernel coding style - kernel.org
"K&R" and "The Practice of Programming" by Brian W. Kernighan (born 1.1.1942 in Toronto, Canada) and Rob Pike
VCF East 2019 -- Brian Kernighan interviews Ken Thompson - YT - 40:00 Grep - WP - B, NB, C
... more coming soon ...
p... - ... coming soon ♡ ♥ ♡ ...
p...-... - ...
Next steps:
Raspberry Pi - Arduino - Linux analysis - build our own operating system, language, servers, network, AT SS - build L community international, online forum, political party with laws assuring peace and happiness for everyone ♡ ♥ ♡
End of book :-))) ♡ ♥ ♡
• liquid.html - terasof.com
• WebGL Water - madebyevan.com
• Gabriel Links - terasof.com/ga.html
♡ Listen to The Tallis Scholars / Peter Phillips - The Palestrina 400 Collection ♡♡♡ ♡ ♡♡♡
by Jens Gustedt - Jens Gustedt's Blog - Amazon Paperback - 7 Oct. 2019 - €35.19 - 315 pages
p1 - Our mascot for this level is the magpie, one of most intelligent nonhuman species on earth - capable of elaborate social rituals and usage of tools
This first level of book may be your first encounter with C - provides with rough knowledge about C programs, purpose, structure, and how to use them - not meant to give complete overview, it can’t and doesn’t even try - on the contrary, it is supposed to give a general idea of what this is all about, open up questions, and promote ideas and concepts - these then will be explained in detail in the higher levels
$ c99 -Wall -o 1.1_getting_started 1.1_getting_started.c -lm
p5 - • c99 = compiler program> ./getting-started // executes file
Unfortunately, when programming yourself, very often you will have a program that works only partially and that may produce wrong or unreliable results [3] = .00007,
One of the difficulties for newcomers: same punctuation characters used to express different concepts - for example, the pairs {} and [] are each used for 3 different purposes in listing 1.1int main(void);
double A[5];
size_t i;
p10 - all 3 declarations follow a pattern - each has identifier (main, A, i) and specification (type) of certain properties associated with that identifier:int printf(char const format[static 1], ...);
typedef unsigned long size_t;
#define EXIT_SUCCESS 0
Because the specifics of these predeclared features are of minor importance, this info normally hidden from you in these include files or header files - if need to know their semantics, usually bad idea to look them up in corresponding files, as these tend to be barely readable - instead, search in the documentation that comes with your platform - for the brave, I always recommend a look into the current C standard, where they all come from - for the less courageous,
following commands may help:
> apropos printf
> man printf
> man 3 printf
p11 - a declaration only describes a feature but does not create it, so repeating a declaration does not do much harm but adds redundancy (Überflüssigkeit ...)for (size_t i = 0; i < 5; ++i) {
printf ("element %zu is %g, \tits square is %g\n",
i,
A[i],
A[i] * A[i]);
}
In slight abuse of terminology, first 2 types of scope are called block scope, because the scope is limited by a block of matching { ... } - the 3rd type, as used for
main, which is not inside a { ... } pair, is called file scope - identifiers in file scope often referred to as globalssize_t i = 0;
is a declaration of i such that the initial value is 0 --- in C, such a declaration with an initializer also defines the object with the corresponding name: that is, it instructs the compiler to provide storage in which the value of the variable can be storeddouble A [5] = {
[0] = 9.0,
[1] = 2.9,
[4] = 3.E+25,
[3] = .00007,
};
This initializes the 5 items in A to the values 9.0, 2.9, 0.0, 0.00007, and 3.0E+25, in
that order:int main(void) {
...
}
In examples so far seen names for 2 different features: objects, i and A, and functions, main and printf - in contrast to object or function declarations, where several are allowed for the same identifier, definitions of objects or functions must be unique - that is, for a C program to be operational, any object or function that is used must have a definition (otherwise the execution would not know where to look for them), and there must be no more than one definition (otherwise the execution could become inconsistent)for (size_t i = 0; i < 5; ++i) {
printf("element %zu is %g,\tits square is %g\n",
i,
A[i],
A[i] * A[i]);
}
return EXIT_SUCCESS;
we discussed the lines corresponding to the call to printf - other types of statements are: for and return statements, and an increment operation, indicated by operator
++. In the following subsection, we will go a bit into the details of 3 categories of statements: iterations (do something several times), function calls (delegate execution somewhere else), and function returns (resume (wiederaufnehmen) execution from where a function was called).printf("element %zu is %g, \tits square is %g\n",
i,
A[i],
A[i] * A[i]);
the called function is printf - a function call usually provides more than just the name of the function, but also arguments - here, these are the long chain of characters, i, A[i], and A[i] * A[i] - the values of these arguments are passed over to the function - in this case, these values are the information that is
printed by printf - the emphasis here is on “value”: although i is an argument, printf will never be able to change i itself - such a mechanism is called call by value - other programming languages also have
call by reference, a mechanism where the called function can change the value of a variable - C does not implement pass by reference, but it has another mechanism to pass the control of a variable to another function: by taking addresses and transmitting pointers - we will see these mechanism much laterp17 - mascot for this level, the common raven, is a very sociable corvid (Corvide, Rabenvogel) and known for its problem-solving capacity - ravens organize in teams and have been observed playing even as adults
This level will acquaint (jmd. m. etw. vertraut machen) you with the C programming language: that is, it will provide you with enough knowledge to write and use good C programs - "good" here refers to a modern understanding of the language, avoiding most of the pitfalls of
early dialects of C, and offering you some constructs that were not present before and that are portable across the vast majority of modern computer architectures, from your cell phone to a mainframe computer - having worked through these sections, you should be able to write short code for everyday needs: not extremely sophisticated, but
useful and portable
Buckle up (schnall dich an)
p18 - C is a permissive language - just for the moment, we'll introduce some restrictions - we’ll try to avoid handing out guns in this level, place the key to the gun safe out of your reach for
the moment, marking its location with big visible exclamation marks
The most dangerous constructs in C are the so-called casts (converting one datatype into another, known as type casting or type-conversion), so we’ll skip them at this level - there are many other pitfalls, less easy to avoid - will approach some of them in a way that might look unfamiliar to you, in particular if you learned your C basics in the last millennium or if you were introduced to C on a platform that wasn’t upgraded to current
ISO C for years
experienced C programmers: what follows may take some getting used to or even provoke allergic reactions - if you happen to break out in spots when you read some of the code here, take a deep breath and try to relax, but please do not skip these pages
inexperienced C programmers: much of following discussion bit over your head: for example, we may use terminology you may not yet heard of - digression (Abschweifung) for you, may skip to start of section 3 and come back later when you feel a bit more comfortable - be sure to do so before the end of this level
“getting used to” our approach on this level may concern (betreffen, angehen) the emphasis (Schwerpunkt) and ordering in which we present the material:
• will focus primarily on unsigned versions of integer types
• will introduce pointers in steps: first, in disguise as parameters to functions (section 6.1.4), then with their state (being valid or not, section 6.2), and then, on the next level, (section 11), using their entire potential
• will focus on use of arrays whenever possible, instead
you might also be surprised by some style considerations we will discuss in following points - on next level, will dedicate section 9 to these questions - please be patient and accept them for the moment as they are
(1) We bind type modifiers (9 main types: char (= int type) int float short long long_long double unsigned signed + combinations - WP) and qualifiers (4: const volatile restrict atomic - WP) to the left: we want to
separate identifiers (names) visually from their type - so we will typically write things as
char* name;
where char* is the type and name is the identifier - we also apply the left-binding rule to qualifiers and write
char const* const path_name;
Here first const qualifies the char to its left, the * makes it to a pointer, and the 2nd const again qualifies what is to its left
(2) We do not use continued declarations: they obfuscate (verwirren, verschleiern ...) the bindings of type declarators. For example:
unsigned const* const a, b; // const*const without space _ ?
Here, b has type unsigned const: that is, the first const goes to the type, and the second const only goes to the declaration of a - such rules are highly confusing, and you have more important things to learn
p19 - (3) We use array notation for pointer parameters: we do so wherever these assume that the pointer can’t be null - examples:
/* These emphasize that the arguments cannot be null. */
argc and argv - crasseux.com - 'argument count' and 'argument vector'
size_t strlen(char const string[static 1]); // size_t = unsigned integer type used to represent the size of any object (including arrays) in the particular implementation --- strlen() function calculates the length of a given string - WP
int main(int argc, char* argv[argc +1]); // argc and argv are how command line arguments are passed to main()
/* Compatible declarations for the same functions. */
size_t strlen(const char *string);
int main(int argc, char **argv);
The first stresses the fact that strlen must receive a valid (non-null) pointer and will access at least one element of string - the second summarizes the fact that main receives an array of pointers to char: the program name, argc-1
(not +1 ?) program arguments, and one null pointer that terminates the array -
note that the previous code is valid as it stands - the second set of declarations only adds additional equivalent declarations for features that are already known to the compiler
(4) We use function notation for function pointer parameters: Along the same lines, we do so whenever we know that a function pointer can’t be null:
/* This emphasizes that the "handler" argument cannot be null. */
Here, the first declaration of atexit emphasizes that, semantically, it receives a function named handler as an argument and that a null function pointer is not allowed - technically, the function parameter handler is "rewritten" to a
function pointer much as array parameters are rewritten to object pointers, but this is of minor interest for a description of the functionality
int atexit(void handler(void));
/* Compatible declaration for the same function. */
int atexit(void (*handler)(void));
Note, again, that the previous code is valid as it stands and that the second declaration just adds an equivalent declaration for atexit.
(5) We define variables as close to their first use as possible: Lack of variable initialization, especially for pointers, is one of the major pitfalls for novice C programmers - this is why we should, whenever possible,
combine the declaration of a
variable with the first assignment to it: the tool that C gives us for this purpose is the definition: a declaration together with an initialization - this gives a name to a value and introduces this name at the first place where it is used
This is particularly convenient (zweckmäßig, geeignet ...) for for loops - the iterator variable of one loop is semantically a different object from that in another loop, so we declare the
variable within the for to ensure it stays within the loop’s scope
(6) We use prefix notation for code blocks: (++a prefix; a-- postfix --- Difference between Increment and Decrement Operators - geeksforgeeks.org) To be able to read a code block, it is important to capture 2
things about it easily: its purpose and its extent - therefore:
• All { are prefixed on the same line with the statement (or declaration) that introduces them
• The code inside is indented by one level
• The terminating } starts a new line on the same level as the statement that introduced the block
• Block statements that have a continuation after the } continue on the same line
p20 - Examples:
# include <stdlib.h>
2.1_hello_world.c -
2.1_hello_world
# include <stdio.h>
# include <stdbool.h>
int main (int argc, char* argv[argc+1]) {
argc = 0; // FO inserted line to check program, then change to argc = 2
puts ("hello, world");
if (argc > 1) {
while (true) {
puts ("some programs never stop");
}
} else {
do {
puts ("but this one does");
} while (false);
}
return EXIT_SUCCESS;
}
p21 - This section covers
• Conditional execution with if
• Iterating over domains
• Making multiple selections
In our introductory example, listing 1.1, we saw 2 different constructs that allowed us to control the flow of a program’s execution: functions and the
for iteration - functions are a way to transfer control unconditionally - the call transfers control unconditionally to the function, and a return statement unconditionally transfers it
back to the caller - we
will come back to functions in section 7
The for statement is different in that it has a controlling condition (i < 5 in the example) that regulates if and when the dependent block or statement ({ printf(...) })
is executed - C has 5 conditional control statements: if, for, do, while, and switch
We will look at these statements in this section: if introduces a conditional execution depending on a Boolean expression; for, do, and while are different forms of iterations; and
switch is a multiple selection based on an integer value.
C has some other conditionals that we will discuss later: the ternary operator, denoted by an expression in the form cond ? A : B (section 4.4), the compile-time pre-processor conditionals
#if/#ifdef/#ifndef/#elif/#else/#endif (section 8.1.5), and type generic expressions denoted with the keyword
_Generic (section 16.6)
3.1. Conditional execution - the first construct that we will look at is specified by the keyword if:
if ( i > 25) {
Here we compare i against the value 25 - if it is larger than 25, j is set to the value
i - 25 - in the example, i > 25 is called the
controlling expression, and the part in { ... } is called the dependent block
j = i - 25;
}
On the surface, this form of an if statement resembles (ähnelt, gleicht) the for statement that we already encountered - but it works differently than that: there is only one part inside the parentheses, and that determines whether the dependent statement or block is run once or
not at all
There is a more general form of the if construct:
if ( i > 25) {
It has a 2nd dependent statement or block that is executed if the controlling condition is not fulfilled - syntactically, this is done by introducing another keyword else that separates the 2 statements or blocks
j = i - 25;
} else {
j = i;
}
The if (...) ... else ... is a selection statement - it selects 1 of the 2 possible code paths according to the contents of ( ... ) - the general form is
if (condition) statement0-or-block0
possibilities for condition (the controlling expression) are numerous - they can range from simple comparisons, as in this example, to very complex nested expressions
else statement1-or-block1
We will present all the primitives that can be used in section 4.3.2.
p22 - The simplest of such condition specifications in an if statement can be seen in the following example, in a variation of the for
loop from listing 1.1:
for (size_t i = 0; i < 5; ++i) {
Here the condition that determines whether printf is executed is just i: a numerical value by itself can be interpreted as a condition - the text will only be printed when the value of i is not 0
if (i) {
printf("element %zu is %g, \tits square is %g\n",
i,
A[i],
A[i] * A[i]);
}
}
[Exc 1] Add the if (i) condition to the program, and compare the output to the previous. >> not printing anymore: element 0 is 9, its square is 81 --- because if (i) can't be 0 (p23 - bool b = ...;)
There are 2 simple rules for the evaluation of a numerical condition:
Takeaway 1.3.1.1 The value 0 represents logical false
Takeaway 1.3.1.2 Any value different from 0 represents logical true
The operators == and != allow us to test for equality and inequality, respectively.
a == b is true if the value of a is equal to the value of b, and false otherwise; a != b is false if a is equal to b, and true otherwise. Knowing how numerical values are evaluated as conditions, we can avoid redundancy (Überflüssigkeit) - for example, we can rewrite
if (i != 0) {
as:
...
}if (i) {
Which of these two versions is more readable is a question of coding style and can be subject to fruitless debates - while the 1st might be easier for occasional readers of C code to read, the latter is often preferred in projects that assume some
knowledge about C’s type system
...
}
The type bool, specified in stdbool.h, is what we should be using if we want to store truth values - its values are false and true - technically, false is just another name for 0 and true for 1 - it’s important to
use false and true (and not the numbers) to emphasize that a value is to be interpreted as a condition - we will learn more about the bool type in section 5.7.4.
Redundant (überflüssige) comparisons quickly become unreadable and clutter (durcheinanderbringen) your code - if you have a conditional that depends on a truth value, use that truth value directly as the condition
Again, we can avoid redundancy by rewriting something like
bool b = ...;
as
...
if (( b != false ) == true ) { // [Exs 1] Add the if (i) condition to the program, and compare the output to the previous.
...
}bool b = ...;
p23 - Generally:
...
if (b) {
...
}
Takeaway 1.3.1.3 Don’t compare to 0, false, or true
Using the truth value directly makes your code clearer and illustrates one of the basic concepts of the C language:
Takeaway 1.3.1.4 All scalars (comes from linear algebra, used to differentiate a single number from a vector or matrix - meaning in computing is similar - distinguishes single value like an integer or float from a data structure like an array) have a truth value
Here, scalar types include all the numerical types such as size_t, bool, and int that we already encountered, and pointer types; see table 3.1 for the types that are frequently used in this book - we will come back to them in section 6.2.
TABLE 3.1. Scalar types used in this book
Level | Name | Other | Category | Where | printf |
0 | size_t | Unsigned | <stddef.h> | "%zu" "%zx" | |
0 | double | Floating | Built in | "%e" "%f" "%g" "%a" | |
0 | signed | int | Signed | Built in | "%d" |
0 | unsugned | Unsigned | Built in | "%u" "%x" | |
0 | bool | _Bool | Unsigned | <stdbool.h> | "%d" as 0 or 1 |
1 | ptrdiff_t | Signed | <stddef.h> | "%td" | |
1 | char const* | String | Built in | "%s" | |
1 | char | Character | Built in | "%c" | |
1 | void* | Pointer | Built in | "%p" | |
2 | unsigned char | Unsigned | Built in | "%hhu" "%02hhx" |
for (clause1; condition2; expression3) statement-or-block
This statement is actually quite generic - usually, clause1 is an assignment expression or a variable definition - it serves to state an initial value for the iteration domainfor (size_t i = 10; i; --i) {
something (i);
}
for (size_t i = 0, stop = upper_bound (); i < stop; ++i) {
something_else (i);
}
for (size_t i = 9; i <= 9; --i) {
something_else (i);
}
(Implementing upper_bound() and lower_bound() in C - geeksforgeeks.org)while(condition) statement-or-block
do statement-or-block while(condition);
The following example shows a typical use of the first - it implements the so-called Heron approximation to compute the multiplicative inverse \(\frac{x}{1}\) of a number \(\ x\)
#include <tgmath.h>
double const eps = 1E-9; // desired precision
...
double const a = 34.0;
double x = 0.5;
while (fabs(1.0 - a * x) >= eps) { // iterates until close
x *= (2.0 - a * x); // Heron approximation
}
//> -inf // loop endlessly
(tgmath.h - type-generic macros - pubs.opengroup.org - The <tgmath.h> header shall include the headers <math.h> and <complex.h> and shall define several type-generic macros -
C mathematical functions - WP)do { // Iterates
x *= (2.0 - a * x); // Heron approximation
} while (fabs (1.0 - a * x) >= eps); // Iterates until close
This means if the condition evaluates to false, a while loop will not run its dependent block at all, and a do loop will run it once before terminating. --- FO test: both, while and do while are runnung -int endlessly
!while (true) {
double prod = a * x;
if (fabs(1.0 - prod) < eps) { // Stops if close enough
break;
}
x *= (2.0 - prod); // Heron approximation
}
p25 - This way, we can separate the computation of the product a * x, the evaluation of the stop condition, and the update of x --- the condition of the while then becomes trivial - the same thing can be done using a for, and there is a tradition among C programmers to write it as follows:
for (;;) {
double prod = a * x;
if (fabs(1.0 - prod) < eps) { // Stops if close enough
break;
}
x *= (2.0 - prod); // Heron approximation
}
for(;;) here is equivalent to while(true) - the fact that the controlling expression of a for (the middle part between the ;;) can be omitted and is interpreted as “always true” is
just a historical artifact in the rules of C and has no other special purposefor (size_t i = 0; i < max_iterations; ++i) {
if (x > 1.0) { // Checks if we are on the correct side of 1
x = 1.0 / x;
continue;
}
double prod = a * x;
if (fabs(1.0 - prod) < eps) { // Stops if close enough
break;
}
x *= (2.0 - prod); // Heron approximation
}
max_iterations ... ?> ./heron 0.07 5 6E+23 // 0
heron: a=7.00000e-02, x=1.42857e+01, a*x=0.999999999996 // 1
heron: a=5.00000e+00, x=2.00000e-01, a*x=0.999999999767 // 2
heron: a=6.00000e+23, x=1.66667e-24, a*x=0.999999997028 // 3
p26 - To process the numbers on the command line, the program uses another library function strtod from stdlib.h - [Exs 4][Exs 5][Exs 6]
CHALLENGE 1 (Sequential sorting algorithms). Can you do
(1) A merge sort (with recursion) - beginning first try chl1.1_merge_sort.c - runs correctly with scanned input count and unordered numbers - recursion not finished
(2) A quick sort (with recursion) - beginning first try chl1.2_quick_sort.c - runs correctly with scanned input count and unordered numbers - recursion not finished
on arrays with sort keys such as double or strings to your liking?
Nothing is gained if you don’t know whether your programs are correct - therefore, can you provide a simple test routine that checks if the resulting array really is sorted?
- this test routine should just scan once through the array and should be much, much faster than your sorting algorithms
LISTING 3.1. - Computing multiplicative inverses of numbers
#include <stdlib.h>
[Exc 4] Analyze listing 3.1 by adding printf calls for intermediate values of x --- done - at first not printing some lines :-( - added (info from p25) to filename at commandline: 0.07 5 6E+23 // 0 --- now showing all printf() lines :-) - Exc 4 "heron.c"
file with different entered printf():
3.1_computing_multiplicative_inverses_of_numbers.c - run with:
#include <stdio.h>
/* lower and upper iteration limits centered around 1.0 */
static double const eps1m01 = 1.0 - 0x1P-01;
static double const eps1p01 = 1.0 + 0x1P-01;
static double const eps1m24 = 1.0 - 0x1P-24;
static double const eps1p24 = 1.0 + 0x1P-24;
int main(int argc, char* argv[argc +1]) {
for (int i = 1; i < argc; ++i) { // process args
double const a = strtod(argv[i], 0); // arg -> double
double x = 1.0;
for (;;) { // by powers of 2
double prod = a * x;
if (prod < eps1m01) {
x *= 2.0;
} else if (eps1p01 < prod) {
x *= 0.5;
} else {
break;
}
}
for (;;) { // Heron approximation
double prod = a * x;
if ((prod < eps1m24) || (eps1p24 < prod)) {
x *= (2.0 - prod);
} else {
break;
}
}
printf("heron: a = %.5e,\tx = %.5e,\ta * x = %.12f\n", a, x, a*x);
}
return EXIT_SUCCESS;
}
// LISTING 3.1. - Computing multiplicative inverses of numbers - p26./3.1_computing_multiplicative_inverses_of_numbers 0.07 5 6E+23 // 0
[Exc 5] Describe the use of the parameters argc and argv in listing 3.1. (go p19 etc.) -> argc = "count" for counting arguments, argv = vector, array containing the strings of all arguments -
3.2.3_argc_argv.c --- ?
int main(int argc, char* argv[argc +1]) {
[Exc 6] Print out the values of eps1m01, and observe the output when you change them slightly - done successfully
for (int i = 1; i < argc; ++i) {
double const a = strtod(argv[i], 0);
...
p27 - 3.3. Multiple selection - The last control statement that C has to offer is the switch statement and is another selection statement - it is mainly used when cascades of if-else constructs would be too tedious:
if (arg == 'm') {
We have a more complex choice than a false-true decision, and that
can have several outcomes - we can simplify this as follows:
puts ("this is a magpie");
} else if (arg == 'r') {
puts ("this is a raven");
} else if (arg == 'j') {
puts ("this is a jay");
} else if (arg == 'c') {
puts ("this is a chough");
} else {
puts ("this is an unknown corvid");
}switch (arg) {
Here we select one of the puts calls according to the value of the arg variable - like
printf, function puts is provided by stdio.h - it outputs a line with the string that is passed as an argument - we provide specific cases for characters 'm', 'r', 'j', 'c' and a fallback case labeled default - the default case is triggered if arg doesn’t match any of the case values
case 'm': puts ("this is a magpie");
break;
case 'r': puts ("this is a raven");
break;
case 'j': puts ("this is a jay");
break;
case 'c': puts ("this is a chough");
break;
default: puts ("this is an unknown corvid");
}
[Exs 7] (Test the example switch statement in a program - see what happens if you leave out some of the break statements) > exc7_switch.c - test: to leave out a break statement prints the next case too, etc.
Syntactically, a switch is as simple as
switch (expression) statement-or-block
and its semantics are quite straightforward: the case and default labels serve as jump targets - according to the value of the expression, control continues at the statement that is labeled accordingly - if we hit a break statement, the whole switch under which it appears terminates, and control is transferred to the next statement
after the switch
By that specification, switch statements can be used much more widely than iterated if-else constructs:
switch (count) {
exc7.1_triangle.c (replace all ♡ symbols in this code with ♡) - test: exc7.1_triangle.c - have fun +♡+♡+♡+
default: puts ("++++ ..... +++");
case 4: puts ("++++");
case 3: puts ("+++");
case 2: puts ("++");
case 1: puts ("+");
case 0:;
}
Once we have jumped into the block, execution continues until it reaches a break or the end of the block - in this case, because there are no break statements, we end up running all subsequent puts statements
p28 - For example, the output when the value of count is 3 is a triangle with three lines:
+++
The structure of a switch can be more flexible than if-else, but it is restricted in
another way:
++
+
Takeaway 1.3.3.1 case values must be integer constant expressions
In section 5.6.2, we will see what these expressions are in detail - for now, it suffices to know that these have to be fixed values that we provide (bereitstellen) directly in the source, such as the 4, 3, 2, 1, 0 in the previous example - in particular, variables such as count are only allowed in the switch part, not in the individual case s (?)
With the greater flexibility of the switch statement also comes a price: it is more error prone - in particular, we might accidentally skip variable definitions:
Takeaway 1.3.3.2 - case labels must not jump beyond a variable definition
p29 - This section covers
• Performing arithmetic
• Modifying objects
• Working with booleans
• Conditional compilation with the ternary (bedingten) operator
• Setting the evaluation (Auswertung) order
Already used some simple examples of expressions, code snippets that compute a value based on other values - the simplest such expressions are arithmetic expressions, which are similar to those we learned in school - but there are others, notably comparison operators such as == and !=, which we saw earlier
this section, values and objects on which we will do these computations will be mostly type size_t, already met - such values correspond to “sizes,” so they are numbers that cannot be negative - their range of possible values starts at 0 - we like to represent all non-negative integers, often denoted as \( \mathbb {N} \), \( \mathbb {N} _{0} \), or “natural” numbers in mathematics - unfortunately, computers are finite, so we can’t directly represent all the natural numbers, but we can do a reasonable approximation - there is a big upper limit
SIZE_MAX that is the upper bound of what we can represent in a size_t
Takeaway 1.4.0.1 The type size_t represents values in the range [0, SIZE_MAX]
The value of SIZE_MAX is quite large - depending on the platform, it is one of
216 − 1 = 65535
232 − 1 = 4294967295
264 - 1 = 18446744073709551615
first value is a minimal requirement; nowadays, such a small value would only occur on
some embedded platforms - other two values much more commonly used today - second still found on some PCs and laptops, and large majority of newer platforms have third - choice of value large enough for calculations not too sophisticated - the standard header stdint.h provides SIZE_MAX such that you don’t have <stdint.h> to figure out value yourself, don't have to specialize your program accordingly
concept of “numbers that cannot be negative” to which we referred for size_t corresponds to what C calls unsigned integer types - symbols and combinations like + and != are called operators, and the things to which they are applied are called operands; so, in something like a + b, + is the operator and a and
b are its operands
for overview of all C operators see following tables: table 4.1 lists the operators that operate on values, table 4.2 lists those that operate on objects, and table 4.3 lists those that operate on types - to work with these, you may have to
jump from one table to another
For example, if you want to work out an expression such as a + 5, where a is some variable of type unsigned, you first have to go to the third line in table 4.2 to see that a is evaluated (ausgewertet, berechnet)
Then, you can use the third line in table 4.1 to deduce (herleiten) that the value of a and 5 are combined in an arithmetic operation: a + --- don’t be frustrated if you don’t understand everything in these tables - a lot of the concepts that are mentioned have not yet been introduced; they are listed here to form a
reference for the entire book
4.1 Arithmetic - Arithmetic operators form first group in table 4.1 of operators that operate on values
p30 - Table 4.1 - Value operators: the Form column gives the syntactic form of the operation, where @ represents the operator and a and possibly b denote values that serve as operands - for
arithmetic and bit operations, the type of the result is a type that reconciles (abstimmen) the types of a and b - for some of the operators, the Nick column gives an alternative form of the operator, or lists a combination of operators that has special meaning - most of the operators and terms will be discussed
later
size_t a = 45;
size_t b = 7;
size_t c = (a - b) * 2; // = 76
size_t d = a - b * 2; // = 31
Here, c must be equal to 76, and d to 31 - as you can see from this little example, subexpressions can be grouped together with parentheses to enforce a preferred binding of the operator ---size_t c = (+a + -b) * 2; // = 76 as well, equivalent to size_t c above
Even though we use an unsigned type for our computation, negation and difference by means of the operator - are well defined - that is, regardless of the values we feed into such a subtraction, our computation will always have a valid result - in fact, one of the miraculous properties of size_t is that
+-* arithmetic always works where it can - as long as the final mathematical result is within the range [0, SIZE_MAX], then that result will be the value of the expressiona + SIZE_MAX - (SIZE_MAX+1) = a - 1
The operators / and % have the nice property that their results are always smaller than or equal to their operands:an_object @ = some_expression;
they are just convenient abbreviations for combining the arithmetic operator @ and assignment; see table 4.2 --- a mostly equivalent form is
an_object = (an_object @ (some_expression));
in other words, there are operators +=, -=, *=, /=, and %= --- for example, in a for loop, the operator += can be used:
for ( size_t i = 0; i < 25; i += 7) {
...
}
[Exc 8] - implement some computations using a 24-hour clock, such as 3 hours after 10:00 and 8 hours after 20:00a = b = c += ++d;
a = (b = (c += (++d))); // Same
But such combinations of modifications to several objects in one go is generally frowned upon (verpönt) - don’t do that unless you want to obfuscate (vernebeln, verwirren) your code - such changes to objects that are involved in an expression are referred to as side effectssize_t c = (a < b) + (a == b) + (a > b);
size_t d = (a <= b) + (a >= b) - 1;
4.3.1_comparison.c - works fine :-)double largeA[N] = {0};
...
/* Fill largeA somehow */
size_t sign[2] = {0 , 0};
for (size_t i = 0; i < N; ++i) { // Would say choose a positive number for N
sign[(largeA[i] < 1.0)] += 1; // ? Study arrays
}
[false]
[true]element 1 is 1, its square is 1
element 2 is 2, its square is 4
↥ ?double largeA[N] = {0}; // N ? Would say choose positive number N and same amount in {0, 1, ... , N-1};
...
/* Fill largeA somehow */
size_t isset[2] = {0 , 0};
for (size_t i = 0; i < N; ++i) { // N ? Would say the chosen N appers here
isset[!!largeA[i]] += 1;
}
Here, the expression !!largeA[i] applies the ! operator twice and thus just ensures that largeA[i] is evaluated as a truth value (Takeaway 1.3.1.4) - as a result, the array elements isset[0] and isset[1] will hold the number of values that are equal to 0.0 and
unequal, respectively.// This never divides by 0.
if (b != 0 && (( a / b) > 1)) { // ?
++x;
}
p35 - Here, the evaluation of a/b is omitted conditionally during execution, and thereby a division by zero can never occur - equivalent code would be
if (b) { // ?
// This never divides by 0.
if (a / b > 1) {
++x;
}
}
↥ ?size_t size_min(size_t a, size_t b) {
return (a < b) ? a : b;
}
Syntax of a conditional operator
Expression1 ? expression2 : expression3;
Conditional Operator in C - javatpoint.com - excellent C language explanations!# include <tgmath.h>
# ifdef __STDC_NO_COMPLEX__
# error "we need complex arithmetic"
# endif
double complex sqrt_real(double x) {
return (x < 0) ? CMPLX(0, sqrt(-x)) : CMPLX(sqrt(x), 0); // conditional expression
}
In this function, sqrt is called only once, and the argument to that call is never negative - so, sqrt_real is always well behaved; no bad values are ever passed to sqrtprintf("%g and % g\n", f(a), f(b));
we wouldn’t know which of the last two arguments is evaluated firstp38 - This section covers
• Understanding the abstract state machine
• Working with types and values
• Initializing variables
• Using named constants
• Binary representations of types
We will now change our focus from “how things are to be done” (statements and expressions) to the things on which C programs operate: values and data - a concrete program at an instance in time has to represent values -
humans have a similar strategy: nowadays we use a decimal presentation to write numbers on paper using the Hindu-Arabic numeral system - but we have other systems to write numbers: for example, Roman numerals (i, ii, iii, iv, and so on) or textual notation - to know that the word twelve denotes the value 12 is a nontrivial step and reminds us that European languages denote numbers not only in decimal but also in other systems - English and German mix with base 12, French with bases
16 and 20 - for non-native French speakers like myself, it may be difficult to spontaneously associate quatre vingt quinze (four times twenty and fifteen) with the value 95
Similarly, representations of values on a computer can vary “culturally” from architecture to architecture or are determined by the type the programmer gave to the value - therefore, we should try to reason primarily about values and not about representations if we want to write portable code
If you already have some experience in C and in manipulating bytes and bits, you will need to make an effort to actively “forget” your knowledge for most of this section - thinking about concrete representations of values on your computer will inhibit (blockieren, sperren, inhibieren ...) you more than it helps
Takeaway 1.5.0.1 - C programs primarily reason about values and not about their representation
The representation that a particular value has should in most cases not be your concern; the compiler is there to organize the translation back and forth between values and representations
In this section, we will see how the different parts of this translation are supposed to work - the ideal world in which you will usually “argue” in your program is C’s abstract state machine (section 5.1) - it gives a vision of the execution of your program that is mostly independent of the platform on which the program runs - the components of the state of this machine, the objects, all have a fixed interpretation (their type) and a value that varies in time -
C’s basic types are described in subsection 5.2, followed by descriptions of how we can express specific values for such basic types (subsection 5.3), how types are assembled in expressions (subsection 5.4), how we can
ensure that our objects initially have the desired values (subsection 5.5), how we can give names to recurrent values (subsection 5.6), and how such values are represented in the abstract state machine
(subsection 5.7).
5.1. The abstract state machine - A C program can be seen as a sort of machine that manipulates values: the particular values that variables of the program have at a given time, and also intermediate values that are the
result of computed expressions - let us consider a basic example:
p39 -
double x = 5.0;
Here we have two variables, x and y, that have initial values 5.0 and 3.0, respectively (jeweils) - the third line computes some expressions: a subexpression
double y = 3.0;
...
x = (x * 1.5) - y;
printf("x is \%g\n", x);x
that evaluates (evaluieren, festsetzen) x and provides (liefert) the value 5.0;
(5.0 * 1.5)
that results in the value 7.5;
y
that evaluates y and provides the value 3.0;
7.5 - 3.0
that results in 4.5;
x = 4.5
that changes the value of x to 4.5;
x
that evaluates x again, but that now provides the value 4.5; and
printf("x is \%g\n", 4.5)
that outputs a text line to the terminal
Not all operations and their resulting values are observable from within your program - they are observable only if they are stored in addressable memory or written to an output device - in the example, to a certain extent, the printf statement “observes” what was done on the previous line by evaluating the variable x and then writing a string representation of that value to the terminal - but the other subexpressions and their results (such as the multiplication and subtraction) are not observable as such, since we never define a variable that is supposed to hold these values
Your C compiler is allowed to shortcut any of the steps during a process called optimization only if it ensures the realization of the end results - here, in our toy example, there are basically two possibilities - the first is that variable x is not used later in the program, and its acquired value is only relevant for our printf statement - in that case, the only effect of our code snippet is the output to the terminal, and the compiler may well (and will!) replace the whole snippet with the equivalent
printf ("x is 4.5\n");
that is, it will do all the computations at compile time and, the executable that is produced will just print a fixed string - all the remaining code and even the definitions of the variables disappear
The other possibility is that x might be used later - then a decent compiler would either do something like
p40 -
double x = 4.5;
or maybe
printf("x is 4.5\n");printf("x is 4.5\n");
because to use x at a later point, it is not relevant whether the assignment took place before or after the printf
double x = 4.5;
For an optimization to be valid, it is only important that a C compiler produces an executable that reproduces the observable states - these consist of the contents of some variables (and similar entities that we will see later) and the output as they evolve during the execution of the program - this whole mechanism of change is called the abstract state machine
To explain the abstract state machine, we first have to look into the concepts of a value (what state are we in), the type (what this state represents), and the representation (how state is distinguished) - as the term abstract suggests, C’s mechanism allows
different platforms to realize the abstract state machine of a given program differently according to their needs and capacities - this permissiveness is one of the keys to C’s potential for optimization
5.1.1. Values - A value in C is an abstract entity that usually exists beyond your program, the particular implementation of that program, and the representation of the value during a particular run of the program - as an example, the value and concept of 0 should and will always have the same effects on all C platforms: adding that value to another value x will again be x, and evaluating a value 0 in a control expression will always trigger the false branch of the control statement
So far, most of our examples of values have been some kind of numbers - this is not an accident, but relates to one of the major concepts of C
Takeaway 1.5.1.1 - All values are numbers or translate to numbers
This property really concerns all values a C program is about, whether these are the characters or text we print, truth values, measures that we take, or relations that we investigate - think of these numbers as mathematical entities that are independent of your program and its concrete realization
The data of a program execution consists of all the assembled values of all objects at a given moment - the state of the program execution is determined by:
• The executable
• The current point of execution
• The data
• Outside intervention, such as IO from the user
If we abstract from the last point, an executable that runs with the same data from the same point of execution must give the same result - but since C programs should be portable between systems, we want more than that - we don’t want the result of a computation to depend on the executable (which is platform specific) but ideally to depend only on the program specification itself - an important step to achieve this platform independence is the concept of types
5.1.2. Types - A type is an additional property that C associates with values - up to now, we have seen several such types, most prominently size_t, but also double and bool
Takeaway 1.5.1.2 - All values have a type that is statically determined
p41 - Takeaway 1.5.1.3 - Possible operations on a value are determined by its type
Takeaway 1.5.1.4 - A value’s type determines the results of all operations
5.1.3. Binary representation and the abstract state machine - Unfortunately, the variety of computer platforms is not such that the C standard can completely impose (auferlegen, ...) the results of the operations on a given type - things that are not completely specified as such by the standard are, for example, how the sign of a signed type is represented the (sign representation), and the precision to which a double floating-point operation is performed (floating-point representation)9 - C only imposes properties on representations such that the results of operations can be deduced a priori from two different sources:
• The values of the operands
• Some characteristic values that describe the particular platform
For example, the operations on the type size_t can be entirely determined when inspecting the value of SIZE_MAX in addition to the operands - we call the model to represent values of a given type on a given platform the binary representation of the type
Takeaway 1.5.1.5 - A type’s binary representation determines the results of all operations
Generally, all information we need to determine that model is within reach of any C program: the C library headers provide the necessary information through named values (such as SIZE_MAX), operators, and function calls
Takeaway 1.5.1.6 - A type’s binary representation is observable
This binary representation is still a model and thus an abstract representation in the sense that it doesn’t completely determine how values are stored in the memory of a computer or on a disk or other persistent storage device - that representation is the object representation - in contrast to the binary representation, the object representation usually is not of much concern to us, as long as we don’t want to hack together values of objects in main memory or have to communicate between computers that have different platform models - much later, in section 12.1, we will see that we can even observe the object representation, if such an object is stored in memory and we know its address
As a consequence, all computation is fixed through the values, types, and their binary representations that are specified in the program - the program text describes an abstract state machine that regulates how the program switches from one state to the next - these transitions are determined by value, type, and binary representation only
Takeaway 1.5.1.7 - (as-if) Programs execute as if following the abstract state machine
5.1.4. Optimization - How a concrete executable manages to follow the description of the abstract state machine is left to the discretion of the compiler creators - most modern C compilers produce code that doesn’t follow the exact code prescription: they cheat wherever they can and only respect the observable states of the abstract state machine - for example, a sequence of additions with constant values such as
x += 5;
9Other international standards are more restrictive about these representations. For example, the POSIX [2009] standard enforces a particular sign representation, and ISO/IEC/IEEE 60559 [2011] normalizes floating-point representations
/* Do something else without x in the meantime. */
x += 7;
p42 - may in many cases be done as if it were specified as either
/* Do something without x. */
or
x += 12;x += 12;
The compiler may perform such changes to the execution order as long as there will be no observable difference in the result: for example, as long as we don’t print the intermediate value of x and as long as we don’t use that intermediate value in another computation
/* Do something without x. */
But such an optimization can also be forbidden because the compiler can’t prove that a certain operation will not force program termination - in our example, much depends on the type of x --- if the current value of x could be close to the upper limit of the type, the innocent-looking operation x += 7 may produce an overflow - such overflows are handled differently according to the type - as we have seen, overflow of an unsigned type is not a problem, and the result of the condensed operation will always be consistent with the two separate ones - for other types, such as signed integer types (signed) and floating-point types
(double), an overflow may raise an exception and terminate the program - in that case, the optimization cannot be performed
As we have already mentioned, this allowed slackness between program description
and abstract state machine is a very valuable feature, commonly referred to as optimization.
Combined with the relative simplicity of its language description, this is actually one of
the main features that allows C to outperform other programming languages that have a lot more knobs and whistles - an important consequence of this discussion can be summarized as follows:
Takeaway 1.5.1.8 - Type determines optimization opportunities
5.2. Basic types - C has a series of basic types and means (Verhältnisse?) of constructing derived types from them that we will describe later, in section 6
Mainly for historical reasons, the system of basic types is a bit complicated, and the syntax to specify such types is not completely straightforward - there is a first level of specification that is done entirely with keywords of the language, such as signed, int, and
double - this first level is mainly organized according to C internals - on top of that is a second level of specification that comes through header files, and we have already seen examples: size_t and bool - this second level is organized by type semantics, specifying what properties a particular type brings to the programmer
We will start with the first-level specification of such types - as we discussed earlier (Takeaway 1.5.1.1), all basic values in C are numbers, but there are different kinds of numbers - as a principal distinction, we have two different classes of numbers, each with two subclasses each: unsigned
integers, signed integers, real floating-point numbers, and complex floating-point numbers - each of these four classes contains several types - they differ according to their precision, which determines the valid range of values that are allowed for a particular type10 - Table 5.1 contains an overview of the 18 base types
As you can see from the table, there are six types that we can’t use directly for arithmetic, the so-called narrow types - they are promoted to one of the wider types before they are considered in an arithmetic expression - nowadays, on any realistic platform, this promotion will be a signed int of the same value as the narrow type, regardless of whether the narrow type was signed
10The term precision is used here in a restricted sense as the C standard defines it - it is different from the accuracy (Genauigkeit) of a floating-point computation
p43 - TABLE 5.1. - Base types according to the four main type classes - Types with a gray background don’t allow for arithmetic; they are promoted (befördert?) before doing arithmetic - type char is special since it can be unsigned or signed, depending on the platform - all types in this table are considered to be distinct types, even if they have the same class and precision
[[[[[signed char] short] int] long] long long]
But this inclusion does not need to be strict - for example, on many platforms, the set of values of int and long are the same, although the types are considered to be different - an analogous inclusion holds for the six unsigned types:[[[[[[bool] unsigned char] unsigned short] unsigned] unsigned long] unsigned long long]
But remember that for any arithmetic or comparison, the narrow unsigned types are promoted to signed int and not to unsigned int, as this diagram might suggest[[Non-negative signed values] Unsigned values]
That is, for a given rank, the non-negative values of the signed type fit into the unsigned type - on any modern platform you encounter, this inclusion is strict: the unsigned type has values that do not fit into the signed type - for example, a common pair of maximal values is 231 − 1 = 2 147 483 647 for signed int and 232 − 1 = 4 294 967 295 for unsigned intputs("first line\n"
"another line\n"
"first and "
"second part of the third line");
11If used in the context of the printf function, another character also becomes “special”: the % character. If you want to print a literal % with printf, you have to duplicate it.float | double | long double | literal | 0.2F | 0.2 | 0.2L |
value | 0x1.99999AP-3F | 0x1.999999999999AP-3 | 0xC.CCCCCCCCCCCCCCDP-6L |
double a = 1; // Harmless; value fits type
signed short b = -1; // Harmless; value fits type
signed int c = 0x80000000; // Dangerous; value too big for type
signed int d = -0x80000000; // Dangerous; value too big for type
signed int e = -2147483648; // Harmless; value fits type
signed short g = 0x80000000; // Loses information; has value 0
Here, the initializations of a and b are harmless. The respective values are well in the
range of the desired types, so the C compiler can convert them silently.1 + 0.0 // Harmless; double
1 + I // Harmless; complex float
INT_MAX + 0.0F // May lose precision; float
INT_MAX + I // May lose precision; complex float
INT_MAX + 0.0 // Usually harmless; double
Here, the first two examples are harmless: the value of the integer constant 1 fits well into the type double or complex float. In fact, for most such mixed operations, whenever the range of one type fits into the range of the other, the result has the type of the wider range.-1 < 0 // True, harmless, same signedness
-1L < 0 // True, harmless, same signedness
-1U < 0 // False, harmless, same signedness
-1 < 0U // False, dangerous, mixed signedness
-1U < 0 // False, dangerous, mixed signedness
-1L < 0U // Depends, dangerous, same or mixed signedness
-1LL < 0UL // Depends, dangerous, same or mixed signedness
The first three comparisons are harmless, because even if they mix operands of different types, they do not mix signedness. Since for these cases the ranges of possible values nicely contain each other, C simply converts the other type to the wider one and does the comparison there.double a = 7.8;
double b = 2 * a;
double c = {7.8};
double d = {0};
p51 - Initializers for other types must have these {}. For example, array initializers contain initializers for the different elements, each of which is followed by a comma:
double A [ ] = {7.8 , };
double B[3] = {2 * A[0], 7, 33, };
double C[ ] = {[0] = 6, [3] = 1, };
[0]#include <stdlib.h>
#include <stdio.h>
// #include <stdbool.h>
int main(int argc, char* argv[argc+1]) {
char const*const bird[3] = {
"raven",
"magpie",
"jay",
};
char const*const pronoun [3] = {
"we",
"you",
"they",
};
char const*const ordinal [3] = {
"first",
"second",
"third",
};
// ...
for (unsigned i = 0; i < 3; ++i)
printf("Corvid %u is the %s\n", i, bird [i]);
// ...
for (unsigned i = 0; i < 3; ++ i)
printf("%s plural pronoun is %s\n", ordinal[i], pronoun[i]);
return EXIT_SUCCESS;
}
// mc p51-52
// compile: c99 -Wall -o 5.6_named_constants 5.6_named_constants.c -lm
18The exceptions are variable-length arrays; see subsection 6.1.3.enum corvid {magpie, raven, jay, corvid_num, };
char const*const bird[corvid_num] = {
[raven] = "raven",
[magpie] = "magpie",
[jay] = "jay",
};
...
for (unsigned i = 0; i < corvid_num; ++i)
printf("Corvid %u is the %s\n", i, bird[i]);
This declares a new integer type enum corvid for which we know four different values.enum corvid {magpie, raven, jay, chough, corvid_num, };
char const*const bird[corvid_num] = {
[chough] = "chough",
[raven] = "raven",
[magpie] = "magpie",
[jay] = "jay",
};
...
// added by FO from code above, 5.6.2
for (unsigned i = 0; i < corvid_num; ++i) // added by FO from code above
printf("Corvid %u is the %s\n", i, bird[i]); // added by FO from code above
20A third class of read-only objects exist: temporary objects. We will see them later, in subsection 13.2.2.enum {p0 = 1, p1 = 2* p0, p2 = 2* p1, p3 = 2* p2,};
To define these constants, we can use integer constant expressionsC (ICE). Such an ICE provides a compile-time integer value and is much restricted. Not only must its value be determinable at compile time (no function call allowed), but also no evaluation of an object must participate as an operand to the value:
signed const o42 = 42;
enum {
b42 = 42, // Ok: 42 is a literal
c52 = o42 + 10 , // Error: o42 is an object
b52 = b42 + 10 , // Ok: b42 is not an object
};
Here, o42 is an object, const-qualified but still, so the expression for c52 is not an “integer constant expression.”# define M_PI 3.14159265358979323846
This macro definition has the effect that the identifier M_PI is replaced in the following program code by the double constant. Such a macro definition consists of five different parts:(T) {INIT}
That is, a type, in parentheses, followed by an initializer. Here’s an example:
# define CORVID_NAME /**/ /
(char const*const [corvid_num]) { /
[chough] = "chough", /
[raven] = "raven", /
[magpie] = "magpie", /
[jay] = "jay", /
}
With that, we could leave out the bird array and rewrite our for loop:
for (unsigned i = 0; i < corvid_num; ++i)
printf("Corvid %u is the %s\n", i, CORVID_NAME[i]);
Whereas compound literals in macro definitions can help us to declare something that behaves similarly to a constant of a chosen type, it isn’t a constant in the narrow sense of C.enum corvid {magpie, raven, jay, chough, corvid_num,};
# define FLOCK_MAGPIE 1U
# define FLOCK_RAVEN 2U
# define FLOCK_JAY 4U
# define FLOCK_CHOUGH 8U
# define FLOCK_EMPTY 0U
# define FLOCK_FULL 15U
int main(void) {
unsigned flock = FLOCK_EMPTY;
...
if (something) flock |= FLOCK_JAY;
...
if (flock & FLOCK_CHOUGH)
do_something_chough_specific (flock);
}
__________# define FLOCK_MAGPIE (1 U << magpie )
# define FLOCK_RAVEN (1 U << raven )
# define FLOCK_JAY (1 U << jay )
# define FLOCK_CHOUGH (1 U << chough )
# define FLOCK_EMPTY 0U
# define FLOCK_FULL ((1 U << corvid_num ) - 1)
bool is_negative(unsigned a) {
unsigned const int_max = UINT_MAX/2;
return a > int_max;
}
bool is_signed_less(unsigned a, unsigned b) {
if (is_negative (b) && !is_negative(a)) return false;
else return a < b;
}
Table 5.6 shows an example of how the negative of our example value 240 can be constructed. For unsigned types, -A can be computed as ~A + 1.[Exs 30][Exs 31][Exs 32] Two’sp65 - This section covers
• Grouping objects into arrays
• Using pointers as opaque types
• Combining objects into structures
• Giving types new names with typedef
All other data types in C are derived (abgeleitet), from the basic types that we know now - there are 4 strategies for deriving data types - two of them are called aggregate data types, because they combine multiple instances of one or several other data types:
Arrays: These combine items that all have the same base type (subsection 6.1)
Structures: These combine items that may have different base types (subsection 6.3)
The two other strategies to derive data types are more involved:
Pointers: Entities that refer to an object in memory
Pointers are by far the most involved concept, and we will delay a full discussion of them to section 11 - here, in subsection 6.2, we will only discuss them as opaque data types, without even mentioning the real purpose they fulfill
Unions: These overlay items of different base types in the same memory location
Unions require a deeper understanding of C’s memory model and are not of much use in a programmer’s everyday life, so they are only introduced later, in subsection 12.2.
There is a 5. strategy that introduces new names for types: typedef (subsection 6.4)
Unlike the previous four, this does not create a new type in C’s type system, but only creates
a new name for an existing type - in that way, it is similar to the definition of macros with #define; thus the choice for the keyword for this feature
6.1. Arrays - Arrays group objects of same type into an encapsulating object - see pointer types later (section 11), but many people who come to C confused about arrays and pointers - completely normal: arrays and pointers closely related in C, and to explain them we face a chicken and egg problem: arrays look like pointers in many contexts, and pointers refer to array objects - we chose an order of introduction that is perhaps unusual: we will start with arrays and stay with them as long as possible before introducing pointers - this may seem “wrong” to some of you, but remember that everything stated here must be viewed based on the as-if Rule (Takeaway 1.5.1.7) - will first describe arrays in a way that is consistent with C’s assumptions about abstract state machine
Takeaway 1.6.1.1 - Arrays are not pointers
Later, we will see how these two concepts relate, but for the moment it is important to read this section without prejudice (Voreingenommenheit) about arrays; otherwise, you will delay your ascent to a better understanding of C
6.1.1. Array declaration - we have already seen how arrays are declared: by placing something like [N] after another declaration - for example:
double a[4];
p66 - Here, a comprises (umfasst, enthält ...) 4 subobjects of type double and b comprises N of type signed - we visualize arrays with diagrams like the following, with a sequence of boxes of their base type:
signed b[N];
[0]
[1]
[2]
[3]
a
[0]
[N-1]
b ... ...
The dots ... here indicate that there may be an unknown number of similar items between the two boxes
The type that composes an array may itself again be an array, forming a multidimensional array - the declarations for those become a bit more difficult to read since [ ] binds to the left - the following two declarations declare variables of exactly the same type:
double C[M][N];
Both C and D are M objects of array type double[N] --- this means we have to read a nested array declaration from inside out to describe its structure:
double (D[M])[N];
c
[0]
p80 - This section covers
• Introduction to simple functions
• Working with main
• Understanding recursion
already seen different means (Mittel) C offers for conditional execution: execution that, based on a value, chooses one branch of the program over another to continue - the reason for a potential “jump” to another part of the program code (for example, to an else branch) is a runtime decision that depends on runtime data - this section starts with a discussion of unconditional ways to transfer control to other parts of our code: by themselves, they do not require any runtime data to decide where to go
The code examples we have seen so far often used functions from the C library that provided features we did not want (or were not able) to implement ourselves, such as printf for printing and strlen for computing the length of a string - the idea behind this concept of functions is that they implement a certain feature, once and for all, and that we then can rely on that feature in the rest of our code
A function for which we have seen several definitions is main, the entry point of execution into a program - in this section, we will look at how to write functions ourselves that may provide features just like the functions in the C library
The main reasons motivating the concept of functions are modularity and code factorization:
• Functions avoid code repetition - in particular they avoid easily introduced copy-and-paste errors and spare the effort of editing in multiple places if you modify a piece of functionality - thereby, functions increase readability and maintainability
• Use of functions decreases compilation times - a given code snippet that we encapsulate in a function is compiled only once, not at each point where it is used
• Functions simplify future code reuse - once we have extracted code into a function that provides certain functionality, it can easily be applied in other places that we did not even think of when implementing the function
• Functions provide clear interfaces - function arguments and return types clearly specify the origin and type of data that flows into and out of a computation - additionally, functions allow us to specify invariants for a computation: pre- and post-conditions
• Functions provide a natural way to formulate algorithms that use a “stack” of intermediate (Zwischenstufe ..) values
In addition to functions, C has other means of unconditional transfer of control, which are mostly used to handle error conditions or other forms of exceptions from the usual control flow:
• exit, _Exit, quick_exit, and abort terminate the program execution (see subsection 8.7)
• goto transfers control within a function body (see subsections 13.2.2 and 14.5)
• setjmp and longjmp can be used to return unconditionally to a calling context (see subsection 17.5)
• Certain events in the execution environment or calls to the function raise may raise signals that pass control to a specialized function, a signal handler
7.1. Simple functions - We have used a lot of functions and seen some their declarations (for example in section 6.1.5) and definitions (such as listing 6.3). In all of these functions, parentheses ( ) play an important syntactical role - they are used for function
p81 - declarations and definitions, to encapsulate the list of parameter declarations - for function calls, they hold the list of arguments for that concrete call - this syntactic role is similar to [ ] for arrays: in declarations and definitions, they contain the size of the corresponding dimension - in a designation like A[i], they are used to indicate the position of the accessed element in the array
All the functions we have seen so far have a prototype: their declaration and definition, including a parameter type-list and a return type - to see that, let us revisit the leapyear function from listing 6.3 (yday.c):
bool leapyear(unsigned year) {
A declaration of that function (without a definition) could look as follows:
/* All years that are divisible by 4 are leap years,
unless they start a new century, provided they
are not divisible by 400. */
return !(year % 4) && ((year % 100) || !(year % 400));
}bool leapyear (unsigned year);
Alternatively, we could even omit the name of the parameter and/or add the storage specifier extern:44
extern bool leapyear (unsigned);
Important for such a declaration is that the compiler sees the types of the argument(s) and the return type, so here the prototype of the function is “function receiving an unsigned and returning an bool.”
There are two special conventions that use the keyword void:
• If the function is to be called with no parameter, the list is replaced by the keyword void, like main in our very first example (listing 1.1)
• If the function doesn’t return a value, the return type is given as void: for example, swap_double
Such a prototype helps the compiler in places where the function is to be called. It only has to know about the parameters the function expects. Have a look at the following:
extern double fbar (double);
Here, the call fbar(2) is not directly compatible with the expectation of function fbar: it wants a double but receives a signed int. But since the calling code knows this, it can convert the signed int argument 2 to the double value 2.0 before calling the function. The same holds for the use of the return value in an expression: the caller knows that the return type is double, so floating-point division is applied for the result expression.
...
double fbar2 = fbar(2) / 2;
C has obsolete ways to declare functions without prototype, but you will not see them here. You shouldn’t use them; they will be retired in future versions.
Takeaway 1.7.1.1 - All functions must have prototypes.
A notable exception to that rule are functions that can receive a varying number of parameters, such as printf. They use a mechanism for parameter handling called a variable argument listC , which comes with the header stdargs.h.
44More details on the keyword extern will be provided in subsection 13.2.
p82 - ...
... p83-89 ...
p90 - This section covers
• Doing math, handling files, and processing strings
• Working with main
• Managing the runtime environment
• Terminating programs
The functionality that the C standard provides is separated into two big parts: proper C language, and C library - we have looked at several functions that come with the C library, including printf, puts, and strtod, so you should have a good idea what to expect: basic tools that implement features that we need in everyday programming and for which we need clear interfaces and semantics to ensure portability
On many platforms, the clear specification through an application programming interface (API) also allows us to separate the compiler implementation from the library implementation - for example, on Linux systems, we have a choice of different compilers, most commonly gcc and clang, and different C library implementations, such as the GNU C library (glibc), dietlibc, or musl; potentially, any of these choices can be used to produce an executable
We will first discuss the general properties and tools of the C library and its interfaces, and then describe some groups of functions: mathematical (numerical) functions, input/output functions, string processing, time handling, access to the runtime environment, and program termination
8.1. General properties of the C library and its functions - Roughly, library functions target one or two purposes:
Platform abstraction layer - Functions that abstract from the specific properties and needs of the platform - these are functions that need platform-specific bits to implement basic operations such as IO, which could not be implemented without deep knowledge of the platform - for example, puts has to have some concept of a “terminal output” and how to address it - implementing these functionalities would exceed the knowledge of most C programmers, because doing so requires OS- or even processor-specific magic - be glad that some people did that job for you
Basic tools - Functions that implement a task (such as strtod) that often occurs in programming in C and for which it is important that the interface is fixed - these should be implemented relatively efficiently, because they are used a lot, and they should be well tested and bug free so we can rely safely on them - implementing such functions should in principle be possible for any confirmed C programmer [Exs 51]
A function like printf can be viewed as targeting both purposes: it can effectively be separated into a formatting phase providing a basic tool and an output phase that is platform specific - there is a function snprintf (explained much later, in subsection 14.1) that provides the same formatting functionalities as printf but stores the result in a string
This string could then be printed with puts to give the same output as printf as a whole - in the following sections, we will discuss the different header files that declare the interfaces of the C library (subsection 8.1.1), the different types of interfaces it provides (subsection 8.1.2), the various error strategies it applies (subsection 8.1.3), an optional series of interfaces intended to improve application safety (subsection 8.1.4), and tools that we can use to assert platform-specific properties at compile time (subsection 8.1.5)
[Exs 51]Write a function my_strtod that implements the functionality of strtod for decimal floating-point constants
# define putchar(A) putc(A, stdout)
As before, these are just textual replacements, and since the replacement text may contain a macro argument several times, it would be bad to pass any expression with side effects to such a macro or function - hopefully, our previous discussion about side effects (takeaway 1.4.2.2) has already convinced you not to do thatif (puts("hello world") == EOF) {
perror ("can 't output to terminal:");
exit(EXIT_FAILURE);
}
\hspace{1cm} Here we see that puts falls into the category of functions that return a special value on error, EOF, “end-of-file.” The perror function from stdio.h is then used to provide an additional diagnostic that depends on the specific error. exit ends the program execution. Don’t wipe failures under the carpet. In programming,p117 - The Eurasian jay may be solitary or found in pairs - it is known for its mimicry (Nachahmung) of other bird calls, for its alertness (Wachsamkeit, Munterkeit, Aufmerksamkeit, Flinkheit ...), and for its dispersal (Verbreitung) of seeds that contribute to forest expansion
Now we are advanced enough to go to the heart of C - completing this level should enable you to write C code professionally; it therefore begins with an essential discussion
about the writing and organization of C programs - then it fills in the gaps for the major C constructs that we have skipped so far: it fully explains pointers, familiarizes you with C’s memory model and with dynamic memory allocation, and allows you to understand most of C’s library interface
p118 - This section covers
• Writing readable code
• Formatting code
• Naming identifiers
Programs serve both sides: first, as we have already seen, they serve to give instructions to the compiler and the final executable - but equally important, they document the intended behavior of a system for the people (users, customers, maintainers, lawyers, and so on) who have to deal with it
Therefore, we have a prime directive:
Takeaway C - All C code must be readable
The difficulty with that directive is knowing what constitutes (bedeutet, konstituiert ...) “readable” - not all experienced C programmers agree, so we will begin by trying to establish a minimal list of necessities - the first things we must have in mind when discussing the human condition is that it is constrained by (abhängig von) two major factors: physical ability and cultural baggage
Takeaway 2.9.0.1 - Short-term memory and the field of vision are small
Torvalds et al. [1996], the coding style for the Linux kernel
(kernel.org coding-style), is a good example that insists on that aspect and certainly is worth a detour, if you haven’t read it yet - its main assumptions are still valid: a programming text has to be represented in a relatively small “window” (be it a console or a graphical editor) that consists of roughly 30 lines of 80 columns, making a “surface” of 2,400 characters - everything that doesn’t fit has to be memorized - for example, our very first program in listing 1.1 fits into these constraints
By its humorous reference to Kernighan and Ritchie [K&R, 1978], the Linux coding style also refers to another fundamental fact:
Takeaway 2.9.0.2 - Coding style is not a question of taste but of culture
Ignoring this easily leads to endless and fruitless debates about not much at all
Takeaway 2.9.0.3 - When you enter an established project, you enter a new cultural space
Try to adapt to the habits of the inhabitants - when you create your own project, you have a bit of freedom to establish your own rules - but be careful if you want others to adhere to (befolgen ...) them; you must not deviate too much from the common sense that reigns in the corresponding community
9.1. Formatting - The C language itself is relatively tolerant of formatting issues - under normal circumstances, a C compiler will dumbly parse an entire program that is written on a single line with minimal white space and where all identifiers are composed of the letter l and the digit 1 - the need for code formatting originates in human incapacity
Takeaway 2.9.1.1 - Choose a consistent strategy for white space and other text formatting
Formatting concerns indentation, placement of parentheses and all kinds of brackets ( { }, [ ], and ( ) ), spaces before and after operators, trailing spaces, and multiple new lines - the human eye and brain are quite peculiar (eigen ...) in their habits, and to ensure that they work properly and efficiently, everything must be in sync
In the introduction for level 1, you saw a lot of the coding style rules applied to the code in this book - take them as an example of one style; you will most likely encounter other styles as you go along - let us recall some of the rules and introduce some others that have not yet been presented:
p119 -
• We use prefix notation for code blocks: that is, an opening { is at the end of a line
• We bind type modifiers and qualifiers to the left
• We bind function ( ) to the left, but ( ) of conditions are separated from their keyword (such as if or for) with a space
• A ternary expression has spaces around the ? and the :
• Punctuation marks (:, ;, and ,) have no space before them but either one space or a new line after
As you see, when written out, these rules can appear quite cumbersome (umständlich ...) and arbitrary (willkürlich ...) - they have no value as such; they are visual aids that help you and your collaborators understand new code in the blink of an eye - they are not meant to be meticulously (akribisch genau ...) typed by you directly, but you should acquire (erarbeiten) and learn the tools that can help you with them
Takeaway 2.9.1.2 - Have your text editor automatically format your code correctly
I personally use Emacs (//gnu.org/software/emacs/) for that task (yes, I am that old). For me, it is ideal since it understands a lot of the structure of a C program by itself - your mileage will probably vary, but don’t use a tool in everyday life that gives you less - text editors, integrated development environments (IDEs), and code generators are there for us, not the other way around
In bigger projects, you should enforce such a formatting policy for all the code that circulates and is read by others - otherwise, it will become difficult to track differences between versions of programming text - this can be automated by command-line tools that do the formatting - here, I have a long-time preference for astyle (artistic style sourceforge.net) - again, your mileage may vary; choose anything that ensures the task
9.2. Naming - The limit of such automatic formatting tools is reached when it comes to naming
Takeaway 2.9.2.1 - Choose a consistent naming policy for all identifiers
There are two different aspects to naming: technical restrictions on one hand and semantic conventions on the other - unfortunately, they are often mixed up and the subject of endless ideological debate
For C, various technical restrictions apply; they are meant to help you, so take them seriously - first of all, we target all identifiers: types (struct or not), struct and union members, variables, enumerations, macros, functions, function-like macros - there are so many tangled (verworrene) name spaces that you have to be careful
In particular, the interaction between header files and macro definitions can have surprising effects - here is a seemingly innocent example:
double memory_sum(size_t N, size_t I, double strip[N][I]);
• N is a capitalized identifier, and thus your collaborator could be tempted to define a macro N as a big number
• I is used for the root of −1 as soon as someone includes complex.h
• The identifier strip might be used by a C implementation for a library function or macro
• The identifier memory_sum might be used by the C standard for a type name in the future
Takeaway 2.9.2.2 - Any identifier that is visible in a header file must be conforming
p120 - here, conforming is a wide field - in C jargon, an identifier is reserved if its meaning is fixed by the C standard and you may not redefine it otherwise:
• Names starting with an underscore and a second underscore or a capital letter are reserved for language extensions and other internal use
• Names starting with an underscore are reserved for file scope identifiers and for enum, struct and union tags
• Macros have all-caps names
• All identifiers that have a predefined meaning are reserved and cannot be used in file scope - this includes a lot of identifiers, such as all functions in the C library, all identifiers starting with str (like our strip, earlier), all identifiers starting with E, all identifiers ending in _t, and many more
What makes all of these rules relatively difficult is that you might not detect any violation for years; and then, all of a sudden, on a new client machine, after the introduction of the next C standard and compiler or after a simple system upgrade, your code explodes
A simple strategy to keep the probability of naming conflicts low is to expose as few names as possible
Takeaway 2.9.2.3 - Don’t pollute the global space of identifiers.
Expose only types and functions as interfaces that are part of the application programming interface (API): that is, those that are supposed to be used by users of your code.
A good strategy for a library that is used by others or in other projects is to use naming prefixes that are unlikely to create conflicts. For example, many functions and types in the POSIX thread API are prefixed with pthread_. For my tool box P99, I use the prefixes p99_ and P99_ for API interfaces and p00_ and P00_ for internals.
There are two sorts of names that may interact badly with macros that another programmer writes and which you might not think of immediately:
• Member names of struct and union
• Parameter names in function interfaces
The first point is the reason why the members in standard structures usually have a prefix to their names: struct timespec has tv_sec as a member name, because an uneducated user might declare a macro sec that would interfere in unpredictable way when including time.h. For the second point, we saw an example earlier. In P99, I would specify such a function something like this:
double p99_memory_sum (size_t p00_n, size_t p00_i,
This problem gets worse when we are also exposing program internals to the public view. This happens in two cases:
p00_strip[p00_n][p00_i]);
• So-called inline functions, which are functions whose definition (not only declaration) is visible in a header file
• Function-like macros
We will discuss these features much later, see sections 15.1 and 16
Now that we have clarified the technical points of naming, we will look at the semantic aspect
Takeaway 2.9.2.4 - Names must be recognizable and quickly distinguishable
That has two parts: distinguishable and quickly. Compare the identifiers in table 9.1. For your personal taste, the answers on the right side of this table may be different. This reflects my taste: an implicit context for such names is part of my personal expectation
p121 -
Recognizable
Distinguishable
Quickly
lllll1llOll
llllll1l0ll
No
No
No
myLineNumber
nmyLineNumber
mYes
YesYes
YesNo
Yes
ffs
lowBitclz
highBitNo
YesYes
YesYes
Yes
p00Orb
p00_Orbp00Urb
p00_urbNo
YesYes
YesNo
Yes
return theVerySeldomlyUsedConstant*theVerySeldomlyUsedConstant /
number_of_elements;
Hungarian notation, in turn, tends to use obscure abbreviations for types or concepts, produces unpronounceable identifiers, and completely breaks down if you have an API change.Summary
• Coding style is a matter of culture. Be tolerant and patient.
• Code formatting is a matter of visual habits. It should be automatically provided by your environment such that you and your co-workers can read and write code effortlessly.
• Naming of variables, functions, and types is an art and plays a central role in the comprehensiveness of your code.
p123 - This section covers
• How to document interfaces
• How to explain implementations
Being an important societal, cultural, and economic activity, programming needs a certain form of organization to be successful. As with coding style, beginners tend to underestimate the effort that should be put into code and project organization and documentation: unfortunately, many of us have to go through the experience of reading our own code some time after we wrote it, and not having any clue what it was all about.
Documenting or, more generally, explaining program code is not an easy task. We have to find the right balance between providing context and necessary information and boringly stating the obvious. Let’s have a look at the two following lines:
u = fun4you (u , i , 33 , 28) ; // ;)
...
++ i; // incrementing i
p133 - This section covers
• Introduction to pointer operations
• Using pointers with structs, arrays, and functions
Pointers are the first real hurdle to a deeper understanding of C - they are used in contexts where we have to be able to access objects from different points in the code, or where data is structured dynamically on the fly
The confusion of inexperienced programmers between pointers and arrays is notorious (berüchtigt), so be warned that you might encounter difficulties in getting the terms correct - on the other hand, pointers are one of the most important features of C - they are a big plus to help us abstract from the bits and odds of a particular platform and enable us to write portable code - so please, equip yourself with patience when you work through this section, because it is crucial (entscheidend) for the understanding of most of the rest of this book
The term pointer stands for a special derived type construct that “points” or “refers” to something - we have seen the syntax for this construct, a type (the referenced type) that is followed by a * character - for example, p0 is a pointer to a double:
double* p0;
The idea is that we have one variable (the pointer) that points to the memory of another object:
p0 →
(box2 example FO testing a new box: hello, world :-))
An import distinction that we will have to make throughout this section is between the pointer (on the left of the arrow) and the unnamed object that is pointed to (on the right)
Our first usage of a pointer will be to break the barrier between the code of the caller of a function and the code inside a function, and thus allow us to write functions that are not pure - this example will be a function with this prototype:
void double_swap(double* p0, double* p1);
Here we see two function arguments that “point” to objects of type double - in the example, the function double_swap is supposed to interchange (swap) the contents of these two objects - for example, when the function is called, p0 and p1 could be pointing to double variables d0 and d1, respectively (jeweils), that are defined by the caller:
d0
d1
p0 →
← p1
By receiving information about two such objects, the function double_swap can effectively
change the contents of the two double objects without changing the pointers themselves:
d0
d1
p0 →
← p1
Using pointers, the function will be able to apply the change directly to the variables of the calling function; a pure function without pointers or arrays would not be able to do this - in this section, we will go into the details of different operations with pointers (subsection 11.1) and specific types for which pointers have particular properties: structures (subsection 11.2), arrays (subsection 11.3), and
functions (subsection 11.4)
11.1. Pointer operations - Pointers are an important concept, so there are several C language operations and features just for them - most importantly, specific operators allow us to deal with the “pointing-to” and “pointed-to”
relation between pointers and the objects to which they point (subsection 11.1.1)
p134 - Also, pointers are considered scalars: arithmetic operations are defined for them, offset additions (subsection 11.1.2) and subtractions (subsection 11.1.3); they have state (subsection 11.1.4); and they have a dedicated “null” state (subsection 11.1.5).
11.1.1. Address-of and object-of operators - If we have to perform tasks that can’t be expressed with pure functions, things get more involved - we have to poke around (herumstöbern) in objects that are not variables of the function - pointers are a suitable abstraction to do this
So, let us use the function double_swap from earlier to swap the contents of two double objects d0 and d1 - for the call, we use the unary address-of operator “&” - it allows us to refer to an object through its address - a call to our function could look like this:
double_swap(&d0, &d1);
The type that the address-of operator returns is a pointer type and can be specified with the * notation that we have seen - an implementation of the function could look like this: inside the function, pointers p0 and p1 hold the addresses of the objects on which
void double_swap( double* p0, double* p1) {
the function is supposed to operate: in our example, the addresses of d0 and d1. But the
function knows nothing about the names of the two variables d0 and d1; it only knows p0
and p1
double tmp = *p0;
*p0 = *p1;
*p1 = tmp;
}
<unknown>
<unknown>
p0 →
← p1
To access them, another construct that is the inverse of the address-of operator is used: the unary object-ofC operator “*”: *p0 then is the object corresponding to the first argument - with the previous call, that would be d0, and similarly *p1 is the object d1 [Exs 9]
*p0
*p1
p0 → double 3.5
double 10 ← p1
Please note that the * character plays two different roles in the definition of double_swap. In a declaration, it creates a new type (a pointer type), whereas in an expression it dereferencesC
the object to which a pointer refersC . To help distinguish these two usages of the same symbol, we usually flush the * to the left with no blanks in between if it modifies a type (such as double*) and to the right if it dereferences a pointer (*p0).
Remember from subsection 6.2 that in addition to holding a valid address, pointers
may also be null or indeterminate.
Takeaway 2.11.1.1 - Using * with an indeterminate or null pointer has undefined behavior.
In practice, though, both cases will usually behave differently. The first might access a
random object in memory and modify it. Often this leads to bugs that are difficult to trace
because it will poke into objects it is not supposed to. The second, if the pointer is null,
will manifest early during development and nicely crash our program. Consider this to be
a feature.
______________
[Exs 9]Write a function that receives pointers to three objects and that shifts the values of these objects cyclically.
p135 -
11.1.2. Pointer addition - We already have seen that a valid pointer holds the address of an object of its reference type, but actually C assumes more than that:
Takeaway 2.11.1.2 - A valid pointer refers to the first element of an array of the reference type.
Or, in other words, a pointer may be used to refer not only to one instance of the reference type, but also to an array of an unknown length n
0
n-1
p0 → double .........
double
This entanglement between the concept of pointers and arrays is taken an important step further in the syntax. In fact, for the specification of the function double_swap, we wouldn’t even need the pointer notation. In the notation we have used so far, it can equally be written as
...
...
p150 - This section covers
• Understanding object representations
• Working with untyped pointers and casts
• Restricting object access with effective types and alignment
Pointers present us with a certain abstraction of the environment and state in which
our program is executed, the C memory model. We may apply the unary operator & to
(almost) all objects18 to retrieve their address and use it to inspect and change the state of
our execution.
This access to objects via pointers is still an abstraction, because seen from C, no
distinction of the “real” location of an object is made. It could reside in your computer’s
RAM or on a disk file, or correspond to an IO port of a temperature sensor on the moon;
you shouldn’t care. C is supposed to do the right thing, regardless.
And indeed, on modern operating systems, all you get via pointers is something called
virtual memory, basically a fiction that maps the address space of your process to physical
memory addresses of your machine. All this was invented to ensure certain properties of
your program executions:
portable: You do not have to care about physical memory addresses on a specific machine.
safe: Reading or writing virtual memory that your process does not own will affect neither
your operating system nor any other process.
The only thing C must care about is the type of the object a pointer addresses. Each pointer
type is derived from another type, its base type, and each such derived type is a distinct
new type.
Takeaway 2.12.0.1 Pointer types with distinct base types are distinct.
In addition to providing a virtual view of physical memory, the memory model also
simplifies the view of objects themselves. It makes the assumption that each object is
a collection of bytes, the object representation (subsection 12.1);19 see figure 12.1 for a
schematic view. A convenient tool to inspect that object representation is unions (subsec-
tion 12.2). Giving direct access to the object representation (subsection 12.3) allows us to
do some fine tuning; but on the other hand, it also opens the door to unwanted or conscious
manipulations of the state of the abstract machine: tools for that are untyped pointers (sub-
section 12.4) and casts (subsection 12.5). Effective types (subsection 12.6) and alignment
(subsection 12.7) describe formal limits and platform constraints for such manipulations.
12.1. A uniform memory model. Even though generally all objects are typed, the
memory model makes another simplification: that all objects are an assemblage of bytesC .
The sizeof operator that we introduced in the context of arrays measures the size of an
object in terms of the bytes that it uses. There are three distinct types that by definition use
exactly one byte of memory: the character types char, unsigned char, and signed char.
Takeaway 2.12.1.1 sizeof(char) is 1 by definition.
Not only can all objects be “accounted” in size as character types on a lower level,
they can even be inspected and manipulated as if they were arrays of such character types.
A little later, we will see how this can be achieved, but for the moment we will just note
the following:
________________
18Only objects that are declared with keyword register don’t have an address; see subsection 13.2.2 on level 2
19The object representation is related to but not the same thing as the binary representation that we saw in subsection 5.1.3
p151 - ...
...
• Linux desktop_control administrator_rights editor/IDE (atom, vi ...) .c_files/source_code terminal navigation/path file_system folder/directories command compiler (gcc, c99 ...) compilation run_compiled_code input_output error_handling
• token header/libraries declaration/expression initialization statement indentation evaluation brackets ( ) { } [ ] < > /* */ comment_//... keyword equation operators block scope arithmetics algorithms constant identifier/name variable string boolean types qualifiers properties/arguments conditionals conversion iteration array function recursion pointer structure union macro control_flow jump thread termination ; return make memory GUI circuit_board cheat_sheet
• performing arithmetics and algorithms
• performing graphics and animation
• programming arduino raspberry_pi robots
• programming ships airplanes spaceships
declaration - all identifiers have to be declared - double A[5]; int main(void); size_t i; - ta - p9 2.2
types - 5.2 - table 5.1 -
exc
exc_bubble_sort.c
6.3_struct_tm.c
Links
• Jacob Sorber - YT -
jacobsorber.com
• C Tutorial - cprogramming.com
• My Preferred C Learning Resource - allaboutcprogramming.quora.com - Eliana Davies -
Which Programming Language Should I Learn First? - YT - Code With Huw Collinabourne -
bitwisecourses.com - courses by Huw Collinabourne
• Which are the best books to learn C? - quora.com
• How I program C - Eskil Steenberg - YT -
gamepipeline.org
• Enable snaps on Ubuntu and install sam - snapcraft.io
Sam - The Text Editor Sam (shortcut for Samantha) by Rob Pike - sam.cat-v.org
Brian Kernighan's Programming Setup | Lex Fridman - YT
sam, B, E, sam.save, samterm, samsave - screen editor with structural regular expressions - manpages.ubuntu.com
The Text Editor sam - Rob Pike - rob@plan9.bell-labs.com - 9p.io
Rob King - Fix permissions on ssam - github.com
Sam (text editor)
- WP
• Notes on Programming in C
- by Rob Pike - doc.cat-v.org
• argc and argv - crasseux.com
int main (int argc, char *argv[])
argc stands for "argument count" - argv stands for "argument vector"
books:
• Wie aktuell ist K&R C? - mikrocontroller.net
• C Primer Plus - Amazon
21st Century C --- "K&R ANSI C" ist sicher auch heute noch eines der besten Bücher zu C. Wie gut es wirklich ist, merkt man erst bei der 2. o. 3. Lektüre, da einem viele gute Hinweise beim ersten Lesen gar nicht auffallen. Fast jeder Satz ist wichtig.
links:
• List of C-family programming languages - WP
• Switching to C - One Year Later - YT -
Why I'm switching to C in 2019 - YT -
C standard library WP -
ANSI C WP -
C17 (C standard revision) WP -
C17 (C standard revision) N2176 C17 ballot ISO/IEC 9899:2017 - PDF -
The Standard iso-9899.info
A guide to understanding Linux software libraries in C -
Martin Kalin - Professor, College of Computing and Digital Media
• A Tour of the Acme Editor - YT
• Jacob Sorber - YT Channel
• Reading and Writing Files in C, two ways (fopen vs. open) - YT
• ubuntu.com -
Ubuntu - WP -
check installed version:
# terminal bash comment
EasyEffects EasyEffects (Formerly PulseEffects) – Apply Audio Effects to PipeWire Apps ubuntuhandbook.org
How to Enable PipeWire Audio Service to Replace PulseAudio in Ubuntu 21.10 & 21.04 ubuntuhandbook.org
lsb_release -a
Atom HTML Preview - atom.io
apm install atom-html-preview
Press CTRL-SHIFT-H in the editor to open the preview pane
#> No LSB modules are available.
#> Distributor ID: Ubuntu
#> Description: Ubuntu 21.10
#> Release: 21.10
#> Codename: impish
# alternative:
hostnamectl
Some useful commands:
su = superuser - exit = logout superuser
poweroff or shutdown now = shutdown, or shutdown = (1 min)
reboot or or sudo shutdown -r now = Restart, or 15:30, or 2 (min) etc. - systemctl suspend = sleep
Ubuntu Linux Change Hostname (computer name) - cyberciti.biz
If you want to open a file or folder with administrator rights, you may be rejected, even if you are admin! - error message: unit process subject does not have uid set - workaround with
sudo apt install nautilus-admin
it will give you the uid set - now you can open files or folders with right-click and select 'Edit as Administrator' -
How to Open and Edit Files and Folders in Ubuntu Desktop as an Administrator - not working, even not after restart :-( - vitux.com
SetUID, SetGID, and Sticky Bits in Linux File Permissions - geeksforgeeks.org
We can change the permissions using the chmod command, which essentially changes the ‘r’, ‘w’ and ‘x’ characters associated with the file - for checking your status enter:
ls -l /etc/passwd // checks your status
ownership of files also depends on the uid (user ID) and the gid (group ID) of the creator - to locate the setuid, look for an ‘s’ instead of an ‘x’ in the executable bit of the file permissions -> sudo chmod u+s //> missing operand after 'u+s'
#> -rw-r--r-- 1 root root (4 digits) (date) (time h:s) /etc/passwd // could be rwsr-xr-x
How can I find my User ID (UID) from terminal? - askubuntu.com - 1. id -u <username> --- 2. echo $UID --- 3. sudo id -u ---
4. id --- 5. id -u --- 6. id -g (for group) --- and some more ... --- different results!
Let's learn permissions: Permissions in Linux - geeksforgeeks.org
type ls -l -> first line beginning with - for file, second with d for directory or folder - followed by nine characters - a number - owner of file, name - owner of group, name - size of file in bytes - date and time - filename or directory's name:
-rw-rw-r-- 1 username username 2358 Feb 6 2021 spinner.c
Read, write, execute and -
drwxrwxr-x 3 username username 4096 Jan 4 2021 Steam
‘r’ means you can “read” the file’s contents
‘w’ means you can “write”, or modify, the file’s contents
‘x’ means you can “execute” the file - this permission is given only if the file is a program
If any of the “rwx” characters is replaced by a ‘-‘, then that permission has been revoked (aufgehoben, zurückgenommen) - next example:
chmod ug+rw,o-x abc.mp4
adds read(r) and write(w) permission to both user(u) and group(g) and revoke execute(x) permission from others(o) for the file abc.mp4 --- or
chmod ug=rx,o+r abc.c
assigns read(r) and execute(x) permission to both user(u) and group(g) and add read permission to others(o) for the file abc.c - numerous combinations of file permissions you can invoke, revoke and assign - ... read more in link above :-) ...
Let's learn permissions: Fix GVfs Admin “Unix process subject does not have uid set” in Ubuntu 21.10, 21.04 - ubuntuhandbook.org - says: error "... not have uid set" is a bug :-) - method 1: download 6 packages and install - method 2: run command to add the PPA:
sudo add-apt-repository ppa:ubuntuhandbook1/gvfs
Then either upgrade GVfs via “Software Updater” or run the apt command in terminal:
sudo apt install gvfs
Now log out and back in to apply change --- terminal shows:
#> Description:
Now everything works again!! Administator can open files and directories/folders - yeeeah :-)))
//> a patch version of gvfs to workaround gedit / nautilus admin:// error for 64-bit Ubuntu 21.04 & Ubuntu 21.10.
Apps:
atom clang vi vim nvim chrome filezilla skype telegram signal yoshimi gnome_shell_extensions (solve problems for Ubuntu 21.10 installing Flatpak)
Make install:
sudo apt install make
Learn make in 60 seconds. - YT by Jacob Sorber -
Make_(software) - WP
make --version # check installed version
#> GNU Make 4.3
#> Built for x86_64-pc-linux-gnu
Learn Makefiles
With the tastiest examples - makefiletutorial.com -
GNU make
- gnu.org
Flatpak Ubuntu Quick Setup - flatpak.org
Opensnitch install? How to install OpenSnitch firewall on Linux Ubuntu, Fedora, openSUSE and derivatives - linuxstoney.com
How to Fix ‘E: Could not get lock /var/lib/dpkg/lock’ Error in Ubuntu Linux - itsfoss.com
ps aux | grep -i apt
Saved Desktop reboot failure -
Ubuntu 21.10 - GDM problem - login screen - askubuntu.com
Suspend failure -
Ubuntu 21.10 - GDM problem - login screen - askubuntu.com
Nvidia driver failure:
How to Install or Upgrade Nvidia Drivers on Ubuntu 21.10 Impish Indri - linuxcapable.com
sudo apt update && sudo apt upgrade -y
sudo whoami
#> root
su # = superuser, root
ubuntu-drivers devices # displays drivers, nvidia-driver-470 recommended
sudo ubuntu-drivers autoinstall
sudo apt install nvidia-driver-470
reboot # = restart
nvidia-smi # = check status of graphic card
#> NVIDIA-SMI has failed because it couldn't communicate with the NVIDIA driver. Make sure that the latest NVIDIA driver is installed and running.
How to Install Nvidia Driver in Ubuntu Linux (GUI and CLI method) - debugpoint.com
sudo lshw -C display
Search: Ubuntu System, GUI: > Additional Drivers:
#> *-display UNCLAIMED // ?
#> description: VGA compatible controller
#> product: GM206 [GeForce GTX 950]
#> vendor: NVIDIA Corporation
...
NVIDIA Corporation: GM206 [GeForce GTX 950]
This device is using the recommended driver.
• Using NVIDIA driver metapackage from nvidia-driver-470 (proprietary, tested)
1 proprietary driver in use.
A proprietary driver has private code that Ubuntu developers can't review or improve. Security and other updates are dependent on the driver vendor.
...
nvidia-smi
same message as above: #> NVIDIA-SMI has failed because it couldn't communicate with the NVIDIA driver. Make sure ...
#> NVIDIA-SMI has failed because it couldn't communicate with the NVIDIA driver. Make sure that the latest NVIDIA driver is installed and running.sudo apt install nvidia-prime
Nvidia-settings: Unable to find display on any available system - forums.developer.nvidia.com
sudo prime-select nvidia
#> Error: no integrated GPU detected.
nvidia-xconfig --enable-all-gpus --cool-bits=31 --allow-empty-initial-configuration
Nvidia-settings - Unable to load info from any available system - forums.developer.nvidia.com
#> WARNING: Unable to locate/open X configuration file.
#> WARNING: Unable to parse X.Org version string.
#> Package xorg-server was not found in the pkg-config search path.ps aux |grep X
Ubuntu20.04 NVIDIA 470 driver is not loaded - forums.developer.nvidia.com
#> root 3692 8.9 2.3 472836 192696 tty2 Rl+ 22:09 8:07 /usr/lib/xorg/Xorg vt2 -displayfd 3 -auth /run/user/1000/gdm/Xauthority -nolisten tcp -background none -noreset -keeptty -novtswitch -verbose 3
77> root 12102 0.0 0.0 9624 2548 pts/0 S+ 23:40 0:00 grep --color=auto X
graphics.h install:
How i used graphics.h in Ubuntu 21.10? - askubuntu.com
sudo apt-get install build-essential
#> old versions - not accepted:
sudo apt install libsdl-image1.2 libsdl-image1.2-dev guile-2.2 guile-2.2-dev libsdl1.2debian libart-2.0-dev libaudiofile-dev libdirectfb-dev libdirectfb-extra libfreetype6-dev libxext-dev x11proto-xext-dev libfreetype6 libaa1 libaa1-dev libslang2-dev libasound2 libasound2-dev
#> sudo apt-get install libsdl-image1.2 libsdl-image1.2-dev guile-2.0 guile-2.0-dev libsdl1.2debian libart-2.0-dev libaudiofile-dev libesd0-dev libdirectfb-dev libdirectfb-extra libfreetype6-dev libxext-dev x11proto-xext-dev libfreetype6 libaa1 libaa1-dev libslang2-dev libasound2 libasound2-dev
sudo apt-get install guile-2.2-libs
if rejected: libsdl1.2debian libdirectfb-dev libxext-dev guile-2.0 guile-2.0-dev, install each separately with: sudo apt-get install ... --- terminal: //> ... is already the newest version
if guile-2.0 or guile-2.0-dev rejected:
sudo apt-get install guile-2.2-libs
Updated Open Graphics Drivers - since 2011! - launchpad.net - Supported Ubuntu versions: 20.04 (focal) - 21.04 (hirsute) - 21.10 (impish)
sudo add-apt-repository ppa:oibaf/graphics-drivers
Updated and Optimized Ubuntu Free Graphics Drivers - launchpad.net
How to add “graphics.h” C/C++ library to gcc compiler in Linux - launchpad.net - 05 Aug, 2021
How do I use graphics.h in Ubuntu? - launchpad.net - 05 Aug, 2021
OpenGL:
Ubuntu – How to use graphics.h in Ubuntu - itectec.com - 05 Aug, 2021
sudo apt-get install freeglut3-dev
works fine :-) created opengl_test.c
Vulkan:
GNU/Linux Ubuntu Vulkan SDK Installation Guide - tutorialforlinux.com
Ubuntu Gnome Shell Extensions:
...
• Go (programming language) - WP -
go.dev
create file helloworld.go
package main
create a new folder "helloworld" - save file helloworld.go in this folder - via terminal move to this folder: cd helloworld - compile with
import "fmt"
func main() {
fmt.Println("hello, world")
}go mod init example/helloworld
run with
//> go: creating new go.mod: module example/helloworldgo run .
//> hello, world
or create executable
go build -o helloworld helloworld.go
file helloworld is created into your folder - run with
go run ./helloworld
//> hello, world
More: Top 5 online resources to learn Go from scratch in 2020 - golang.cafe -
Effective Go - go.dev
• What's the best way to learn LISP? - stackoverflow.com -
Lisp (programming language) - WP -
John McCarthy (1927-2011): Artificial Intelligence (complete) - Thinking Allowed -Jeffrey Mishlove - YT
• Practical Common Lisp - gigamonkeys.com
• What's the best way to learn LISP? - geeksforgeeks.org
sudo apt-get -y install sbcl
• Atom editor: install atom-slime language-lisp lisp-paredit - read for more: Practical Common Lisp - gigamonkeys.com
sudo apt-get -y install curl
curl -o /tmp/ql.lisp http://beta.quicklisp.org/quicklisp.lisp
sbcl --no-sysinit --no-userinit --load /tmp/ql.lisp \
--eval '(quicklisp-quickstart:install :path "~/.quicklisp")' \
--eval '(ql:add-to-init-file)' \
--quit
sbcl ; ("sbcl" starts Common Lisp - ";" starts a comment until end of line)
* (print "hello, world from Lisp") ; "*" is the asterisk-symbol showing that Lisp is running on terminal, waiting for your code input --- output: hello, world from Lisp
(exit) ; quits Common Lisp
• How To Open ePub Books In Ubuntu Linux - itsfoss.com
sudo apt-get install fbreader
• Common Lisp Documentation - The Common Lisp HyperSpec - lispworks.com
• How hard is it to learn LISP? - quora.com - - "You can easily learn LISP in one day, sufficient to implement simple applications." James McDonald, former Founder at Lucid, Inc. - "Yes, Lisp is about the most easy programming language. The syntax is very simple as the usage to define new functions. The only difficult thing is to keep parenthesis in balance and right places." (Pekka Järveläinen)
• Practical Common Lisp - gigamonkeys.com
LISt Processing - ...
• 54 Best Lisp Books of All Time - bookauthority.org
• Which book is the best for learn Common Lisp? - reddit.com
• Land of Lisp - github.com -
clisp.org - p18 install:
sudo apt-get install clisp
p19 - REPL (read-eval-print loop) -
[1]> (+ 3 (* 2 4))
11
p21 - ch2 - Creating your first Lisp program - Guess-My-Number Game - guess number betw. 1-100 -
p22 - need write 3 functions: guess-my-number, smaller, and bigger - player calls functions from REPL - starting CLISP (or any other Lisp), you are presented with REPL, from which the commands you type will be read, then evaluated, and finally printed - in this case, we’re running the commands guess-my-number, smaller, and bigger - to call a function in Lisp, you put parentheses around it, along with any parameters you wish to give the function - since these particular functions
don’t require any parameters, we simply surround their names in parentheses when we enter them - let’s think about the strategy behind this simple game - after a little thought, we come up with the following steps:
1.Determine the upper and lower (big and small) limit of the player’s number - since the range is between 1 and 100, the smallest possible number would be 1 and the biggest would be 100
2.Guess a number in between these two numbers
3.If the player says the number is smaller, lower the big limit
4.If the player says the number is bigger, raise the small limit
= binary search - defparameter (can be changed), defvar (can't)
> (defparameter *foo* 5)
this book only defparameter - dynamic variable or special variable later - spaces and line breaks are completely ignored by Lisp:
FOO
> *foo*
5
> (defparameter *foo* 6)
FOO
> *foo*
6
> (defvar *foo* 5)
FOO
> *foo*
5( defparameter
p25 - this book not so much interested in indentation rules ... -
*small* 1)
*SMALL*[1]> (load "example.lisp")
...
p114 - graphviz.org - creates graph.dot.png ... p116 - ...
p303 - ch15 - DICE OF DOOM, A GAME WRITTEN IN THE FUNCTIONAL STYLE -
dicewars - gamedesign.jp
... - ch20 - Epilogue - p463 end of chapters - INDEX ... p482 - Ads - p488 end of book
by Guy L. Steele Jr. - c 1984, 1989 - 30ch 1095p + Bibliography + Index p1131 end of book
by Paul Graham - 17ch 286p + Appendix A-D + Notes + Index p433 end of book
• haskell.org -
WP
wiki.ubuntuusers.de - before installong Haskell install:
System requirements
Resolve generated breaks, this may be caused by held packages
Please ensure the following distro packages are installed before continuing (you can exit ghcup and return at any time):
build-essential curl libffi-dev libffi8ubuntu1 libgmp-dev libgmp10 libncurses-dev libncurses5 libtinfo5
Haskell in 5 steps - wiki.haskell.org
ghci -- start
quit: ctrl D
-- comment
main = putStrLn "hello, world from FO .hs :-)" -- helloworld.hs
ghc -o helloworld helloworld.hs -- compile
./helloworld -- run
--> hello, world from FO .hs :-) -- output
• scratch.mit.edu -
WP
https://scratch.mit.edu
• gtk.org -
WP
getting started - docs.gtk.org
Install GTK4 Linux - docs.gtk.org
LEARNING gtk3 - PDF - riptutorial.com
How to create GUI in C programming using GTK Toolkit - geeksforgeeks.org
GTK3 Drawing Area - docs.gtk.org
• liquid.html - terasof.com
• Graphics Programming in Linux - opensourceforu.com - first used Turbo C and graphics.c - got stuck with its limited graphics functionality and maximum resolution - teacher introduced OpenGL - rich graphics experience and platform independence - contacted the LFY editorial team members for platform to share about OpenGL - OpenGL (Open-source Graphics Library) has 3 major library header files:
gl.h, glu.h and glut.h - create basic.c and basic2.c (red window) - ...
• A Beginner’s Guide to Setup OpenGL in Linux (Debian)
- medium.com - instruction to setup OpenGL in Ubuntu and Debian-based distros
• Recommended tutorial on how to use OpenGL: learnopengl.com, website by joeydevries.com - Joey's complete book "Learn OpenGL - Graphics Programming", and the free PDF of the complete book, 523 pages! Wow! 1st edition 6/2020 --- Breakout Game -
Breakout II - ...
• Recommended videos, 31 lessons to learn OpenGL by
The Cherno - YT
• Is it possible to use OpenGL purely with C instead of C++? - gamedev.stackexchange.com - Yes, in fact OpenGL is a C library - you can perfectly use it with C
• Cont. Beginner's Guide OpenGL - installing libraries GLFW and GLAD - ...
• Vulkan -
vulkan.org -
vulkan-tutorial.com - 284 pages - Copyright 4/2020 - p8: You can use C instead of C++ if you want, but you will have to use a different linear algebra library and you will be on your own in terms of code structuring. We will use C++ features like classes and RAII to organize logic and resource lifetimes.
Install And Test Vulkan On Linux - linuxconfig.org - not working
Graphics driver check on Ubuntu 20.04 - linuxconfig.org
How to Install or Upgrade Nvidia Drivers on Ubuntu 21.10 Impish Indri - after installation check status of the nvidia graphic card:
nvidia-smi
NVIDIA-SMI has failed because it couldn't communicate with the NVIDIA driver. Make sure that the latest NVIDIA driver is installed and running.
NVIDIA-SMI has failed because it couldn’t communicate with the NVIDIA driver. Make sure that the latest NVIDIA driver is installed and running - forums.developer.nvidia.com
E: dpkg was interrupted... run 'sudo dpkg --configure -a' -
sudo apt-get install -f // rejects - after that:
How do I disable UEFI Secure Boot? - support.avira.com -
sudo dpkg --configure -a // rejects - then:
sudo rm /var/lib/apt/lists/lock // delets lock files
sudo rm /var/cache/apt/archives/lock // same
sudo rm /var/lib/dpkg/lock // same - now repeat:
sudo dpkg --configure -a // again: Your system has UEFI Secure Boot enabled.sudo mokutil --enable-validation // to re-enable Secure Boot if wanted
Vulkan tutorials for C - linustechtips.com
SDL2-C - my efforts to make coding with pure C and SDL2 more clear and fun
Getting Started with SDL and C - learncgames.com
HOW TO SET UP SDL2 ON LINUX - learncgames.com
SDL2 DRAG AND DROP - gigi.nullneuron.net
• Metal_(API)
• Demo Unreal Engine Unreal Engine Metahuman with Faceware Mark IV HMC, Xsens, Manus & GlassboxTech
• ch1 - Inroduction - p7 - ... - learncpp.com - ...
• Next-Gen Graphics FINALLY Arrive [Unreal Engine 5] - YT
Difference Between C and C++ by Vijay Singh Khatri - hackr.io
• C system progr. lang. - comkpiled, lightweight, manual memory managm. - strength is performance - C++ originally should be a more flexible and efficient extension of C - OOP - compatible to C, lightweight, compiled
• C top-down - C++ like all OOP lang. bottom-up
• C++ compatible with some other lang., C not
• ...
by Cyber Punk Architects
ch8 - How To Be Completely Anonymous Online Like The Pro's
p66 - 1 Linux - 2 ExpressVPN - 3 TOR network - 4 email address "Hushmail's", website Mailinator, defended from the third party sites which are tracking your IP address and location every time you go online - 5 never use Google while hacking - StartPage or DuckDuck go will prevent google from remembering your online searches and history of your online activity - you will be able to search through the google without compromising your identity -
p71 - 6 use public wireless connection, never public WiFi
ch9 - How To Setup NMAP
setting up NMAP (network mapper, the type of security scanner which is used in order to discover any hosts and service on the devices) - ... - nmap.org
sudo apt-get install nmap
How to Install and Use Nmap on Ubuntu - unixcop.com
nmap --version //> Nmap version 7.80 ( nmap.org )
man nmap // manual nmap
nmap --help
...
ch10 - How To Keep Yourself Safe From Being Hacked
...
by Simon Long - 1st edition, Raspberry Pi Press, 19. April 2019, $4,99/16,76 - for Raspberry Pi - 156 pages - Amazon
Welcome ...
p5 - C earlyb 1970s - ... - most of Linux and Raspbian written in C - ... 1st book part intro to C for absolute beginners - 2nd part shows how to use C to create desktop applications for
Raspbian, using the GTK toolkit - you don’t need any programming experience, and a Raspberry
Pi running Raspbian is all you need to get started - About Simon Long ...
Contents
p7 - Ch 1-26...
Ch1 - Getting started
raspberrypi.com -
Raspberry Pi - WP -
Raspbian OS - WP -
What is a Raspberry Pi? - YT -
raspberrypi.org/help -
Which Raspberry Pi should you choose for your project? - opensource.com
Raspberry Pi 4 - raspberrypi.com
minimum need: 1. 3B+ 2. power supply 3. micro SD card 4. keyboard 5. mouse 6. TV or computer screen 7. HDMI screen cable, adapter if needed - optional: 8. case 9. speakers or headphones 10. breadboard cables and other ---
A BRAND-NEW PI FOR π DAY - YT
Raspberry Pi 3 Model B+ is the newest, fastest, and easiest to use -
• 1. Raspberry Pi
3 Model B+ 1.4GHz 64-bit quad-core ARM Cortex-A53 Dual-band 802.11ac wireless LAN Bluetooth 4.2 - 3x faster Ethernet - Power over Ethernet support (with separate PoE HAT) NZ$63.25 inc GST
+ 2. power adapter
Raspberry Pi Official White Multi Plugs In (AU EU UK US) Travel Adapter with MicroUSB Connector 5.1V 2.5A 13W NZ$21.85
+ 3. card
Raspberry Pi TRANSCEND 16GB MicroSD Card Pre-loaded with NOOBS V2 NZ$27.94
+ 8. case
Official Red & White Enclosure for Raspberry Pi 3 Model B and B+ NZ$20.70
+ 10. breadboard cables Raspberry Pi Breadboarding Wire Bundle NZ$18.40
+ 10.a lead cables test
Raspberry Pi Alligator Clip Test Lead Cables (Multi Colours) 40cm Long (Set of 10) NZ$10.35
+ 11. camera Raspberry Pi Infrared Night Vision Camera Kit. Focal Adjustable, 1080P, 5MP, Sensitive Infrared Lights. Quick Install Guide NZ$72.45
+ 4. keyboard
+ 5. mouse
+ 6. screen TV or computer
+ 7. HDMI cable
+ Raspberry Pi Official White HDMI Cable 2m Male to Male HDMI 2.0 with Ethernet and Audio Return Channel Support 3D, 4K, 2160P/60Hz and X.V.colour NZ$16.10
9. speakers or headphones
--- Combo Deal Raspberry Pi 3 Model B+ Official Starter Kit White Edition with OS (Inc 1. New Pi3 Mainboard, 2. Multi Plugs Aadapter, 8. Red & White Case, 3. 16GB NOOBS Card, 7. HDMI 2M Cable) NZ$150.66 -
pbtech.co.nz
What languages are used to program raspberry PI? - raspberrypi.org
Python 2 or 3 - raspberrypi.org
Other shop pishop.nz
Machines for world knowledge 300 Exabytes, ship SH and spaceship SS - Computers should do what humans want them to do - input output - keyboard pad touchscreen cam mic controller etc. - based binary - ASM and C needed - terminal - IN HTML CSS JS WASM - Arduino Rapberry PI etc. - Linux - games animation sounddesign graficdesign - art FP MP WR - O MG
Chrome: Ctrl F5 > reload tab
C terasof.com (first studies)
IDL Webdesign -
HTML Workshop -
JavaScript
TIOBE Index for December 2021 - tiobe.com
30 Best PS4 games to play right now 2021 - tiobe.com -
What Remains of Edith Finch - 2017 - WP
Session 1 • Thu 2021-3-18 Coswig (Anhalt) 23:15-3:40 WR cfo.html - start ♡♡♡