If you are completely new to C consider to start here:

♡ Coding Demo ♡





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

Difference between C and C++
Raspberri Pi


♡ Listen to New York Jazz Lounge - Bar Jazz Masterpieces ♡♡♡ ♡ ♡♡♡



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!


... coming soon :-))

1 • Linux Ubuntu (Mac Windows etc.) install and adjust

#include <stdio.h>

int main(void) {
    printf("hello, world\n");
    return 0;

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 ...

ch1 to ch27

p... - ... coming soon ♡ ♥ ♡ ...

ch27 - basic examples - small programs of all kinds

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

Test the Best

Your browser does not support the HTML canvas tag.

Modern C

by Jens Gustedt - Jens Gustedt's Blog - Amazon Paperback - 7 Oct. 2019 - €35.19 - 315 pages


(Wed 2021-12-15 Leipzig 17:37-21:54 S1 (session)) Level 0 Encounter - Ch 1-2 p1 - Level 1 Acquaintance - p17 Ch 3-8 - Level 2 Cognition - p117 Ch 9-14 - Level 3 Experience - p203 Ch 15-19 - p287 Takeaways - p299 Bibliography - p301 Index - eob p315

About this book

p_i - p_iii - 2nd ed Oct. 10, 2019
p_i - C early 70s, Richie 1993 - used in incredible number of apps - all around us, computers phones cameras set-top boxes refrigerators cars mainframes satellites ... basically in all modern device with progr. interface - in contrast to ubiquitous (allgegenwärtig) presence of C programs good knowledge is scarce (rar - ä) ... C fails to motivate climbing to higher levels of knowledge - book intended to change that general attitude - therefore organized in levels - splits some difficult subjects (pointers etc.) across levels to not swamp readers too early with wrong info - book's organiz. will be explained shortly - many universally applicable ideas presented, valid for other progr. lang. (such as Java Python Ruby C# C++), book primarily addresses concepts and practices unique to C or of practic. value when progr. in C
C versions
... - book refers mainly to C17 - ... - some compilers don't implement this standard completely - need at least compiler C99 - for changes that C11 added using emulation layer such as my macro package P99 might suffice - available at gitlab.inria.fr
C and C++
p_ii - ... - as in all human activities, progress in C is driven by many factors: corporate or individual interest, politics, beauty, logic, luck, ignorance, selfishness, ego, sectarianism, ... (add your primary motivation here) - development of C has flaws and artifacts (Prüf-Gegenstand, Gegenstand ...) that can only be understood with their historical and societal context - important part C++ sister lang. - C and C++ separated from a common ancestor more than 30 years ago and have evolved separately ever since - exchanged and adopted each other’s concepts over the years - nevertheless, many differences remain, and generally all that is said in this book is about C, not C++ --- many code examples that are given will not even compile with a C++ compiler - so we should not mix sources of both languages
Takeaway A - C and C++ are different: don’t mix them, and don’t mix them up - takeaways marked in book, summarize features, rules, recommendations etc. - list of takeaways end of book, can be used as cheat sheet
need to fulfill - learn them first, or may waste a lot of time - can't learn progr. lang. without practicing - must have decent progr. environment at disposal (PC laptop), must master it to some extent - IDE (an integrated development environment) is software for building applications that combines common developer tools into a single graphical user interface (GUI) or collection of separate utilities - editors such as vim or emacs, (atom FO), compilers such as c99 gcc clang
Must be able to do following:
1 • navigate file system - hierarchically in directories - find and manipulate files
2 • edit progr. text - diffrent from edit. letter - editor should have basic understanding of C - if open .c it highlights some keywords, indents code according to nestedness of {} brackets
3 • execute/run progr. - progr.s in book very basic at first, will not offer any graphical featrures - need to be launched in command line - example of such a progr. is the compliler - on Unix-like envoronments command line usually called a shell, launched in a console or terminal
4 • compile progr. text - some environments provide menu button or keyboard shortcut fot compilation - alternative to that is launch compiler in command line of terminal - this compiler must adhere (einhalten, befolgen ...) to recent standards; don't waste your time with compiler that doesn't conform
p_iii - if never progr. before book will be tough - knowing some of following will help: Basic C (historical versions) C++ Fortran R bash JavaScript Java MATLAB Perl Python Scilab etc. - perhaps other progr. experience, maybe without noticing - many technical specific. come in some sort of specialized language that can be helpful as analogy: HTML and LaTeX
you should have an idea of following concepts, precise meanings may be bit different in C than in the context where you learned them:
1 • Variables - a b x y name - Named entities that hold values
2 • Conditionals - if else ... - Doing something (or not) subject to a precise condition - ( = conditional statements, c. expressions and c. constructs - WP)
3 • Iteration - a++ --b --- Doing something repeatedly for a specified number of times or until a certain condition is met - ( = applying a function repeatedly, using the output from one iteration as the input to the next - WP)
Source code
many of progr. code snippets presented in this book available for download as a .zip archive from the book’s website at hal.inria.fr - allows to view in context and compile and try out - archive also contains a makefile with discription of components needed to compile these files - centered around Linux or, more generally, POSIX systems - may help find out what you need on a different system
Exercises and challenges
book organized in levels 0-3 - starting level 0 "Encounter" will summarize the very basics of progr. with C - ...
level 1 "Acquaintance" details most principal concepts and features such as control structures, data types, operators, and functions - it should give you a deeper understanding of the things that are going on when you run your programs - this knowledge should be sufficient for an introductory course in algorithms and other work at that level, with the notable caveat (Vorsichtsmaßnahme) that pointers are not yet fully introduced
level 2 "Cognition" goes to the heart of C - fully explains pointers, familiarizes with C's memory model, and allows to understand most of C's library interface - Completing level should enable to write C code professionally - therefore begins with essential discussion about the writing and organization of C programs - ...
level 3 "Experience" - goes into detail about specific topics, such as performance, reentrancy, atomicity, threads, and type-generic programming - these are probably best discovered as you go, which is when you encounter them in the real world - nevertheless, as a whole, they are necessary to round off the discussion and to provide you with full expertise in C - anybody with some years of professional programming in C or who heads a software project that uses C as its main programming language should master this level
Jens Gustedt completed studies of mathematics at Univ. of Bonn and Berlin Technical University - research at that time covered intersection between discrete mathematics and efficient computation - since 1998 working as senior scientist at French National Institute for Computer Sciende and Control (INRA), first in LOROA lab, Nancy, since 2013 in ICube lab, Strasbourg - throughout career most of scientific research has been accompanied by the development of software, at the beginning mostly in C++ --- later exclusively in C - now serves AFNOR as an expert on the ISO committee JTC1/SC22/WG14 and is co-editor of the C standard document ISO/IEC 9899:2008 - also has successful blog that deals with programming in C and related topics gustedt.wordpress.com

Level 0 - Encounter ch 1-2 p1-16

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

ch1 - Getting started

p2 - Simple program chosen because contains many of constructs of C lang. - ... - reaction of advanced readers, beginners or intermediate - be patient - after 10 pages understanding will have increased a lot - citation from "Hitchhiker's Guide to the Galaxy by Douglas Adams Adams 1986:
Takeaway B - Don't panic
... - progr. in C = having computer complete some specific tasks - Listing 1.1 A first example of a C program -
1. section covers: • Introduction to imperative programming • Compiling and running code
1.1 - Imperative programming
p3 ??? - Listing 1.1_getting_started.c - printed some lines on terminal, ordered printf function to fulfill that - rest is some sugar to specify which nunbers will be printed, and how many of them - ... -
Takeaway - C is an imperative programming language
... -
1.2. Compil(e)ing and running -
p4 - compiler translates C text .c into code that machine can understand, binary code or executable - too complicated to explain now - C provides a level of abstraction for all the different machine-specific languages (usually referred to as assembler) $ c99 -Wall -o 1.1_getting_started 1.1_getting_started.c -lm p5 - • c99 = compiler program
-Wall = warn tells it to warn us about anything that it finds unusual
-o 1.1_getting_started tells it to store the compiler output in a file named 1.1_getting_started
• 1.1_getting_started.c names the source file containing the C code
-lm tells it to add some standard mathematical functions if necessary > ./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
p7 - different warnings for compiling 1.2_getting_started.c - different compilers like clang gcc --- you can force your compiler to reject programs that produce such diagnostics - for gcc, such a command-line option would be -Werror
Takeaway - C is a compiled programming language ?
Takeaway - A correct C program is portable between different platforms ?
Takeaway A C program should compile cleanly without warnings
• C designed to give computers orders - thereby it mediates between us (the programmers) and computers
• C must be compiled to be executed - the compiler provides the translation between the language that we understand (C) and the specific needs of the particular platform
• C gives a level of abstraction that provides portability - one C program can be used on many different computer architectures
• The C compiler is there to help you - if it warns you about something in your program, listen to it
- Recompile until you have a flawless program - 3rd difference between 1.1 and 1.2: return EXIT_SUCCESS; --- return 0;

ch2 - The principal structure of a program

p8 - This section covers
• C grammar
• Declaring identifiers
• Defining objects
• Instructing the compiler with statements
real programs will be more complicated and contain additional constructs, but very similar - Listing 1.1 already has most of the structural elements of a C program - 2 categories of aspects to consider in a C program: syntactical aspects (how do we specify the program so the compiler understands it?) and semantic aspects (grammer) (what do we specify so that the program does what we want it to do?) - now following: semantic aspects: declarative parts (what things are), definitions of objects (where things are), and statements (what things are supposed to do)
2.1 Grammar
elements: special words (Listing 1.1) #include int void double for return (C jargon = directives, keywords, reserved identifiers) - in this book usually bold - these words represent concepts and features that the C language imposes and that cannot be changed - punctuation: 5 kinds of brackets: { ... } ( ... ) [ ... ] /* ... */ and < ... > --- group certain parts together - always in pairs - 2 different separators or terminators: comma , and semicolon ; - , separates 4 arguments of printf - line 15 , can follow last element of list - 1.1_getting_started.c line 15:     [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.1
Takeaway - punctuation characters can be used with several different meanings -
comments: /* ... */ everything inside is comment - Exs9 find different uses of {} and [] ...
p9 - comments ignored by compiler - explain + document code - improves readability + comprehensibility (Verständlichkeit, Fasslichkeit) - other form // so-called C++-style comment, extends from // to end of line
literals: refer to fixed values that are part of program - 0, 1, 3, 4, 5, 9.0, 2.9, 3.E+25, .00007, and "element %zu is %g, \tits square is %g\n"
identifiers: these are “names” that we (or the C standard) give to certain entities in the program - here we have A, i, main, printf, size_t, and EXIT_SUCCESS - identifiers can play different roles in a program - among other things, they may refer to
data objects (such as A and i). These are also referred to as variables
type aliases, such as size_t, that specify the “sort” of a new object, here of i - observe the trailing _t in the name - this naming convention is used by the C standard to remind you that the identifier refers to a type
functions, such as main and printf
constants, such as EXIT_SUCCESS
functions: 2 of the identifiers refer to functions: main and printf - as we have already seen, printf is used by the program to produce some output - the function main in turn is defined: that is, its declaration int main(void) is followed by a block enclosed in { ... } that describes what that function is supposed to do. In our example, this function definition goes from line 6 to 24 - main has a special role in C programs, as we will encounter: it must always be present, since it is the starting point of the program’s execution
operators: of the numerous C operators, our program only uses a few:
= for initialization and assignment,
< for comparison,
++ to increment a variable (to increase its value by 1), and
* to multiply two values
Just as in natural languages, the lexical elements and the grammar of C programs that we have seen here have to be distinguished from the actual meaning these constructs convey (vermitteln). In contrast to natural languages, though, this meaning is rigidly specified and usually leaves no room for ambiguity (Mehrdeutigkeit). In the following subsections, we will dig into the 3 main semantic categories that C distinguishes: declarations, definitions, and statements
2.2. Declarations
Before we may use a particular identifier in a program, we have to give the compiler a declaration that specifies what that identifier is supposed to represent - this is where identifiers differ from keywords: keywords are predefined by the language and must not be declared or redefined
Takeaway All identifiers in a program have to be declared
3 of the identifiers we use are effectively declared in our program: main, A, and i - later on, we will see where the other identifiers (printf, size_t, and EXIT_SUCCESS) come from - we already mentioned the declaration of the main function - all three declarations, in isolation as “declarations only,” look like this: int 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:
• i is of type size_t
• main is additionally followed by parentheses, ( ... ), and thus declares a function of type int
• A is followed by brackets, [ ... ], and thus declares an array - an array is an aggregate of several items of the same type; here it consists of 5 items of type double - these 5 items are ordered and can be referred to by numbers, called indices, from 0 to 4
Each of these declarations starts with a type, here int, double, and size_t - will see later what that represents - for the moment, it is sufficient to know that this specifies that all three identifiers, when used in the context of a statement, will act as some sort of “numbers
The declarations of i and A declare variables, which are named items that allow us to store values - they are best visualized as a kind of box that may contain a “something” of a particular type:
             [0]                   [1]                   [2]                      [3]                           [4]
important to distinguish box itself (object), specification (type), content (value) and name or label written on box - in such diagrams we put ?? if don't know actual value of item -
for other 3 identifiers (printf, size_t, EXIT SUCCESS) we don't see any declaration - in fact they are predeclared identifiers - compiling listing 1.2, the info about these identifiers doesn't come out of nowhere - we have to tell compiler where to obtain info about them - done right at start of progr. - in lines 2 and 3 printf is provided by stdio.h, and size_t and EXIT_SUCCESS come from stdlib.h - real declarations of these identifiers specified in .h files with these names somwhere on computer, something like: 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 ...)
Takeaway Identifiers may have several consistent declarations - contradicting declarations for the same identifier in same part of program generally not allowed - "same part of program” is supposed to mean: the scope is a part of the program where an identifier is visible
Takeaway Declarations are bound to the scope in which they appear - These scopes of identifiers are unambiguously described by the grammar - in listing 1.1, we have declarations in different scopes:
A is visible inside the definition of main, starting at its declaration on line 8 and ending at the closing } on line 24 of the innermost { ... } block that contains that declaration
i has more restricted visibility. It is bound to the for construct in which it is declared. Its visibility reaches from that declaration on line 16 to the end of the { ... } block that is associated with the for on line 21
main is not enclosed in a { ... } block, so it is visible from its declaration onward until the end of the file for (size_t i = 0; i < 5; ++i) {
  printf ("element %zu is %g, \tits square is %g\n",
    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 globals
2.3. Definitions
Generally, declarations only specify the kind of object an identifier refers to, not what the concrete value of an identifier is, nor where the object it refers to can be found - this important role is filled by a definition
Takeaway Declarations specify identifiers, whereas definitions specify objects
We will later see that things are a little more complicated in real life, but for now we can make the simplification that we will always initialize our variables - an initialization is a grammatical construct that augments a declaration and provides an initial value for the object - for instance,
size_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 stored
Takeaway An object is defined at the same time it is initialized
Our box visualization can now be completed with a value, 0 in this example:
p12 - A is a bit more complex because it has several components: double 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:
             [0]                   [1]                   [2]                      [3]                           [4]
The form of an initializer that we see here is called designated: a pair of brackets with an integer designate which item of the array is initialized with the corresponding value - for example, [4] = 3.E+25 sets the last item of the array A to the value 3.E+25 - as a special rule, any position that is not listed in the initializer is set to 0 - in our example, the missing [2] is filled with 0.0
Takeaway Missing elements in initializers default to 0
array positions or indices do not start with 1 for the first element, but with 0 - think of an array position as the distance of the corresponding array element from the start of the array
Takeaway For an array with n elements, the first element has index 0, and the last has index n-1
For a function, we have a definition (as opposed to only a declaration) if its declaration is followed by braces { ... } containing the code of the function: 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)
Takeaway Each object or function must have exactly one definition
(we will see later how these number literals with dots (.) and exponents (E+25) work)
2.4. Statements
p13 - the second part of the main function consists primarily of statements - statements are instructions that tell the compiler what to do with identifiers that have been declared so far - we have for (size_t i = 0; i < 5; ++i) {
  printf("element %zu is %g,\tits square is %g\n",
         A[i] * A[i]);

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).
2.4.1. Iteration - the for statement tells the compiler that the program should execute the printf line a number of times. This is the simplest form of domain iteration that C has to offer. It has 4 different parts
the code that is to be repeated is called the loop body: it is the { ... } block that follows the for ( ... ) - the other 3 parts are those inside the ( ... ) part, divided by semicolons:
(1) The declaration, definition, and initialization of the loop variable i, which we already discussed. This initialization is executed once before any of the rest of the entire for statement
(2) A loop condition, i < 5 specifies how long the for iteration should continue - this tells the compiler to continue iterating as long as i is strictly less than 5 - the loop condition is checked before each execution of the loop body
(3) Another statement, ++i, is executed after each iteration. In this case, it increases the value of i by 1 each time
If we put all of these together, we ask the program to perform the part in the block five times, setting the value of i to 0, 1, 2, 3, and 4, respectively, in each iteration - the fact that we can identify each iteration with a specific value for i makes this an iteration over the domain 0, . . . , 4 - there is more than one way to do this in C, but for is the easiest, cleanest, and best tool for the task
Takeaway Domain iterations should be coded with a for statement
A for statement can be written in several ways other than what we just saw. Often, people place the definition of the loop variable somewhere before the for or even reuse the same variable for several loops - don’t do that: to help an occasional reader and the compiler understand your code, it is important to know that this variable has the special meaning of an iteration counter for that given for loop
Takeaway The loop variable should be defined in the initial part of a for
2.4.2. Function calls are special statements that suspend the execution of the current function (at the beginning, this is usually main) and then hand over control to the named function
p14 - in our example printf("element %zu is %g, \tits square is %g\n",
       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 later
2.4.3. Function return - the last statement in main is a return - it tells the main function to return to the statement that it was called from once it’s done - here, since main has int in its declaration, a return must send back a value of type int to the calling statement - in this case, that value is EXIT_SUCCESS
Even though we can’t see its definition, the printf function must contain a similar return statement - at the point where we call the function on line 17, execution of the statements in main is temporarily suspended - execution continues in the printf function until a return is encountered - after the return from printf, execution of the statements in main continues from where it stopped
FIGURE 2.1. - Execution of a small program
Figure 2.1 shows a schematic view of the execution of our little program: its control flow - first, a process-startup routine (on the left) that is provided by our platform calls the user-provided function main (middle) - that, in turn, calls printf, a function that is part of the C library, on the right - once a return is encountered there, control returns back to main (5 loops); and when we reach the return in main, it passes back to the startup routine - the
p15 - latter transfer of control, from a programmer’s point of view, is the end of the program’s execution

• C distinguishes the lexical structure (the punctuators, identifiers, and numbers), the grammatical structure (syntax), and the semantics (meaning) of programs
• All identifiers (names) must be declared (type) such that we know the properties of the concept they represent
• All objects (things that we deal with) and functions (methods that we use to deal with things) must be defined; that is, we must specify how and where they come to be
Statements indicate how things are going to be done: iterations (for) repeat variations of a certain task, functions calls (printf(...)) delegate a task to a function, and function returns (return something;) go back where we came from
p16 - (blank)

Level 1 - Acquaintance ch 3-8 p17-116

p17 - 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. */
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);
argc and argv - crasseux.com - 'argument count' and 'argument vector'
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. */
int atexit(void handler(void));
/* Compatible declaration for the same function. */
int atexit(void (*handler)(void));
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
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>
# 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;
2.1_hello_world.c - 2.1_hello_world

ch3 - Everything is about control

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) {
  j = 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
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) {
  j = i - 25;
} else {
  j = i;
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
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
else statement1-or-block1
possibilities for condition (the controlling expression) are numerous - they can range from simple comparisons, as in this example, to very complex nested expressions
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) {
  if (i) {
    printf("element %zu is %g, \tits square is %g\n",
           A[i] * A[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
[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 The value 0 represents logical false
Takeaway 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 = ...;
if (( b != false ) == true ) { // [Exs 1] Add the if (i) condition to the program, and compare the output to the previous.
as bool b = ...;
if (b) {
p23 - Generally:
Takeaway 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 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"

3.2. Iterations - Previously, we encountered the for statement to iterate over a domain; in our introductory example, it declared a variable i that was set to the values 0, 1, 2, 3, and 4 - the general form of this statement is 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 domain
condition2 tests whether the iteration should continue - then, expression3 updates the iteration variable used in clause1 - it is performed at the end of each iteration - some advice:
• Because we want iteration variables to be defined narrowly in the context for a for loop (cf. Takeaway, clause1 should in most cases be a variable definition
• Because for is relatively complex with its 4 different parts and not easy to capture visually, statement-or-block should usually be a { ... } block
Let’s see some more examples: for (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)
p24 - The 1st for counts i down from 10 to 1, inclusive - the condition is again just the evaluation of the variable i; no redundant (überflüssiger) test against value 0 is required - when i becomes 0, it will evaluate to false, and the loop will stop - the 2nd for declares two variables, i and stop - as before, i is the loop variable, stop is what we compare against in the condition, and when i becomes greater than or equal to stop, the loop terminates
The 3rd for looks as though it would go on forever, but actually it counts down from 9 to 0 - in fact, in the next section, we will see that “sizes” in C (numbers that have type size_t) are never negative
[Exc 2] (Exercise 3 missing?) Try to imagine what happens when i has value 0 and is decremented by means of the operator --
Observe that all 3 for statements declare variables named i. These three variables with the same name happily live side by side, as long as their scopes don’t overlap
There are two more iterative statements in C, while and do: 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)
(C fabs() - The fabs() function returns the absolute value of a number --- [Mathematics] |x| = fabs(x) [In C programming] --- programiz.com)
C mathematical functions - WP)
What does EPS mean in C? - some form of epsilon to determine whether the number is "small enough to be insignificant" - according to C99: the machine epsilon is "the difference between 1 and the least value greater than 1 that is representable in the given floating point type" ... - stackoverflow.com)
(Methods of computing square roots - Babylonian_method (Heron's method) - WP)

It iterates as long as the given condition evaluates true - the do loop is very similar, except that it checks the condition after the dependent block: 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 !
As with the for statement, with do and while it is advisable to use the { ... } block variants - there is also a subtle syntactical difference between the two: do always needs a semicolon ; after the while (condition) to terminate the statement - later, we will see that this is a syntactic feature that turns out to be quite useful in the context of multiple nested statements; see section 10.2.1.
All 3 iteration statements become even more flexible with break and continue statements - a break statement stops the loop without reevaluating the termination condition or executing the part of the dependent block after the break statement: while (true) {
  double prod = a * x;
  if (fabs(1.0 - prod) < eps) { // Stops if close enough
  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
  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 purpose
The continue statement is less frequently used - like break, it skips the execution of the rest of the dependent block, so all statements in the block after the continue are not executed for the current iteration - however, it then reevaluates the condition and continues from the start of the dependent block if the condition is true: for (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;
  double prod = a * x;
  if (fabs(1.0 - prod) < eps) { // Stops if close enough
  x *= (2.0 - prod); // Heron approximation
max_iterations ... ?
In these examples, we use a standard macro fabs, which comes with the tgmath.h header (“tgmath” stands for type generic mathematical functions) - it calculates the absolute value of a double - listing 3.1 is a complete program that implements the same algorithm, where fabs has been replaced by several explicit comparisons against certain fixed numbers: for example, eps1m24 defined to be 1 − 2−24, or eps1p24 as 1 + 2−24 - we will see later (section 5.3) how the constants 0x1P-24 and similar used in these definitions work
In the first phase, the product of the current number under investigation a with the current estimate x is compared to 1.5 and 0.5, and then x is multiplied by 0.5 or 2 until the product is close to 1 --- then, the Heron approximation as shown in the code is used in a second iteration to close in and to compute the multiplicative inverse with high accuracy
The overall task of the program is to compute the inverse of all numbers that are provided to it on the command line - an example of a program execution looks like this - terminal: > ./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>
#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 {
    for (;;) {  // Heron approximation
      double prod = a * x;
      if ((prod < eps1m24) || (eps1p24 < prod)) {
        x *= (2.0 - prod);
      } else {
    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
[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: ./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]) {
  for (int i = 1; i < argc; ++i) {
    double const a = strtod(argv[i], 0);
[Exc 6] Print out the values of eps1m01, and observe the output when you change them slightly - done successfully
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') {
  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");
We have a more complex choice than a false-true decision, and that can have several outcomes - we can simplify this as follows: switch (arg) {
  case 'm': puts ("this is a magpie");
  case 'r': puts ("this is a raven");
  case 'j': puts ("this is a jay");
  case 'c': puts ("this is a chough");
  default:  puts ("this is an unknown corvid");
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
[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) {
  default: puts ("++++ ..... +++");
  case 4: puts ("++++");
  case 3: puts ("+++");
  case 2: puts ("++");
  case 1: puts ("+");
  case 0:;
exc7.1_triangle.c (replace all ♡ symbols in this code with ♡) - test: exc7.1_triangle.c - have fun +♡+♡+♡+
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 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 - case labels must not jump beyond a variable definition

CHALLENGE 2 (Numerical derivatives) (Derivate, Ableitungen, Abbildung Mathematik) - something we’ll deal with a lot is the concept of numerical algorithms - to get your hands dirty, see if you can implement the numerical derivative double f(double x) of a function double F(double x) - ?
Implement this with an example F for the function that you use for this exercise - good primary choice for F would be a function for which you know the derivative, such as sin, cos, or sqrt - this allows to check your results for correctness
CHALLENGE 3 (π) - compute the N first decimal places of π -> starting with the first 800 digits of pi ... to be continued :-) - chl3_pi.c - ?

Numerical values can be directly used as conditions for if statements; 0 represents “false,” and all other values are “true”
• There are 3 different iteration statements: for, do, and while - for is the preferred tool for domain iterations
• A switch statement performs multiple selection - one case runs into the next, if it is not terminated by a break

ch4 - Expressing computations

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 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

Table 4.2 - Object operators: the Form column gives the syntactic form of the operation, where @ represents the operator, o denotes an object, and a denotes a suitable additional value (if any) that serves as an operand --- an additional * in the Type column requires that the object o be addressable
p31 - Table 4.3 - Type operators: these operators return an integer constant (ICE) of type size_t - they have function-like syntax with the operands in parentheses
4.1.1 +, -, * --- The arithmetic operators +, -, and * mostly work as expected in sum, difference, product of 2 values (a and b) 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 ---
+ and - have unaray variants --- -b gives the negative of b: a value a such that b + a is 0 --- +a simply provides value of a --- following gives 76 as well: 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 expression
Takeaway - unsigned arithmetic is always well defined
Takeaway - the operations +, -, and * on size_t provide the mathematically correct result if it is representable as a size_t
When the result is not in that range and thus is not representable as a size_t value, we speak of arithmetic overflow - overflow can happen, for example, if we multiply two values that are so large that their mathematical product is greater than SIZE_MAX - we’ll look how C deals with overflow in the next section
4.1.2. Division and remainder - the operators / and % are a bit more complicated, because they correspond to integer division and the remainder operation - you might not be as used to them as you are to the other three arithmetic operators --- a/b evaluates to the number of times b fits into a, and a%b is the remaining value once the maximum number of b s are removed from a --- the operators / and % come in pairs: if we have z = a / b, the remainder a % b can be computed as a - z*b:
Takeaway - For unsigned values, a == (a/b)*b + (a%b)
A familiar example for the % operator is the hours on a clock - say we have a 12-hour clock: 6 hours after 8:00 is 2:00 - most people are able to compute time differences on 12-hour or 24-hour clocks - this computation corresponds to a % 12: in our example,
p32 - (8 + 6) % 12 == 2 - [Exc 8] (see bottom of p32) - Another similar use for % is computation using minutes in an hour, of the form a % 60
There is only one value that is not allowed for these two operations: 0s --- divisions by zero is forbidden
Takeaway - Unsigned / and % are well defined only if the second operand is not 0
The % operator can also be used to explain additive and multiplicative arithmetic on unsigned types a bit better - as already mentioned, when an unsigned type is given a value outside its range, it is said to overflow - in that case, the result is reduced as if the % operator had been used - the resulting value “wraps around” the range of the type - in the case of size_t, the range is 0 to SIZE_MAX, and therefore
Takeaway - Arithmetic on size_t implicitly does the computation %(SIZE_MAX+1)
Takeaway - In the case of overflow, unsigned arithmetic wraps around
- this means for size_t values, SIZE_MAX + 1 is equal to 0, and 0 - 1 is equal to SIZE_MAX ?
This “wrapping around” is the magic that makes the - operators work for unsigned types - for example, the value -1 interpreted as a size_t is equal to SIZE_MAX; so adding -1 to a value a just evaluates to a + SIZE_MAX, which wraps around to (?) a + 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:
Takeaway - The result of unsigned / and % is always smaller (or equal to) than the operands
And thus
Takeaway - Unsigned / and % can’t overflow
4.2. Operators that modify objects - Another important operation that we have already seen is assignment: a = 42 --- as you can see from that example, this operator is not symmetric: it has a value on the right and an object on the left - in a freaky abuse of language, C jargon often refers to the right side as rvalue (right value) and to the object on the left as lvalue (left value) - we will try to avoid that vocabulary whenever we can: speaking of a value and an object is sufficient
C has other assignment operators - for any binary operator @, the five (+ - * / %) we have seen all have the syntax 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:00
p33 - The syntax of these operators is a bit picky - you aren’t allowed to have blanks between the different characters: for example, i + = 7 instead of i += 7 is a syntax error.
Takeaway - Operators must have all their characters directly attached to each other
We already have seen two other operators that modify objects: the increment operator ++ and the decrement operator --:
++i is equivalent to i += 1
--i is equivalent to i -= 1
All these assignment operators are real operators - they return a value (but not an object!): the value of the object after the modification - you could, if you were crazy enough, write something like a = 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 effects
Takeaway - Side effects in value expressions are evil
Takeaway - Never modify more than one object in a statement
For the increment and decrement operators, there are even two other forms: postfix increment and postfix decrement - they differ from the one we have seen, in the result they provide to the surrounding expression - the prefix versions of these operators (++a and --a) do the operation first and then return the result, much like the corresponding assignment operators (a += 1 and a -= 1); the postfix operations return the value before the operation and perform the modification of the object thereafter - for any of them, the effect on the variable is the same: the incremented or decremented value
All this shows that evaluation of expressions with side effects may be difficult to follow - don’t do it
4.3. Boolean context - Several operators yield (liefern) a value 0 or 1, depending on whether some condition is verified; see table 4.1 - they can be grouped in 2 categories: comparisons and logical evaluation
4.3.1. Comparison - In our examples, we already have seen the comparison operators ==, !=, <, and > - whereas the latter two perform strict comparisons between their operands, the operators <= and >= perform “less than or equal” and “greater than or equal” comparisons, respectively - all these operators can be used in control statements, as we have already seen, but they are actually more powerful than that
Takeaway - Comparison operators return the value false or true (0 and 1)
Remember that false and true are nothing more than fancy names for 0 and 1, respectively - so, they can be used in arithmetic or for array indexing - in the following code, c will always be 1, and d will be 1 if a and b are equal and 0 otherwise: size_t c = (a < b) + (a == b) + (a > b);
size_t d = (a <= b) + (a >= b) - 1;
4.3.1_comparison.c - works fine :-)
↧ ?
In the next example, the array element sign[false] will hold the number of values in largeA that are greater than or equal to 1.0 and sign[true] those that are strictly less:
p34 - 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]
Finally, there also is an identifier not_eq (couldn't find more about not_eq for C in the internet) that may be used as a replacement for != - this feature is rarely used - it dates back to the times where some characters were not properly present on all computer platforms - to be able to use it, you’d have to include the file iso646.h .
Got it here: code.woboq.org - copy paste to atom editor, saved as iso646.h and put into /usr/include - here my first try to use it: 4.3.2_iso646.h.c - result: element 1 is 1,   its square is 1
element 2 is 2,   its square is 4
↥ ?
4.3.2. Logic - Logic operators operate on values that are already supposed to represent a false or true value - if they do not, the rules described for conditional execution (Takeaway apply first - the operator ! (not) logically negates its operand, operator && (and) is logical and, and operator || (or) is logical or - the results of these operators are summarized in table 4.4
TABLE 4.4 - Logical operators
Logical AND (&&)
false && false: false
false && true: false
true && false: false
true && true: true

Logical OR (||)
false || false: false
false || true: true
true || false: true
true || true: true

Logical NOT (!)
!false: true
!true: false --- True and False for && logic and || Logic table - stackoverflow.com

Similar to the comparison operators,
Takeaway - Logic operators return the value false or true - again, remember that these values are nothing more than 0 and 1 and can thus be used as indices (Kennziffer):
↧ ? 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 - 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.
          [false]    [true]
The operators && and || have a particular property called short-circuit evaluation - this barbaric term denotes the fact that the evaluation of the second operand is omitted if it is not necessary for the result of the operation: // This never divides by 0.
if (b != 0 && (( a / b) > 1)) { // ?
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) {
↥ ?
4.4. The ternary or conditional operator - the ternary (ternär, dreiteilig, dreifach) operator is similar to an if statement, but it is an expression that returns the value of the chosen branch (Ast, Arm, Verzweigung ...): 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!
Similar to the operators && and ||, the second and third operand are evaluated only if they are really needed - the macro sqrt from tgmath.h computes the square root of a non-negative value - calling it with a negative value raises a domain error: # 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 sqrt
Complex arithmetic and the tools used for it require the header complex.h, which is indirectly included by tgmath.h - they will be introduced later, in section 5.7.7
In the previous example, we also see conditional compilation that is achieved with preprocessor directives - the #ifdef construct ensures that we hit the #error condition only if the macro __STDC_NO_COMPLEX__ is defined
4.5. Evaluation order - Of the operators so far, we have seen that &&, ||, and ?: condition the evaluation of some of their operands - this implies in particular that for these operators, there is an evaluation order for the operands: the first operand, since it is a condition for the remaining ones, is always evaluated first:
Takeaway - &&, ||, ?:, and , evaluate their first operand first.
The comma (,) is the only operator we haven’t introduced yet - it evaluates its operands in order, and the result is the value of the right operand - for example, (f(a), f(b)) first evaluates f(a) and then f(b); the result is the value of f(b) - be aware that the comma character plays other syntactical roles in C that do not use the same convention about evaluation - for example, the commas that separate initializations do not have the same properties as those that separate function arguments
The comma operator is rarely useful in clean code, and it is a trap for beginners: A[i, j] is not a two-dimensional index for matrix A, but results in A[j]
Takeaway - Don’t use the , operator
p36 - Other operators don’t have an evaluation restriction - for example, in an expression such as f(a)+g(b), there is no pre-established order specifying whether f(a) or g(b) is to be computed first - if either the function f or g works with side effects (for instance, if f modifies b behind the scenes), the outcome of the expression will depend on the chosen order
Takeaway - Most operators don’t sequence their operands
That order may depend on your compiler, on the particular version of that compiler, on compile-time options, or just on the code that surrounds the expression - don’t rely on any such particular sequencing: it will bite you
The same holds for function arguments - in something like printf("%g and % g\n", f(a), f(b)); we wouldn’t know which of the last two arguments is evaluated first
Takeaway - Function calls don’t sequence their argument expressions
The only reliable way not to depend on evaluation ordering of arithmetic expressions is to ban side effects:
Takeaway - Functions that are called inside expressions should not have side effects

Arithmetic operators do math - they operate on values
Assignment operators modify objects
Comparison operators compare values and return 0 or 1
Function calls and most operators evaluate their operands in a nonspecific order - only &&, ||, and ?: impose an ordering on the evaluation of their operands

p37 - CHALLENGE 4 (Union-Find) - The Union-Find problem deals with the representation of partitions over a base set - we will identify the elements of the base set using the numbers 0, 1, ... and will represent partitions with a forest data structure where each element knows has a “parent” that is another element inside the same partition - each set in such a partition is identified by a designated element called the root of the set
We want to perform two principal operations:
• A Find operation receives one element of the ground set and returns the root of the corresponding set
• A Uniona operation receives two elements and merges the two sets to which these elements belong into one
a) Can you implement a forest data structure in an index table of base type size_t called parent? Here, a value in the table SIZE_MAX would mean a position represents a root of one of the trees; another number represents position of the parent of the corresponding tree - one of the important features to start the implementation is an initialization function that makes parent the singleton partition: that is, the partition where each element is the root of its own private set
b) With this index table, can you implement a Find function that, for a given index, finds the root of its tree?
c) Can you implement a FindReplace function that changes all parent entries on a path to the root (including) to a specific value?
d) Can you implement a FindCompress function that changes all parent entries to the root that has been found? e) Can you implement a Union function that, for two given elements, combines their trees into one? Use FindCompress for one side and FindReplace for the other

(footnote) aC also has a concept called a union, which we will see later, and which is completely different than the operation we are currently talking about - because union is a keyword, we use capital letters to name the operations here

ch5 - Basic values and data

p38 - 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 - 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;
double y = 3.0;
x = (x * 1.5) - y;
printf("x is \%g\n", x);
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 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;
printf("x is 4.5\n");
or maybe printf("x is 4.5\n");
double x = 4.5;
because to use x at a later point, it is not relevant whether the assignment took place before or after the printf
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 - 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 - All values have a type that is statically determined
p41 - Takeaway - Possible operations on a value are determined by its type
Takeaway - 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 - 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 - 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 - (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;
/* Do something else without x in the meantime. */
x += 7;
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
p42 - may in many cases be done as if it were specified as either /* Do something without x. */
x += 12;
or x += 12;
/* Do something without x. */
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
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 - 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, 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

Takeaway - Before arithmetic, narrow integer types are promoted to signed int
Observe that among the narrow integer types, we have two prominent members: char and bool - the first is C’s type that handles printable characters for text, and the second holds truth values, false and true - as we said earlier, for C, even these are just some sort of numbers
The 12 remaining, unpromoted, types split nicely into the four classes
Takeaway - Each of the four classes of base types has three distinct unpromoted types
Contrary to what many people believe, the C standard doesn’t prescribe the precision of these 12 types: it only constrains (belegen?) them - they depend on a lot of factors that are implementation defined
One of the things the standard does prescribe is that the possible ranges of values for the signed types must include each other according to their rank:
[[[[[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:
p44 - [[[[[[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
The comparison of the ranges of signed and unsigned types is more difficult - obviously, an unsigned type can never include the negative values of a signed type - for the non-negative values, we have the following inclusion of the values of types with corresponding rank:
[[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 int
Because the interrelationship between integer types depends on the platform, choosing the “best” type for a given purpose in a portable way can be a tedious task - luckily, we can get some help from the compiler implementation, which provides us with typedef s such as size_t that represent certain features
Takeaway - Use size_t for sizes, cardinalities, or ordinal numbers
Remember that unsigned types are the most convenient types, since they are the only types that have an arithmetic that is defined consistently with mathematical properties: the modulo operation. They can’t raise signals on overflow and can be optimized best. They are described in more detail in subsection 5.7.1.
Takeaway - Use unsigned for small quantities that can’t be negative
If your program really needs values that may both be positive and negative but don’t have fractions, use a signed type (see subsection 5.7.5)
Takeaway - Use signed for small quantities that bear a sign
Takeaway - Use ptrdiff_t for large differences that bear a sign
If you want to do fractional computation with a value such as 0.5 or 3.77189E+89, use floating-point types (see subsection 5.7.7)
Takeaway - Use double for floating-point calculations
Takeaway - Use double complex for complex calculations
The C standard defines a lot of other types, among them other arithmetic types that model special use cases - Table 5.2 list(s) some of them - the second pair represents the types with maximal width that the platform supports - this is also the type in which the preprocessor does any of its arithmetic or comparison
The two types time_t and clock_t are used to handle times - they are semantic types, because the precision of the time computation can be different from platform to platform - the way to have a time in seconds that can be used in arithmetic is the function difftime: it computes the difference of two timestamps - clock_t values present the platform’s model of processor clock cycles, so the unit of time is usually much less than a second; CLOCKS_PER_SEC can be used to convert such values to seconds
p45 - TABLE 5.2. Some semantic arithmetic types for specialized use cases
5.3. Specifying values - We have already seen several ways in which numerical constants (literals) can be specified:
123 Decimal integer constant - The most natural choice for most of us.
077 Octal integer constant - This is specified by a sequence of digits, the first being 0 and the following between 0 and 7. For example, 077 has the value 63. This type of specification merely has historical value and is rarely used nowadays. Only one octal literal is commonly used: 0 itself.
0xFFFF Hexadecimal integer constant - This is specified by starting with 0x followed by a sequence of digits between 0, . . . , 9 and a . . . f. For example, 0xbeaf has the value 48815. The a .. f and x can also be written in capitals, 0XBEAF.
1.7E-13 Decimal floating-point constants - Quite familiar as the version that has a decimal point. But there is also the “scientific” notation with an exponent. In the general form, mEe is interpreted as m · 10e .
0x1.7aP-13 Hexadecimal floating-point constants - Usually used to describe floating-point values in a form that makes it easy to specify values that have exact representations. The general form 0XhPe is interpreted as h · 2e . Here, h is specified as an hexadecimal fraction. The exponent e is still specified as a decimal number.
’a’ Integer character constant - These are characters put between ' apostrophes, such as 'a' or '?'. These have values that are only implicitly fixed by the C standard. For example, 'a' corresponds to the integer code for the character a of the Latin alphabet.
Among character constants, the \ character has a special meaning. For example, we already have seen '\n' for the newline character.
"hello" String literals - They specify text, such as that needed for the printf and puts functions. Again, the \ character is special, as with character constants.11
All but the last are numerical constants: they specify numbers.12 String literals are an exception and can be used to specify text that is known at compile time. Integrating larger text into our code could be tedious, if we weren’t allowed to split string literals into chunks: puts("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.
12You may have observed that complex numbers are not included in this list. We will see how to specify them in subsection 5.3.1.

p46 - Takeaway - Consecutive string literals are concatenated.
The rules for numbers are a little bit more complicated.
Takeaway - Numerical literals are never negative.
That is, if we write something like -34 or -1.5E-23, the leading sign is not considered part of the number but is the negation operator applied to the number that comes after it. We will see shortly where this is important. Bizarre as this may sound, the minus sign in the exponent is considered to be part of a floating-point literal.
We have already seen (Takeaway that all literals must have not only a value but also a type. Don’t mix up the fact of a constant having a positive value with its type, which can be signed.
Takeaway - Decimal integer constants are signed.
This is an important feature: we’d probably expect the expression -1 to be a signed, negative value.
To determine the exact type for integer literals, we always have a first fit rule.
Takeaway - A decimal integer constant has the first of the three signed types that fits it.
This rule can have surprising effects. Suppose that on a platform, the minimal signed value is −215 = −32768 and the maximum value is 215 − 1 = 32767. The constant 32768 then doesn’t fit into signed and is thus signed long. As a consequence, the expression -32768 has type signed long. Thus the minimal value of the type signed on such a platform cannot be written as a literal constant.[Exs 13]
Takeaway - The same value can have different types.
Deducing the type of an octal or hexadecimal constant is a bit more complicated. These can also be of an unsigned type if the value doesn’t fit for a signed type. In the earlier example, the hexadecimal constant 0x7FFF has the value 32767 and thus is type signed. Other than for the decimal constant, the constant 0x8000 (value 32768 written in hexadecimal) then is an unsigned, and expression -0x8000 again is unsigned.[Exs 14]
Takeaway - Don’t use octal or hexadecimal constants to express negative values.
As a consequence, there is only one choice left for negative values.
Takeaway - Use decimal constants to express negative values.
Integer constants can be forced to be unsigned or to be a type with minimal width. This done by appending U, L, or LL to the literal. For example, 1U has value 1 and type unsigned, 1L is signed long, and 1ULL has the same value 1 but type unsigned long long.[Exs 15] Note, that we are representing C constants such as 1ULL in type-writer font and distinguish them from their mathematical value 1 which is in normal font.
[Exs 13]Show that if the minimal and maximal values for signed long long have similar properties, the smallest integer value for the platform can’t be written as a combination of one literal with a minus sign.
[Exs 14]Show that if the maximum unsigned is 216 − 1, then -0x8000 has value 32768, too.
[Exs 15]Show that the expressions -1U, -1UL, and -1ULL have the maximum values and type as the three nonpromoted unsigned types, respectively.

p47 - TABLE 5.3. Examples for constants and their types, under the supposition that signed and unsigned have the commonly used representation with 32 bits
A common error is to try to assign a hexadecimal constant to a signed with the expectation that it will represent a negative value. Consider a declaration such as int x = 0xFFFFFFFF. This is done under the assumption that the hexadecimal value has the same binary representation as the signed value −1. On most architectures with 32-bit signed, this will be true (but not on all of them); but then nothing guarantees that the effective value +4294967295 is converted to the value −1. Table 5.3 has some examples of interesting constants, their values and their types.
Remember that value 0 is important. It is so important that it has a lot of equivalent spellings: 0, 0x0, and '\0' are all the same value, a 0 of type signed int. 0 has no decimal integer spelling: 0.0 is a decimal spelling for the value 0 but is seen as a floating- point value with type double.
Takeaway - Different literals can have the same value.
For integers, this rule looks almost trivial, but for floating-point constants it is less obvious. Floating-point values are only an approximation of the value they present literally, because binary digits of the fractional part may be truncated or rounded.
Takeaway - The effective value of a decimal floating-point constant may be different from its literal value.
For example, on my machine, the constant 0.2 has the value 0.2000000000000000111, and as a consequence the constants 0.2 and 0.2000000000000000111 have the same value.
Hexadecimal floating-point constants have been designed because they better correspond to binary representations of floating-point values. In fact, on most modern architectures, such a constant (that does not have too many digits) will exactly correspond to the literal value. Unfortunately, these beasts are almost unreadable for mere humans. For example, consider the two constants 0x1.99999AP-3 and 0xC.CCCCCCCCCCCCCCDP-6. The first corresponds to 1.60000002384 ∗ 2−3 and the second to 12.8000000000000000002 ∗ 2−6 ; thus, expressed as decimal floating points, their values are approximatively 0.20000000298 and 0.200000000000000000003, respectively. So the two constants have values that are very close to each other, whereas their representation as hexadecimal floating-point constants seems to put them far apart.
Finally, floating-point constants can be followed by the letter f or F to denote a float or by l or L to denote a long double. Otherwise, they are of type double. Be aware that different types of constants generally lead to different values for the same literal. Here is a typical example:
p48 - TABLE 5.3.1.
float double long double
literal 0.2F 0.2 0.2L
value 0x1.99999AP-3F 0x1.999999999999AP-3 0xC.CCCCCCCCCCCCCCDP-6L

Takeaway - Literals have value, type, and binary representations.
(edge: <complex.h> <tgmath.h>)
5.3.1. Complex constants - Complex types are not necessarily supported by all C platforms. This fact can be checked by inspecting __STDC_NO_COMPLEX__. To have full support of complex types, the header complex.h should be included. If you use tgmath.h for mathematical functions, this is already done implicitly.
Unfortunately, C provides no literals to specify constants of a complex type. It only has several macros16 that may ease the manipulation of these types.
The first possibility to specify complex values is the macro CMPLX, which comprises two floating-point values, the real and imaginary parts, in one complex value. For example, CMPLX(0.5, 0.5) is a double complex value with the real and imaginary part of one-half. Analogously, there are CMPLXF for float complex and CMPLXL for long double complex.
Another, more convenient, possibility is provided by the macro I, which represents a constant value of type float complex such that I*I has the value −1. One-character macro names in uppercase are often used in programs for numbers that are fixed for the whole program. By itself, it is not a brilliant idea (the supply of one-character names is limited), but you should definitely leave I alone.
Takeaway - I is reserved for the imaginary unit.
I can be used to specify constants of complex types similar to the usual mathematical notation. For example, 0.5 + 0.5*I would be of type double complex and 0.5F + 0.5F*I of float complex. The compiler implicitly convertsC the result to the wider of the types if we mix, for example, float and double constants for real and imaginary parts.
CHALLENGE 5 (complex numbers) - Can you extend the derivative (Challenge 2) to the complex domain: that is, functions that receive and return double complex values?
5.4. Implicit conversions - As we have seen in the examples, the type of an operand has an influence on the type of an operator expression such as -1 or -1U: whereas the first is a signed int, the second is an unsigned int. The latter might be particularly surprising for beginners, because an unsigned int has no negative values and so the value of -1U is a large positive integer.
Takeaway - Unary - and + have the type of their promoted argument.
So, these operators are examples where the type usually does not change. In cases where they do change, we have to rely on C’s strategy to do implicit conversions: that is, to move a value with a specific type to one that has another, desired, type. Consider the following examples, again under the assumption that −2147483648 and 2147483647 are the minimal and maximal values of a signed int, respectively:
16We will only see in subsection 5.6.3 what macros really are. For now, just take them as names to which the compiler has associated some specific property
p49 - 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.
The next two conversions for c and d are problematic. As we have seen, 0x80000000 is of type unsigned int and does not fit into a signed int. So c receives a value that is implementation-defined, and we have to know what our platform has decided to do in such cases. It could just reuse the bit pattern of the value on the right or terminate the program. As for all implementation-defined features, which solution is chosen should be documented by your platform, but be aware that this can change with new versions of your compiler or may be switched by compiler arguments.
For the case of d, the situation is even more complicated: 0x80000000 has the value 2147483648, and we might expect that -0x80000000 is just −2147483648. But since effectively -0x80000000 is again 2147483648, the same problem arises as for c.[Exs 17]
Then, e is harmless, again. This is because we used a negated decimal literal -2147483648, which has type signed long and whose value effectively is −2147483648 (shown earlier). Since this value fits into a signed int, the conversion can be done with no problem.
The last example for g is ambiguous in its consequences. A value that is too large for an unsigned type is converted according to the modulus. Here in particular, if we assume that the maximum value for unsigned short is 216 − 1, the resulting value is 0. Whether or not such a “narrowing” conversion is the desired outcome is often difficult to tell.
Takeaway - Avoid narrowing conversions.
Takeaway - Don’t use narrow types in arithmetic.
The type rules become even more complicated for operators such as addition and multiplication that have two operands, because these then may have different types. Here are some examples of operations that involve floating-point types: 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.
The next two are problematic because INT_MAX, the maximal value for signed int, usually will not fit into a float or complex float. For example, on my machine, INT_MAX + 0.0F is the same as INT_MAX + 1.0F and has the value 2147483648. The last line shows that for an operation with double, this would work fine on most platforms. Nevertheless, on an existing or future platform where int is 64 bit, an analogous problem with the precision could occur.
Because there is no strict inclusion of value ranges for integer types, deducing the type of an operation that mixes signed and unsigned values can be nasty:
[Exs 17]Under the assumption that the maximum value for unsigned int is 0xFFFFFFFF, prove that -0x80000000 == 0x80000000.
p50 - -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.
The next two cases are unambiguous, but perhaps not what a naive programmer would expect. In fact, for both, all operands are converted to unsigned int. Thus both negated values are converted to large unsigned values, and the result of the comparison is false.
The last two comparisons are even more problematic. On platforms where UINT_MAX ≤ LONG_MAX, 0U is converted to 0L, and thus the first result is true. On other platforms with LONG_MAX < UINT_MAX, -1L is converted to -1U (that is, UINT_MAX), and thus the first comparison is false. Analogous observations hold for the second comparison of the last two, but be aware that there is a good chance the outcome of the two is not the same.
Examples like the last two comparisons can give rise to endless debates in favor of or against signed or unsigned types, respectively. But they show only one thing: that the semantics of mixing signed and unsigned operands is not always clear. There are cases where either possible choice of an implicit conversion is problematic.
Takeaway - Avoid operations with operands of different signedness.
Takeaway - Use unsigned types whenever you can.
Takeaway - Chose your arithmetic types such that implicit conversions are harmless.
5.5. Initializers - We have seen (subsection 2.3) that the initializer is an important part of an object definition. Initializers help us to guarantee that a program execution is always in a defined state: that whenever we access an object, it has a well-known value that determines the state of the abstract machine.
Takeaway - All variables should be initialized.
There are only few exception to that rule: variable-length arrays (VLA); see subsection 6.1.3, which don’t allow for an initializer, and code that must be highly optimized. The latter mainly occurs in situations that use pointers, so this is not yet relevant to us. For most code that we are able to write so far, a modern compiler will be able to trace the origin of a value to its last assignment or its initialization. Superfluous initializations or assignments will simply be optimized out.
For scalar types such as integers and floating points, an initializer just contains an expression that can be converted to that type. We have seen a lot of examples of that. Optionally, such an initializer expression may be surrounded with {}. Here are some examples: 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, };
A  double 7.8 
             [0]                 [1]                 [2]
B  double 15.6  double 7.0  double 33.0 
            [0]                 [1]                [2]                [3]
C  double 6.0  double 0.0  double 0.0  double 1.0 

As we have seen, arrays that have an incomplete typeC because there is no length specification are completed by the initializer to fully specify the length. Here, A has only one element, whereas C has four. For the first two initializers, the element to which the scalar initialization applies is deduced from the position of the scalar in the list: for example, B[1] is initialized to 7. Designated initializers as for C are by far preferable, since they make the code more robust against small changes in declarations.
Takeaway - Use designated initializers for all aggregate data types
If you don’t know how to initialize a variable of type T, the default initializerC T a = {0} will almost18 always do
Takeaway - {0} is a valid initializer for all object types that are not VLA
Several things ensure that this works. First, if we omit the designation (the .membername for struct [see subsection 6.3] or [n] for arrays [see section 6.1]) initialization is just done in declaration orderC : that is, the 0 in the default initializer designates the very first member that is declared, and all other members are then initialized by default to 0 as well. Then, the {} form of initializers for scalars ensures that {0} is also valid for them.
Maybe your compiler warns you about this: annoyingly, some compiler implementers don’t know about this special rule. It is explicitly designed as a catch-all initializer in the C standard, so this is one of the rare cases where I would switch off a compiler warning.
In initializers, we often have to specify values that have a particular meaning for the program.
5.6. Named constants - A common issue even in small programs is that they use special values for some purposes that are textually repeated all over. If for one reason or another this value changes, the program falls apart. Take an artificial setting as an example where we have arrays of strings,19 on which we would like to perform some operations: #include <stdlib.h>
#include <stdio.h>
// #include <stdbool.h>

int main(int argc, char* argv[argc+1]) {
  char const*const bird[3] = {
  char const*const pronoun [3] = {
  char const*const ordinal [3] = {
  // ...
  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]);


// 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.
19This uses a pointer, type char const*const, to refer to strings. We will see later how this particular technique works.

p52 - Here we use the constant 3 in several places, and with three different “meanings” that are not very correlated. For example, an addition to our set of corvids would require two separate code changes. In a real setting, there might be many more places in the code that depend on this particular value, and in a large code base this can be very tedious to maintain.
Takeaway - All constants with a particular meaning must be named
It is equally important to distinguish constants that are equal, but for which equality is just a coincidence.
Takeaway - All constants with different meanings must be distinguished
C has surprisingly little means to specify named constants, and its terminology even causes a lot of confusion about which constructs effectively lead to compile-time constants. So we first have to get the terminology straight (subsection 5.6.1) before we look into the only proper named constants that C provides: enumeration constants (subsection 5.6.2). The latter will help us to replace the different versions of 3 in our example with something more explanatory. A second, generic, mechanism complements this feature with simple text replacement: macros (subsection 5.6.3). Macros only lead to compile-time constants if their replacements are composed of literals of base types, as we have seen. If we want to provide something close to the concept of constants for more-complicated data types, we have to provide them as temporary objects (subsection 5.6.4).
5.6.1. Read-only objects - Don’t confuse the term constant, which has a very specific meaning in C, with objects that can’t be modified. For example, in the previous code, bird, pronoun, and ordinal are not constants according to our terminology; they are const-qualified objects. This qualifierC specifies that we don’t have the right to change this object. For bird, neither the array entries nor the actual strings can be modified, and your compiler should give you a diagnostic if you try to do so:
Takeaway - An object of const-qualified type is read-only
That doesn’t mean the compiler or run-time system may not perhaps change the value of such an object: other parts of the program may see that object without the qualification and change it. The fact that you cannot write the summary of your bank account directly (but only read it) doesn’t mean it will remain constant over time.
There is another family of read-only objects that unfortunately are not protected by their type from being modified: string literals.
Takeaway - String literals are read-only
p53 - If introduced today, the type of string literals would certainly be char const[], an array of const-qualified characters. Unfortunately, the const keyword was introduced to the C language much later than string literals, and therefore it remained as it is for backward compatibility.20
Arrays such as bird also use another technique to handle string literals. They use a pointerC type, char const*const, to “refer” to a string literal. A visualization of such an array looks like this:
                    [0]                           [1]                           [2]
bird  char const*const  char const*const  char const*const 
       "raven"                  "magpie"              "jay"

That is, the string literals themselves are not stored inside the array bird but in some other place, and bird only refers to those places. We will see much later, in subsections 6.2 and 11, how this mechanism works.
5.6.2. Enumerations - C has a simple mechanism to name small integers as we needed them in the example, called enumerationsC: 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.
Takeaway - Enumeration constants have either an explicit or a positional value.
As you might have guessed, positional values start from 0 onward, so in our example we have raven with value 0, magpie with 1, jay with 2, and corvid_num with 3. This last 3 is obviously the 3 we are interested in.
                 [magpie]                    [raven]                    [jay]
bird  char const*const  char const*const  char const*const 
       "raven"                  "magpie"              "jay"

Notice that this uses a different order for the array entries than before, and this is one of the advantages of the approach with enumerations: we do not have to manually track the order we used in the array. The ordering that is fixed in the enumeration type does that automatically.
Now, if we want to add another corvid, we just put it in the list, anywhere before corvid_num:
LISTING 5.1. An enumeratin type and related array of strings
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.
p54 - As for most other narrow types, there is not really much interest in declaring variables of an enumeration type; for indexing and arithmetic, they would be converted to a wider integer, anyhow. Even the enumeration constants themselves aren’t of the enumeration type: Takeaway Enumeration constants are of type signed int. So the interest really lies in the constants, not in the newly created type. We can thus name any signed int constant that we need, without even providing a tagC for the type name: 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.”
Takeaway An integer constant expression doesn’t evaluate any object.
So, principally, an ICE may consist of any operations with integer literals, enumeration constants, _Alignof and offsetof subexpressions, and eventually some sizeof subexpressions.21
Still, even when the value is an ICE, to be able to use it to define an enumeration constant, you have to ensure that the value fits into a signed.
5.6.3. Macros. Unfortunately, there is no other mechanism to declare constants of other types than signed int in the strict sense of the C language. Instead, C proposes another powerful mechanism that introduces textual replacement of the program code: macrosC. A macro is introduced by a preprocessorC #define: # 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:
(1) A starting # character that must be the first non-blank character on the line
(2) The keyword define
(3) An identifier that is to be declared, here M_PI
(4) The replacement text, here 3.14159265358979323846
(5) A terminating newline character
21We will handle the latter two concepts in subsections 12.7 and 12.1.
p55 - With this trick, we can declare textual replacement for constants of unsigned, size_t, and double. In fact, the implementation-imposed bound of size_t, SIZE_MAX, is defined, as well as many of the other system features we have already seen: EXIT_SUCCESS, false, true, not_eq, bool, complex . . . Here, in this book, such C standard macros are all printed in dark red.
The spelling of these examples from the C standard is not representative for the conventions that are generally used in a large majority of software projects. Most of them have quite restrictive rules such that macros visually stick out from their surroundings.
Takeaway Macro names are in all caps.
Only deviate from that rule if you have good reasons, in particular not before you reach level 3.
5.6.4. Compound literals. For types that don’t have literals that describe their constants, things get even more complicated. We have to use compound literalsC on the replacement side of the macro. Such a compound literal has the form (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.
Takeaway A compound literal defines an object.
Overall, this form of macro has some pitfalls:
• Compound literals aren’t suitable for ICE.
• For our purpose here ,to declare named constants, the type T should be const-qualifiedC. This ensures that the optimizer has a bit more slack to generate good binary code for such a macro replacement.
• There must be space between the macro name and the () of the compound literal, here indicated by the /**/ comment. Otherwise, this would be interpreted as the start of a definition of a function-like macro. We will see these much later.
• A backspace character \ at the very end of the line can be used to continue the macro definition to the next line.
• There must be no ; at the end of the macro definition. Remember, it is all just text replacement.
Takeaway Don’t hide a terminating semicolon inside a macro.
Also, for readability of macros, please pity the poor occasional reader of your code:
Takeaway Right-indent continuation markers for macros to the same column.
As you can see in the example, this helps to visualize the entire spread of the macro definition easily.
p56 - 5.7. Binary representions - the binary representation of a type is a model that describes the possible values for that type. It is not the same as the in-memory object representation that describes the more or less physical storage of values of a given type.
Takeaway The same value may have different binary representations.
5.7.1. Unsigned integers. We have seen that unsigned integer types are those arithmetic types for which the standard arithmetic operations have a nice, closed mathematical description. They are closed under arithmetic operations:
Takeaway Unsigned arithmetic wraps nicely.
( <limits.h> <stdint.h> )
In mathematical terms, they implement a ring, ZN , the set of integers modulo some number N . The values that are representable are 0, . . . , N − 1. The maximum value N − 1 completely determines such an unsigned integer type and is made available through a macro with terminating _MAX in the name. For the basic unsigned integer types, these are UINT_MAX, ULONG_MAX, and ULLONG_MAX , and they are provided through limits.h. As we have seen, the one for size_t is SIZE_MAX from stdint.h.
The binary representation for non-negative integer values is always exactly what the term indicates: such a number is represented by binary digits b0 , b1 , . . . , bp−1 called bitsC . Each of the bits has a value of 0 or 1. The value of such a number is computed as

(1)             \( \sum_{i=0}^{p-1} bi2i.\)

The value p in that binary representation is called the precisionC of the underlying type. Bit b0 is called the least-significant bitC , and LSB, bp−1 is the most-significant bitC (MSB).
Of the bits bi that are 1, the one with minimal index i is called the least-significant bit setC , and the one with the highest index is the most-significant bit setC . For example, for an unsigned type with p = 16, the value 240 would have b4 = 1, b5 = 1, b6 = 1, and b7 = 1. All other bits of the binary representation are 0, the least-significant bit set i is b4 , and the most-significant bit set is b7 . From (1), we see immediately that 2p is the first value that cannot be represented with the type. Thus N = 2p and
Takeaway The maximum value of any integer type is of the form 2p − 1.
Observe that for this discussion of the representation of non-negative values, we haven’t argued about the signedness of the type. These rules apply equally to signed and unsigned types. Only for unsigned types, we are lucky, and what we have said so far completely suffices to describe such an unsigned type.
Takeaway Arithmetic on an unsigned integer type is determined by its precision.
Finally, table 5.4 shows the bounds of some of the commonly used scalars throughout this book.
5.7.2. Bit sets and bitwise operators. This simple binary representation of unsigned types allows us to use them for another purpose that is not directly related to arithmetic: as bit sets. A bit set is a different interpretation of an unsigned value, where we assume that it represents a subset of the base set V = {0, . . . , p − 1} and where we take element i to be member of the set, if the bit bi is present.
There are three binary operators that operate on bit sets: |, &, and ^. They represent the set union A ∪ B, set intersection A ∩ B, and symmetric difference A∆B, respectively. For an example, let us choose A = 240, representing {4, 5, 6, 7}, and B = 287, the bit
p57 - Table 5.4 + Table 5.5 ...
set {0, 1, 2, 3, 4, 8}; see table 5.5. For the result of these operations, the total size of the base set, and thus the precision p, is not needed. As for the arithmetic operators, there are corresponding assignment operators &=, |=, and ^=, respectively.[Exs 22][Exs 23][Exs 24][Exs 25]
There is yet another operator that operates on the bits of the value: the complement operator ~. The complement ~A would have value 65295 and would correspond to the set {0, 1, 2, 3, 8, 9, 10, 11, 12, 13, 14, 15}. This bit complement always depends on the precision p of the type.[Exs 26][Exs 27]
All of these operators can be written with identifiers: bitor, bitand, xor, or_eq, and_eq, xor_eq, and compl if you include header iso646.h. ( <iso646.h> )
A typical usage of bit sets is for flags, variables that control certain settings of a program:
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);
[Exs 22]Show that A \ B can be computed by A - (A&B).
[Exs 23]Show that V + 1 is 0.
[Exs 24]Show that A^B is equivalent to (A - (A&B)) + (B - (A&B)) and A + B - 2*(A&B).
[Exs 25]Show that A|B is equivalent to A + B - (A&B).
[Exs 26]Show that ~B can be computed by V - B.
[Exs 27]Show that -B = ~B + 1.

p58 - Here the constants for each type of corvid are a power of two, and so they have exactly one bit set in their binary representation. Membership in a flock can then be handled through the operators: |= adds a corvid to flock, and & with one of the constants tests whether a particular corvid is present.
Observe the similarity between operators & and && or | and ||: if we see each of the bits bi of an unsigned as a truth value, & performs the ‘logical and of all bits of its arguments simultaneously. This is a nice analogy that should help you memorize the particular spelling of these operators. On the other hand, keep in mind that the operators || and && have short-circuit evaluation, so be sure to distinguish them clearly from the bit operators.
5.7.3. Shift operators. The next set of operators builds a bridge between interpretation of unsigned values as numbers and as bit sets. A left-shift operation << corresponds to the multiplication of the numerical value by the corresponding power of two. For example, for A = 240, the set {4, 5, 6, 7}, A << 2 is 240 · 22 = 240 · 4 = 960, which represents the set {6, 7, 8, 9}. Resulting bits that don’t fit into the binary representation for the type are simply omitted. In our example, A << 9 would correspond to set {13, 14, 15, 16} (and value 122880), but since there is no bit 16, the resulting set is {13, 14, 15}, value 57344.
Thus, for such a shift operation, the precision p is again important. Not only are bits that don’t fit are dropped, but it also restricts the possible values of the operand on the right:
Takeaway The second operand of a shift operation must be less than the precision.
There is an analogous right-shift operation >> that shifts the binary representation toward the less-significant bits. Analogously, this corresponds to an integer division by a power of two. Bits in positions less than or equal to the shift value are omitted for the result. Observe that for this operation, the precision of the type isn’t important.[Exs 28]
Again, there are also corresponding assignment operators <<= and >>=.
The primary use of the left-shift operator << is specifying powers of two. In our example, we can now replace the #define s: # 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)
This makes the example more robust against changes to the enumeration.
5.7.4. Boolean values. The Boolean data type in C is also considered an unsigned type. Remember that it has only values 0 and 1, so there are no negative values. For backward compatibility with ancient programs, the basic type is called _Bool. The name bool as well as the constants false and true only come through the inclusion of stdbool.h. Unless you have to maintain a really old code base, you should use the latter.
[Exs 28]Show that the bits that are “lost” in an operation x>>n correspond to the remainder x % (1ULL << n).
p59 - Treating bool as an unsigned type is a stretch of the concept. Assignment to a variable of that type doesn’t follow the modulus rule of Takeaway, but a special rule for Boolean values (Takeaway
You will probably rarely need bool variables. They are only useful if you want to ensure that the value is always reduced to false or true on assignment. Early versions of C didn’t have a Boolean type, and many experienced C programmers still don’t use it.
5.7.5. Signed integers. Signed types are a bit more complicated than unsigned types. A C implementation has to decide about two points:
• What happens on arithmetic overflow?
• How is the sign of a signed type represented?
Signed and unsigned types come in pairs according to their integer rank, with the notable two exceptions from table 5.1: char and bool. The binary representation of the signed type is constrained by the inclusion diagram that we have seen above.
Takeaway Positive values are represented independently from signedness.
Or, stated otherwise, a positive value with a signed type has the same representation as in the corresponding unsigned type. That is why the maximum value for any integer type can be expressed so easily (Takeaway signed types also have a precision, p, that determines the maximum value of the type.
The next thing the standard prescribes is that signed types have one additional bit, the sign bitC . If it is 0, we have a positive value; if it is 1, the value is negative. Unfortunately, there are different concepts of how such a sign bit can be used to obtain a negative number. C allows three different sign representationsC :
• Sign and magnitudeC
• Ones’ complementC
• Two’s complementC
The first two nowadays probably only have historical or exotic relevance: for sign and magnitude, the magnitude is taken as positive values, and the sign bit simply specifies that there is a minus sign. Ones’ complement takes the corresponding positive value and complements all bits. Both representations have the disadvantage that two values evaluate to 0: there is a positive and a negative 0.29
Commonly used on modern platforms is the two’s complement representation. It performs exactly the same arithmetic as we have seen for unsigned types, but the upper half of unsigned values (those with a high-order bit of 1) is interpreted as being negative. The following two functions are basically all that is needed to interpret unsigned values as signed values: 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’s
29Since these two have fallen completely out of use on modern architectures, efforts are underway to remove them from the next revision of the C standard.
[Exs 30]Prove that for unsigned arithmetic, A + ~A is the maximum value.
[Exs 31]Prove that for unsigned arithmetic, A + ~A is −1.
[Exs 32]Prove that for unsigned arithmetic, A + (~A + 1) == 0.
p60 - ...
... p61-64 ...

ch6 - Derived data types

p65 - 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 - will first describe arrays in a way that is consistent with C’s assumptions about abstract state machine
Takeaway - 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];
signed b[N];
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:
          [0]              [1]              [2]              [3]

         [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];
double (D[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:

     [0][0]           [0][N-1]
We also have seen how array elements are accessed and initialized, again with a pair of [ ] - in the previous example, a[0] is an object of double and can be used wherever we want to use, for example, a simple variable - as we have seen, C[0] is itself an array, so C[0][0], which is the same as (C[0])[0], is also an object of type double
Initializers can use designated initializers (also using [ ] notation) to pick the specific position to which an initialization applies - the example code in listing 5.1 contains such initializers - during development, designated initializers help to make our code robust against small changes in array sizes or positions
6.1.2. Array operations - arrays are really just objects of a different type than we have seen so far
Takeaway - An array in a condition evaluates to true
The truth of that comes from the array decay operation, which we will see later - another important property is that we can’t evaluate arrays like other objects
Takeaway - There are array objects but no array values
So arrays can’t be operands for the value operators in table 4.1, and there is no arithmetic declared on arrays (themselves)
Takeaway - Arrays can’t be compared
Arrays also can’t be on the value side of the object operators in table 4.2. Most of the object operators are likewise ruled out from having arrays as object operands, either because they assume arithmetic or because they have a second value operand that would have to be an array, too
Takeaway - Arrays can’t be assigned to
p67 -


ch7 - Functions

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) {
  /* 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));
A declaration of that function (without a definition) could look as follows: 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);
double fbar2 = fbar(2) / 2;
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.
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 - 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 ...

ch8 - C library functions

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

8.1.1. Headers The C library has a lot of functions, far more than we can handle in this book. A header file bundles interface descriptions for a number of features, mostly functions - the header files that we will discuss here provide features of the C library, but later we can create our own interfaces and collect them in headers (section 10).
On this level, we will discuss the functions from the C library that are necessary for basic programming with the elements of the language we have seen so far - we will complete this discussion on higher levels, when we discuss a range of concepts - Table 8.1 has an overview of the standard header files
8.1.2. Interfaces - Most interfaces in the C library are specified as functions, but implementations are free to chose to implement them as macros, where doing so is appropriate - compared to those we saw in subsection 5.6.3, this uses a second form of macros that are syntactically similar to functions, function-like macros: # 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 has already convinced you not to do that
p91 - TABLE 8.2. Error return strategies for C library functions Some functions may also indicate a specific error condition through the value of the errno macro
Some of the interfaces we will look at have arguments or return values that are pointers - we can’t handle these completely yet, but in most cases we can get away with passing in known pointers or 0 for pointer arguments - pointers as return values will only occur in situations where they can be interpreted as an error condition
8.1.3. Error checking - C library functions usually indicate failure through a special return value - what value indicates the failure can be different and depends on the function itself. Generally, you have to look up the specific convention in the manual page for the functions. Table 8.2 gives a rough overview of the possibilities. There are three categories that apply: a special value that indicates an error, a special value that indicates success, and functions that return some sort of positive counter on success and a negative value on failure.
Typical error-checking code looks like the following: if (puts("hello world") == EOF) {
  perror ("can 't output to terminal:");
\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,
Takeaway - Failure is always an option
Takeaway - Check the return value of library functions for errors
An immediate failure of the program is often the best way to ensure that bugs are detected and get fixed early in development
Takeaway - Fail fast, fail early, and fail often
C has one major state variable that tracks errors of C library functions: a dinosaur called errno. The perror function uses this state under the hood, to provide its diagnostic. If a function fails in a way that allows us to recover, we have to ensure that the error state also is reset; otherwise, the library functions or error checking might get confused:

Level 2 - Cognition ch 9-14 p117-202

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

ch9 - Style

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 - 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 - 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 - 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 - 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 - 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 - 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 - 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 - 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:
• 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 - 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
TABLE 9.1. Some examples of well and badly distinguishable identifiers.

The difference between n and m on one side and for ffs and clz on the other is an implicit semantic.
For me, because I have a heavily biased mathematical background, single-letter variable names from i to n, such as n and m, are integer variables. These usually occur inside a quite restricted scope as loop variables or similar. Having a single-letter identifier is fine (we always have the declaration in view), and they are quickly distinguished.
The function names ffs and clz are different because they compete with all other three-letter acronyms that could potentially be used for function names. Accidentally, here, ffs is shorthand for find first (bit) set, but this is not immediately obvious to me. What that it would mean is even less clear: which bit is first, the most significant bit or the least significant?
There are several conventions that combine multiple words in one identifier. Among the most commonly used are the following:
• Camel caseC , using internalCapitalsToBreakWords
• Snake caseC , using internal_underscores_to_break_words
• Hungarian notationC ,1 which encodes type information in the prefix of the identifiers, such as szName, where sz stands for string and zero terminated
As you might imagine, none of these is ideal. The first two tend to obscure our view: they easily clog up a whole precious line of programming text with an unreadable expression:
1 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.
So, in my opinion, none of these rules or strategies have absolute values. I encourage you to take a pragmatic approach to the question.
Takeaway - Naming is a creative act.
It is not easily subsumed by simple technical rules.
Obviously, good naming is more important the more widely an identifier is used. So, it is particularly important for identifiers for which the declaration is generally out of view of the programmer: global names that constitute the API.
Takeaway - File-scope identifiers must be comprehensive.
What constitutes comprehensive here should be derived from the type of the identifier. Type names, constants, variables, and functions generally serve different purposes, so different strategies apply.
1Invented in Simonyi [1976], the PhD thesis of Simonyi Károly
p122 - Takeaway - A type name identifies a concept.
Examples of such concepts are time for struct timespec, size for size_t, a collection of corvidae for enum corvid, person for a data structure that collects data about people, list for a chained list of items, dictionary for a query data structure, and so on. If you have difficulty coming up with a concept for a data structure, an enumeration, or an arithmetic type, you should probably revisit your design.
Takeaway - A global constant identifies an artifact.
That is, a constant stands out for some reason from the other possible constants of the same type: it has a special meaning. It may have this meaning for some external reason beyond our control (M_PI for π), because the C standard says so (false, true), because of a restriction of the execution platform (SIZE_MAX), to be factual (corvid_num), for a reason that is culturally motivated (fortytwo), or as a design decision.
Generally, we will see shortly that file-scope variables (globals) are much frowned upon. Nevertheless, they are sometimes unavoidable, so we have to have an idea how to name them.
Takeaway - A global variable identifies state.
Typical names for such variables are toto_initialized to encode the fact that library toto has already been initialized, onError for a file-scope but internal variable that is set in a library that must be torn down, and visited_entries for a hash table that collects shared data.
Takeaway - A function or functional macro identifies an action.
Not all, but many, of the functions in the C standard library follow that rule and use verbs as a component of their names. Here are some examples:
• A standard function that compares two strings is strcmp.
• A standard macro that queries for a property is isless.
• A function that accesses a data member could be called toto_getFlag.
• The corresponding one that sets such a member would be toto_setFlag.
• A function that multiples two matrices is matrixMult.

• 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.

ch10 - Organization and documentation

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

ch11 - Pointers

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) {
  double tmp = *p0;
  *p0 = *p1;
  *p1 = tmp;
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
            <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 - 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 - 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 ... ...

ch12 - The C memory model

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 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 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 - ...


About -
Level 0 - 1 Start compile - 2 Statements -
Level 1 - 3 Control, iteration - 4 Arithmetic - 5 Basic values and Data - 6 Derived data types, Arrays, Pointers I, Structures - 7 Functions, Recursion - 8 Library -
Level 2 - 9 Style, formatting - 10 Organization Interface - 11 Pointers - 12 Memory - 13 Storage - 14 Processing and IO -
Level 3 - 15 Performance - 16 Macros - 17 Variations in control flow - 18 Threads - 19 Atomic Access and memory consistency -
Takeaways - Bibliography - Index

Terms in C

• 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 -



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"

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.

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 C

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:

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
#> -rw-r--r-- 1 root root (4 digits) (date) (time h:s) /etc/passwd // could be rwsr-xr-x
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'
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
drwxrwxr-x 3 username username      4096 Jan 4 2021 Steam
Read, write, execute and -
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:
//> a patch version of gvfs to workaround gedit / nautilus admin:// error for 64-bit Ubuntu 21.04 & Ubuntu 21.10.
Now everything works again!! Administator can open files and directories/folders - yeeeah :-)))

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
make --version # check installed version
#> GNU Make 4.3
#> Built for x86_64-pc-linux-gnu
Learn make in 60 seconds. - YT by Jacob Sorber - Make_(software) - WP
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
#> *-display UNCLAIMED // ?
#> description: VGA compatible controller
#> product: GM206 [GeForce GTX 950]
#> vendor: NVIDIA Corporation
Search: Ubuntu System, GUI: > Additional Drivers:
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 has failed because it couldn't communicate with the NVIDIA driver. Make sure that the latest NVIDIA driver is installed and running.
same message as above: #> NVIDIA-SMI has failed because it couldn't communicate with the NVIDIA driver. Make sure ... sudo apt install nvidia-prime
sudo prime-select nvidia
#> Error: no integrated GPU detected.
Nvidia-settings: Unable to find display on any available system - forums.developer.nvidia.com
nvidia-xconfig --enable-all-gpus --cool-bits=31 --allow-empty-initial-configuration
#> 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.
Nvidia-settings - Unable to load info from any available system - forums.developer.nvidia.com ps aux |grep X
#> 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
Ubuntu20.04 NVIDIA 470 driver is not loaded - forums.developer.nvidia.com

graphics.h install:
How i used graphics.h in Ubuntu 21.10? - askubuntu.com sudo apt-get install build-essential

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
#> old versions - not accepted:
#> 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

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

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

import "fmt"

func main() {
    fmt.Println("hello, world")
create a new folder "helloworld" - save file helloworld.go in this folder - via terminal move to this folder: cd helloworld - compile with go mod init example/helloworld
//> go: creating new go.mod: module example/helloworld
run with go 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
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)' \

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
• Atom editor: install atom-slime language-lisp lisp-paredit - read for more: Practical Common Lisp - gigamonkeys.com
How To Open ePub Books In Ubuntu Linux - itsfoss.com sudo apt-get install fbreaderCommon 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
[1]> (+ 3 (* 2 4))
p19 - REPL (read-eval-print loop) -
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)
> *foo*
> (defparameter *foo* 6)
> *foo*
> (defvar *foo* 5)
> *foo*
this book only defparameter - dynamic variable or special variable later - spaces and line breaks are completely ignored by Lisp: (        defparameter
     *small* 1)
p25 - this book not so much interested in indentation rules ... - [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

Common Lisp The Language

by Guy L. Steele Jr. - c 1984, 1989 - 30ch 1095p + Bibliography + Index p1131 end of book

ANSI Common Lisp

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
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
Resolve generated breaks, this may be caused by held packages
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


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:
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.
How do I disable UEFI Secure Boot? - support.avira.com - 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


• 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++

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
• ...

The all in one computer programming bible (CPB)

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
nmap --version //> Nmap version 7.80 ( nmap.org )
man nmap // manual nmap
nmap --help
How to Install and Use Nmap on Ubuntu - unixcop.com
ch10 - How To Keep Yourself Safe From Being Hacked

An Introduction to C & GUI Programming (CGP)

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 ...

p7 - Ch 1-26...

Ch1 - Getting started

Raspberry Pi

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 ♡♡♡