How does a C program actually work

2 program structures

The C language was originally intended exclusively for the implementation and expansion of the UNIX operating system, i.e. rather less as a widely used standard programming language. Originally, mind you! This is still evident from their properties, such as
  • Wealth of operators,
  • relative proximity to the machine,
  • possible high portability,
  • small language scope (only 32 keywords),
  • many syntactic possibilities in combination with simplified spellings.
The last point in particular leads to a high tolerance of the compiler towards careless errors. In this regard, C is also ideal when it comes to writing cryptic programs (and tripping yourself up with it).
Practical usability has improved significantly since the ANSI standard was set at the latest; the use of prototypes significantly increases type safety. Finally, as far as the cryptic programs are concerned, the developers themselves say:

General character: The very small scope of language is almost always insufficient (e.g. no I / O option), so routines from defined standard libraries are used.

In some cases, to describe the syntax of C, so-called Syntax diagrams used. These are graphic representations of the sequence of elements in language C. You can use the diagrams to check, so to speak, "with your index finger" whether your program syntax matches the language definition. Each diagram represents a production rule, which means nothing more than a permitted sequence of symbols. There are two forms of symbols:

  • Metalinguistic variable, i. H. a reference to another production rule. Such symbols are shown as a rectangle.
  • Terminal symbols. These symbols stand for themselves.

A program is syntactically correct if it can be represented by a sequence of terminal symbols (whereby the meta-variables are gradually broken down by terminal symbols).

Compiling and linking a C program

2.1 Basic elements of a C program

This chapter deals with some of the basic syntactic elements of the C programming language. As already said in the preface, many language elements of C will very soon be used without these having already been dealt with in the linear sequence of the script. The language constructs are then addressed in the lecture, but if you study at home, you will not be spared a look at the relevant reference points. Now let's turn to some basics:

Character set of C programs

Each C program consists of visible and other characters from a specific character set. The following visible characters are allowed in C:
LettersA B C D E F G H I J K L M O P Q R S T U V W X Y Z
a b c d e f g h i j k 1 m n o p q r s t u v w x y z
Digits0 1 2 3 4 5 6 7 8 9
Underscore _
special character! " # % & ' ( ) * + , - . / : ; < ==""> ? [ \ ] ^ { | } ~

In addition to the visible characters, C has other characters:

Space barSpace, space
warningBEL, bell, signal tone
BackspaceBS, step backwards
Form feedFF, jump to the next top of the page
NewlineNL, end of line, "line feed", line feed
returnCR, "Carriage Return", jump to the beginning of the current line
tabHT, horizontal tab

You can see that the national special characters, such as B. umlauts, do not belong to the character set of C. However, these can appear in literals (strings). The other characters of the character set in C presented above are implemented using escape sequences. This technique is presented below.

In C, names consist of letters, numbers and the underscore. They cannot begin with a number.


The space, line end, tabs, form feed and comments are separators. They separate the basic elements of language. Several of these separators in a row are regarded as one separator. If a backslash (\) is followed by an end-of-line character, a logical line is formed: The \ is a logical line

Format freedom

The program text is entered in C without line numbers and without format. Despite this freedom of format, you should design your programs in such a way that people can read and understand them. A certain convention, freely chosen by the author, can be recognized from the programs presented. Other writers have similar habits, so choose one.


The information on the program documentation is placed in a comment. This will be with /* initiated and with */ completed. A comment can span several lines.
This option is not only important for commenting, but also for pinpointing errors. Suspicious code with /* and */ circled and the fault possibly encircled.
Comments cannot be nested. This restriction is uncomfortable when you "comment out" program text; H. want to override. You have to be careful not to include comments by chance. Therefore, some compiler manufacturers also allow nested comments.
There are compilers that // as the beginning of the comment, the end of the comment is then the end of the line.

Compiler directive

With this instruction to the compiler, further files are included in the source text. These are usually so-called "header files" (see below). Example: #include This compiler directive adds the content of the file to the source code. The location of the header files in the computer's file system is determined by the compiler presettings. Usually the directory is also called "include". Another form of the include statement is: #include "myfile.h" The current directory is usually searched for here. You can also specify a file path for the header file.

Header files

The stdio.h file can be recognized by the ending ".h" as a header file. Header files are inserted at the head of the source text. The header file with the name stdio.h is read in by the compiler. Here you will find important information about the structure of functions that are called during the program run. These header files are pure text files; you can examine the content with an editor.
The compiler finds prototypes, preprocessor macros and symbolic constants in the header files. Prototypes are models for using functions. The preprocessor is started before the actual compilation process; it replaces macros in the text with another text. Frequently used constants such as B. EOF. The coding for the functions mentioned is located in libraries that are linked to the compiled program when it is linked. The compiler is reassured by the header files: Nothing is missing, the further information follows when linking (= combining several binary object files into one program).

The semicolon

In C, each statement must be preceded by a semicolon (;) completed become. In Pascal, for comparison, separates the semicolon is two statements.


There is no special main program in C (as in Pascal or other languages). For every C program is the main function. This is where the procedural part begins. Since is a function, arguments can be passed. A result can also be provided to the calling program. In this case, nothing is handed over and nothing is returned. Nothing can do in C too void to be named. Within the function there is the statement block, which is introduced with "{" and ended with "}". This is a general principle:

Statement blocks are grouped together using curly braces.

is only defined in the program, but not called. You will never find an instruction in a C program. has a special position: it is the function that is always carried out when the program is started.

If the program run is successful, the function returns the value zero. Some compilers require that a return value be specified explicitly at the end of. Then add the following instruction to the program after the last instruction, i.e. before the last curly bracket:


With this information, the operating system that started the program can check whether the called program was properly terminated. In the event of an error, a value not equal to zero is returned.

Standard libraries

The standard functions are provided by standard libraries. The following header files establish the connection to the standard libraries, whereby the following table is only an excerpt from all available header files:
LibraryAreas of responsibility
assert.hReview of conditions
ctype.hType conversions and type tests
errno.hHandling of system errors
float.hFloating point library
limits.hLimits on data types
locale.hManagement of the local structure
math.hmathematical functions
signal.hProcess control
stddef.hStandard constants
stdio.hStandard input and output
stdlib.hStandard library functions
string.hString processing functions
time.h Time management

When programming in C, you either use the standard functions offered above or your own functions, or you can obtain complete function libraries for all conceivable areas of application. This makes C so powerful that both operating systems and any other application can be programmed. A description of the libraries can be found in the appendix.


The key words of a programming language have a predefined meaning that cannot be changed. Here is an overview:

For C, the difference between upper and lower case is important.

The keywords must therefore be written exactly as they were presented. These keywords form the core of the C programming language.

Identifiers and names

In C (not in C ++) an object is a memory area that must consist of a contiguous sequence of one or more bytes. Objects are identified with identifiers. The following rules apply:
  • An identifier consists of a sequence of letters, numbers or the underscore (_); the first character cannot be a number.
  • An identifier can be of any length, but only a certain length is significant.
  • Keywords must not be used as identifiers.
Here, too, a distinction is made between upper and lower case. Names are identifiers of variables, functions and labels.

Escape sequences

In line

find the escape sequence \ nthat moves the cursor to the beginning of the next line (or starts a new line on the printer). Non-printable characters are inserted into the program text via such escape sequences, introduced by "\" (mostly for input and output or in strings). Here is an overview of some of the escape sequences:

characterEscape sequenceimportance
"\"quotation marks
?\?Question mark
BEL\ aBell
BS\ bBackspace
FF\ fForm feed
NL\ nNewline
CR\ rCarriage return
HT\ tHorizontal tab
VT\ vVertical tab

Introductory example

This is probably one of the most famous C programs: #include / * header file '... .h' * / int main (void) / * main program 'main ()' * / {/ * start. .. * / printf ("Hello world! \ n"); / * writes "..." to stdout * / return 0; / * everything okay ... * /} / * ... end of block * / Notes:
  • C distinguishes between upper and lower case letters (case sensitive);
  • Comments (or general characters that the compiler should ignore) are included in; this can also be done across lines.
    Attention: Comments must not be nested!
  • The program consists of expressions that are terminated by '' (so-called "statements" or "instructions");
#include int main (void) {int n, m; / * Declaration of the variables 'n' and 'm' as integer * / printf ("Enter 2 whole numbers:"); scanf ("% d% d", & n, & m); / * Read values ​​from stdin * / printf ("\ nSum of% d and% d:% d \ n", n, m, n + m); / * Result * / return 0; } Remarks:
  • The main program is always called; formally this is a function, therefore or;
  • should always return a value;
  • At the beginning of a block (block: specified by '{' and '}') the variables used here must be declared (unless this has already been done outside); this is done by specifying the data type, followed by the name of the variable.
  • It is common practice to indicate levels of brackets by indenting.
  • The function is used to output text and the content of variables on the screen (); this requires the specification of the desired format; the format specification has the form format.
  • The function is used to read in values; the above format specifications are essentially the same as for. In addition, the variable name must be preceded by a '' in the parameter list (the addresses of the variables are actually transferred).

You have already got to know an important control structure. As block denotes what is enclosed in a pair of brackets. Such a block combines the statements it contains into a so-called "compound statement"; this is syntactically equivalent to a single statement. Blocks have a remarkable independence: local variables can be declared within each block. Example:

int i = 3; {int i = 4; printf ("% d", i); / * output: 4 * /} printf ("% d", i); / * output: 3 * / Another example: Presentation of simple data types and their output with printf (); Use of the assignment and the arithmetic operators +, -, *, / and%. #include #include int main (void) {/ * Note: Within a pair of curly brackets * / / * always declare all local variables required first * / / * and then write the instructions . 3E4f; ff = -5.7E3f; printf ("float: f =% f, ff =% f, f * ff =% f \ n", f, ff, f * ff); / * Division of two double numbers (floating point division) * / d = 1.3E4; dd = -5.7E3; printf ("double: d =% lf, dd =% lf, d / dd =% lf \ n", d, dd, d / dd); / * Subtraction of two long double numbers * / q = 1.3E4l; qq = 1.299999999E41; printf ("long double: q =% Lf, qq =% Lf, q-qq =% Lf \ n", q, qq, q-qq); / * Result of a comparison is 0 or 1 as an int number * / c = 'A'; cc = 'B'; printf ("Comparison of% c and% c results in% d. \ n", c, cc, c == cc); / * Execute several assignments in one expression * / c = cc = 'X'; printf ("Two Ixe:% c% c \ n", c, cc); / * You can output floating point numbers in different formats. The 20 after the% fills up to 20 characters on the left with spaces. * / dd = 4E0 / 3.0; printf ("sliding->% 20lf% 20le% 20lg \ n", dd, dd, dd); dd = 4E9 / 3.0; printf ("comma->% 20lf% 20le% 20lg \ n", dd, dd, dd); dd = 4E20 / 3.0; printf ("formats>% 20lf% 20le% 20lg \ n", dd, dd, dd); / * Function main () and thus terminate the program * / return (0); }


Under a Expression one understands the Linking operands (Values, e.g. variables, constants) using Operators to a new value. An expression therefore always delivers one value. Compared to other programming languages ​​(e.g. Pascal), C has a significantly expanded expression and operator concept:
  • C owns very many operators, including many that do not appear in other languages ​​or are not regarded as operators there. There are unary, binary and one ternary operators in a Order of precedent are arranged (more in Chapter 4).
  • The assignment symbol "="is an operator that simple assignment operator (There are also compound assignment operators). The allocation is also an expression. Its value is the value assigned to the left side, e.g. a = 7 has the value 7.
  • Everyone with one ; Completed expression is a statement in C (expression statement). The assignment is also only confirmed by the final ";"to an instruction.
  • A single function call that looks like a procedure call also provides a final ";"- represents a printout instruction.

L-values ​​and R-values

Expressions have different meanings depending on whether they are to the left or right of the assignment operator.In the example a = b; the expression on the right stands for a value, while the expression on the left indicates the place where the value is to be stored. If we modify the example a little more, the difference becomes even clearer: a = a + 42; The name a, which is also a simple expression, is used here with different meanings. The right of the assignment operator means the value that is stored in the memory cell a, and the left is the address of the memory cell a in which the value of the total expression is to be stored on the right-hand side. From this position Left or right of the assignment operator were also the terms L value (L-Value) and R value (R-Value).
An expression represents one L value when it refers to a storage object. Such an expression can appear to the left and right of the assignment operator.

An expression that does not represent an L value represents one R value It may only appear to the right of the assignment operator. So you can't assign anything to an R value.

An expression that represents an L value can also appear to the right of the assignment operator, but then, as mentioned above, it has a different meaning. If there is an L value to the right of the assignment operator, its name or address is required in order to fetch the value of the variable from the corresponding memory location. This value is then assigned. To the left of the assignment operator there must always be an L value, because you need the name or address of a variable in order to store the assigned value in the corresponding memory location.
Furthermore, a distinction is made between modifiable and non-modifiable L-value. A non-modifiable L value is e.g. the name of an array. An address corresponds to the name. However, this is constant and cannot be modified. On the left side of an assignment there may only be a modifiable L-value, but no R-value or a non-modifiable L-value. Non-modifiable L-values ​​are present if the L-value is an array type, an incomplete type, a type provided with the type attribute, or a - or - type, one of its components of which has one with the Has attribute provided type.

Certain operators can only be applied to L values. The increment operator ++ or the address operator & can only be used on L values. 5 ++ is wrong, i ++ is correct (if i represents a variable). The content operator * can be applied to L and R values.

An L-value is therefore an expression that designates a data object. Besides the already discussed case of an L-value on the right side of an assignment, there are many other cases in which an L-value is converted into the value that is stored in the corresponding object. An L value that is not of an array type is always converted into the value that is stored in the corresponding object and is therefore no longer an L value, unless the object is:

  • Operand of the sizeof operator
  • Operand of address operator &
  • Operand of the increment operator + +
  • Operand of the decrement operator
  • the left operand of the point operator. with structures
  • the left operand of the assignment operator

2.2 Selection structures with if .. else

It is characterized by a non-linear sequence with a forward branch. The process reaches a decision point at which different processing paths are taken depending on a condition. The decision symbol indicates a condition (i.e. a conditional expression), the result of which is a truth value.

one-sided selection

This alternative structure only executes an instruction (sequence) on one of the two branch paths and ends in the merging of both paths.

two-sided selection

With this alternative structure, each branch path leads to its own instruction sequence. It also ends again in a merging of the paths. if (conditional expression) statement1; else statement2; Pascal programmers should note that there is a semicolon in front of the "else". Example 1: if (a> b) max = a; else max = b; Example 2: main () {int x, y, z; x = 3; z = 2; if (z! = 0) / * also: if (z) * / y = x / z; else printf ("Division by zero \ n"); printf ("y =% d \ n", y); } Important: when in doubt, always belongs to the last one. If necessary, clarify with what belongs where. Example: if (i == 1) if (j == 2) printf ("i = 1, j = 2. \ n"); else printf ("i = 1, j unknown. \ n"); else if (j == 2) printf ("i unknown, j = 2. \ n"); else printf ("i and j unknown. \ n"); Chains:. Example: if (i == 1) printf ("i is one. \ N"); else if (i == 2) printf ("i is two. \ n"); else if (i == 3) printf ("i is three. \ n"); else if (i == 4) printf ("i is four. \ n"); else printf ("i is a number. \ n"); Caution: don't!
Either there is only one instruction after the, followed by a semicolon, if there are several instructions, these are bracketed as a block and may be behind the brackets no Semicolon.

Example: simulating a pocket calculator

#include void main (void) {char operator; int value1, value2, erg; if (scanf ("% d% c% d", & value1, & operator, & value2) == 3) {if (operator == '+') erg = value1 + value2; else if (operator == '-') erg = value1- value2; else if (operator == '*') erg = value1 * value2; else if (operator == '/') erg = value1 / value2; else printf ("Wrong entry \ n"); printf ("% d% c% d =% d \ n", value1, operator, value2, erg); } else printf ("too few input values ​​\ n");

Attention! The conditions are only evaluated until the result is known. Example:

The character is only read if x> 0.
(EOF is a symbolic constant found in stdio.h is defined. At the end of the file it has the value -1.)

Example: In the EStG, Par. 32, the following procedure for calculating income tax is specified:
Income tax is in German marks

  1. for taxable income up to DM 4,752:
  2. for taxable income of DM 4,753 to DM 18,035:
  3. for taxable income of DM 18,036 to DM 80,027:
  4. for taxable income of DM 80 028 to DM 13 031:
  5. for taxable income of DM 130032 and more:
Where x is the rounded taxable income, y is one ten-thousandth of the portion of the rounded taxable income in excess of DM 17,982, and z is one ten-thousandth of the portion of the rounded taxable income in excess of DM 79974.
The taxable income must first be rounded off to the nearest amount divisible by 54 without a remainder, if it is not already divisible by 54 without a remainder.
We are looking for a C program that calculates and outputs the income tax to be paid for an income to be imported. #include #include int main (void) {double tax, income, y; scanf (data, "% lf", & income); if (income <0) printf ("income should be positive \ n"); else {/ * number must be rounded down * / income = floor (income / 54) * 54; if (income <4753) tax = 0; else if (income <18036) tax = 0.22 * income - 1045; else if (income <80028) {y = (income - 17982) /10000.0; tax = (((0.34 * y - 21.58) * y + 392) * y + 2200) * y + 2911; } else if (income <130032) {y = (income-79974) /10000.0; tax = (70 * y + 4900) * y + 26974; } else tax = 0.56 * income - 19561; } printf ("% 10g% 10g \ n", income, tax); }

2.3 Conditional expressions with the operator?:

The syntax of an expression with this operator looks like this:

An expression with the conditional operator cannot stand alone (like), but within an expression (e.g. a value assignment). If the result of the conditional expression is true (! = 0), expression1 is used, otherwise expression2.
Examples: Sign operator (number negative: vz = -1, number positive or zero: vz = +1) and maximum as above for "if ...":

2.4 Multiple selection with switch .. case

In this structure there are more than two selection paths that start from a branch and end in a merge. Here the query is not made for a condition, but the conditional expression returns a value. A branch is provided for each possible result value. If a path does not exist for every possible result value of the condition, an additional path must be provided for all cases not dealt with ("else" branch; "default"). switch (expression) {case W1: Instruction1; case W2: Instruction2; ...; case Wn: Instructionn; default: instructiondef; }

If the expression has the value Wi owns, the instruction (sequence) becomes instructioni and all of the following executed. If you want to avoid this, the respective branch must be closed with. It will look like that:

switch (expression) {case W1: Instruction1; break; case W2: Instruction2; break; ...; break; case Wn: Instructionn; break; default: instructiondef; } The two following flowcharts also make the difference visible:

Sample program:

/ * Example for switch: Reading in a number without using scanf * / #include #include int main (void) {/ * Local variables - sometimes with initial value * / unsigned long number = 0 ; enum boolean {false, true} end = false; int character; printf ("Please enter a number. \ n"); while (! end) {/ * read a character from the input * / character = getchar (); switch (character) / * or shorter: switch (character = getchar ()) * / {case '0': number = number * 10; break; case '1': number = number * 10 + 1; break; case '2': number = number * 10 + 2; break; case '3': number = number * 10 + 3; break; case '4': number = number * 10 + 4; break; case '5': number = number * 10 + 5; break; case '6': number = number * 10 + 6; break; case '7': number = number * 10 + 7; break; case '8': number = number * 10 + 8; break; case '9': number = number * 10 + 9; break; default: end = true; break; }} printf ("Number% lu read in, followed by '% c'.", number, character); return (0); } Second example. Calculation of the date difference between two days. #include #include #include void main () {int day1, mon1, year, day2, mon2, days, i; printf ("Number of days between two days \ n"); printf ("year:"); scanf ("% i", & year); printf ("1st date: day:"); scanf ("% i", & tag1); printf ("month:"); scanf ("% i", & mon1); printf ("\ n2. Date: Day:"); scanf ("% i", & tag2); printf ("month:"); scanf ("% i", & mon2); days = day2 - day1; for (i = mon1; i <= mon2-1; i ++) switch (i) {case 2: if (year% 4 == 0 && year% 100! = 0 || year% 400 == 0) days = days + 29; else days = days + 28; break; case 4: case 6: case 9: case 11: days = days + 30; break; case 1: case 3: case 5: case 7: case 8: case 10: case 12: days = days + 31; break; }; printf ("\ n There are% 3i days in between.", days); } A very simple calculator: #include #include main () {double op1, op2; char op [2]; while (scanf ("% lf% 1s% lf", & op1, op, & op2)! = EOF) {switch (op [0]) {case '+': op1 = op1 + op2; break; case '-': op1 = op1 - op2; break; case '*': op1 = op1 * op2; break; case '/': op1 = op1 / op2; break; default: printf ("illegal operator \ n"); continue; } printf ("=% g \ n", op1); }}

2.5 repetition structures with while

Repeat structures arise when a sequence of instructions is to be run through several times to solve a task (e.g. processing all components of a field). There is a non-linear course with backward branching. The programming of a repetition structure leads to a so-called "program loop". It is important to terminate the loop, i. H. At least one instruction must ensure that a variable is changed in such a way that the query condition is no longer met after a finite number of runs.

Repetitive repetition

In this case, the query is at the beginning of the loop (i.e. before the sequence of statements). If the condition is not fulfilled when entering the statement structure, the statement sequence is not executed at all.

Here are some examples of while loops, first calculating the greatest common divisor:

#include #include void main (void) {int i, j; i = 2 * 2 * 3 * 3 * 5 * 17; j = 2 * 2 * 3 * 5 * 5 * 13; / * ggt = 2 * 2 * 3 * 5 = 60 * / if (i <= 0 || j <= 0) {printf ("Invalid values ​​\ n"); } else {while (i! = j) {i = i% j; if (i == 0) i = j; j = j% i; if (j == 0) j = i; } printf ("ggT (i, j) =% d \ n", i); } The following program calculates square numbers between two limits: main () / * table of square numbers * / {int start, end, spanz; printf ("Calculating the square numbers of:"); scanf ("% d", & anf); printf ("Calculation of the square numbers up to:"); scanf ("% d", & ende); printf ("Number of columns for output:"); scanf ("% d", & spanz); putchar ('\ n'); while (anf <= end) {printf ("% 3d x% 3d =% 6d", anf, anf, anf * anf); if (anf% spanz) printf (""); else putchar ('\ n'); Anf = Anf + 1; } putchar ('\ n'); }

2.6 repetition structures with for

This structure of instructions represents the most universal form of repulsive repetition.
  • Expression1:
    The initial expression is executed once at the beginning before going through the loop; it can be used, for example, to preset a counter variable.
  • Expression2:
    This is followed by a rejecting repetition, analogous to the rules of the while loop, i.e. the conditional expression is evaluated and the statement section is only run through if the condition is fulfilled.
  • Expression3:
    Finally, the change expression is processed, e.g. For example, the counter variable can be incremented here, which is checked in the conditional expression at the beginning of the following loop run in order to abort the loop if necessary.
for (init; condition; increment) statement; corresponds to init; while (condition) {statement; increment; } A few examples of the use of the for statement:
Program code Result
for (i = 0; i <4; i ++) {for (j = 0; j <4; j ++) {printf ("*"); } printf ("\ n"); } **** **** **** ****
for (i = 1; i <= 7; i ++) {for (j = 1; j <= 4 - (abs (i-4)); j ++) {printf ("*"); } printf ("\ n"); } * ** *** **** *** ** *
for (i = 1; i <= 7; i ++) {for (j = 1; j <= abs (i-4); j ++) {printf (""); } for (j = 1; j <= 4 - (abs (i-4)); j ++) {printf ("*"); } printf ("\ n"); } * ** *** **** *** ** *
for (i = 1; i <= 7; i ++) {int num1 = 3 - abs (4-i), num2 = 5 - 2 * num1; for (j = 0; j 0) printf ("*"); printf ("\ n"); } * * * * * * * * * * * * *

The following complete program creates a multiplication table:

#include #include void main (void) {int i, j; printf ("* | 1 2 3 4 5 6 7 8 9 10 \ n"); printf ("--- + ---------------------------------------- \ n" ); for (i = 1; i <= 10; i ++) {printf ("% 2d |", i); for (j = 1; j <= 10; j ++) {printf ("% 3d", i * j); } printf ("\ n"); }}

2.7 Repetition structures with do .. while

In this case, in contrast to 2.5, the query is at the end of the loop (i.e. after the statement sequence). The sequence of statements is always executed at least once. This is why this statement structure is also called repetitive repetition.

Loop control at the end, calculation of 5 !:

#include #include void main (void) {int i, j; i = j = 1; do {j = j * i; i = i + 1; } while (i <= 5); printf ("5! =% d \ n", j); }

2.8 Unstructured control instructions

In C it is possible to use a brand to be marked. The label follows the naming conventions for variables. The mark ends with a colon.

This mark can be jumped to with the command.

There are also two other unstructured branch instructions:

cancels the processing of the current structured instruction (see).

executes a jump to the end of the loop and then continues with the next loop pass.

2.9 Assertions

The concept of assurances comes from the field of quality assurance and software verification.An assurance is used to ensure at a certain point in a program that a condition is always met (e.g. "Variable X is greater than zero at this point"). By formulating and checking these conditions, you can ensure that a program behaves correctly at a given point and is not in an indefinite or unexpected state. A distinction is generally made between three types of assurances:
  • Preconditions, i.e. tests that are carried out at the beginning of a program section. A classic example of preconditions are value checks for function parameters.
  • Postconditions that must always apply at the end of a program section.
  • Invariants, i.e. conditions that must always apply. An example of this would be "the counter variable j of the for loop must always be in the valid index area of ​​the array over which it is iterated". Testing whether such promises are correct should actually be unnecessary. Nevertheless, "eternal truths" should also be checked because
    • the program could be faulty compared to the design,
    • The prerequisites may shift after changes, extensions, corrections etc.
    • a reader is made aware of such knowledge.


At certain points in the program, the assurances are written down as a logical expression in the statement ("assurance"):

assert (expression); expression is a logical expression. ust defined in the include file. the expression is evaluated each time the statement is executed. The result can be:
:no action
:The program is aborted immediately, specifying the file name, line number and plain text of the violated assurance.

Example: Obviously incorrect source text fragment (instead of should be):

// process values ​​from 0 ... 9 for (int i = 0; i <= 10; i ++) {assert (i> = 0 && i <10); ... i process ...} Returns the message main.C: 8: failed assertion `i> = 0 && i <10 'All statements of a source text can be used switch off by compiler switch. The code is then translated as if no statement existed. Therefore, instructions are activated during development and testing of a program and deactivated before the software is shipped.

The use of assurances requires common sense (!). A happy medium has to be found between the trivial and the imponderable. The correct handling of individual language constructs can be assumed and does not have to be checked. Conditions that are linked to external influences (user input, file contents, ...) must be handled by the program logic; they cannot be handled with assertions.

In the following example, assert checks whether the stream is really not equal to NULL. If it is NULL, then it is definitely not valid, our expression returns a value of 0 (false) and the program is stopped. Write a small program that contains this function and pass a valid file pointer once and NULL once. Then try both again after inserting before the include statement for the line.

int WriteInFile (FILE * fd) {assert (fd! = NULL); fputs ("Hello World", fd); }

Assertions are used for conditions that come from the design logic. Appropriate points for assurances are:

  • Beginning and end of loop hulls;
  • Separation points between larger composite control structures;
  • Entry into program sections that only work under certain conditions;
  • Completion of error handling;

As already mentioned, statements are only evaluated if the checking of assertions is explicitly activated. Assertions are disabled by default. Due to this fact, the expressions in statements should not contain any side effects, otherwise the program behavior depends on whether assertions are activated or not, as the following example shows:

assert (i ++ 2.10 The C preprocessor Before a C source code is compiled, formal replacements can still be made. This is done with the so-called "preprocessor". Preprocessor statements begin with a. Since they are purely line-oriented, they are always at the beginning of the line, or the first and only in a line and become Not terminated by a semicolon. First of all, the following preprocessor commands are important:
  • to include files:
    • puts the named file from the standard library at this point (i.e. the search path depends on the implementation).
    • is used to integrate your own files.
    -Instructions can be nested, i.e. files can be included which themselves contain -instructions.
  • to define a macro:
    • Simple replacement; Example:

      causes all of them to be replaced from this statement onwards. You should never write constants explicitly in the source code, but always define them formally. It is convention (although not syntactically mandatory) that constants defined in this way are capitalized.
    • Macro replacement (functionsimilar); Example: #define CMULT (x) (3.8 * (x)) #define SQ (x) ((x) * (x)) #define MAX (a, b) ((a)> (b)? (A) : (b)) Advantage: Macro substitutions work for all types.
      Disadvantage: the likelihood of side effects is high; so is z. B. the excessive brackets in the above examples absolutely necessary. the definition would be done without brackets, e.g. B., then the call would not result, but as a text replacement. Furthermore, entire expressions may also be replaced and therefore developed just as often.
    • does not work within.
    • The statement can be undone with the command; Example:
  • : Conditional compilation. Example: #if MAX <1000 #define MIN 100 #else #define MIN 200 #endif Generally evaluates a constant integer expression; any comparison operator can be used instead of ''. You can also branch with if this token is defined, or with, if it is not defined, Example: #ifdef DEBUG printf ("a has the value here:% f \ n", a); #endif
  • You can also output your own preprocessor error messages, which also aborts the compilation process. Example: #ifndef AFLAG #error "AFLAG" not defined! #endif

You can also do nonsense with the preprocessor. For example, what does the following program do?

#include #define o scanf #define O printf #define ooh int #define OO "% d" #define Oho {#define oho} #define Oh main #define hohoho o ## 0 #define oh * # define ho (#define Ho) #define harhar; Oh ho Ho Oho ooh hohoho harhar o ho OO, & hohoho Ho harhar O ho OO, hohoho oh hohoho oh hohoho Ho harhar oho If you look at what the preprocessor makes of it, it becomes a bit clearer. The compiler call for this is: gcc -E program.c The result is, among other things: main () {int o0; scanf ("% d", & o0); printf ("% d", o0 * o0 * o0); } With a bit of reformatting you get: main () {int o0; scanf ("% d", & o0); printf ("% d", o0 * o0 * o0); } Obviously, this is a program that calculates and outputs the third power of an entered integer.
Copyright © Munich University of Applied Sciences, FB 04, Prof. Jürgen Plate