CBE Pt.8: Some Control Structures in C and Program Arguments
In C basically three types of control structures are provided:
-
Conditional statements (if, if .. else and switch statements)
-
Loop statements (for, while and do .. while statements)
-
Unconditional statements (return, break, continue and goto statements)
In the videos you see examples for some conditional statements (if and if ... else) and the loop statements.
The switch and goto statement will not be covered in this session (but they will show up in the grammar). For all other statements (including those that were not used in the video) you find examples below.
Example: Environment Variables
Let's connect C programming with some Unix shell workout.
The standard library provides the function getenv (declared in stdlib.h) to access the value of an exported shell variable. In this example it prints the value of the shell variables USER and FOO:
Portable Method
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | #include <stdio.h>
#include <stdlib.h>
// for: char *getenv(const char *name);
int
main(int argc, char *argv[])
{
// print all program arguments
for (int i = 0; i < argc; ++i) {
printf("argv[%d] = %s\n", i, argv[i]);
}
// print value of an environment variable
printf("Variable %s = %s\n", "USER", getenv("USER"));
printf("Variable %s = %s\n", "FOO", getenv("FOO"));
}
|
With echo $USER you can print the value of variable USER in the Unix shell. On Unix this an exported variable (and therefore every child process gets a copy of it) and it is already initialized with your login name. Before we run our program we also define a shell variable FOO with some value, but we do not export it:
theon$ echo $USER lehn theon$ FOO="This is foo" theon$ echo $FOO This is foo theon$ gcc -Wall -o getenv getenv.c theon$ getenv argv[0] = getenv Variable USER = lehn Variable FOO = (null) theon$
Now we almost do the same but also export the variable:
theon$ echo $USER lehn theon$ export FOO="This is foo" theon$ echo $FOO This is foo theon$ gcc -Wall -o getenv getenv.c theon$ getenv argv[0] = getenv Variable USER = lehn Variable FOO = This is foo theon$
You also can first define a variable without exporting it, and afterwards export it:
theon$ echo $USER lehn theon$ FOO="This is foo" theon$ export FOO theon$ echo $FOO This is foo theon$ gcc -Wall -o getenv getenv.c theon$ getenv argv[0] = getenv Variable USER = lehn Variable FOO = This is foo theon$
Some Not Portable Method to Access Environment Variables
Most implementations of the C library support that main can get a third parameter (here called envp) which is an array with pointers to a char. The size of the array is not provided through an additional parameter. Instead the end of the array is marked with a null pointer.
The C library used by gcc on theon does support this an this program prints all environment variables:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | #include <stdio.h>
extern int x[];
int
main(int argc, char *argv[], char *envp[])
{
for (int i = 0; i < argc; ++i) {
printf("argv[%d] = %s\n", i, argv[i]);
}
for (int i = 0; envp[i]; ++i) {
printf("envp[%d]: %s\n", i, envp[i]);
}
}
|
The program uses a loop to check first if envp[0] is not a null pointer. If that is the case it points to a definition of a variable (of form VARNAME=VALUE). It then continues with the next array element until a null pointer is found:
theon$ gcc -Wall -o envp envp.c theon$ envp argv[0] = envp envp[0]: SHELL=/usr/bin/bash envp[1]: REMOTE_HOST=46.223.15.8 envp[2]: SOURCEFILE_HEADER=/home/numerik/hpc/ss22/hpc0/SimpleHeader envp[3]: JS_FOLLOWSCROLL=/home/numerik/pub/hpc0/DocTool/Templates/tip_followscroll.js envp[4]: PKG_CONFIG_PATH=/opt/ulm/dublin/share/pkgconfig:/opt/ulm/dublin/lib/amd64/pkgconfig:/opt/ulm/dublin/lib/pkgconfig:/usr/lib/pkgconfig:/usr/share/pkgconfig envp[5]: HISTSIZE=8192 envp[6]: QMAILHOST=uni-ulm.de envp[7]: DOC_PROJECT=/home/numerik/hpc/ss22/hpc0 envp[8]: TTYRESET=2d02:1805:f00bf:8a3b:3:1c:7f:15:4:0:0:0:11:13:1a:19:12:f:17:16:0:0:1:1:0:00:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0 envp[9]: LC_MONETARY= envp[10]: FILETREE_HEADER=/home/numerik/pub/hpc0/DocTool/Templates/FiletreeHeader envp[11]: DB_DIR=/home/numerik/hpc/ss22/hpc0/db envp[12]: EDITOR=/opt/ulm/dublin/bin/vim envp[13]: QMAILUSER=michael.lehn envp[14]: _AST_FEATURES=UNIVERSE - att envp[15]: FILETREE_FOOTER=/home/numerik/pub/hpc0/DocTool/Templates/SimpleFooter envp[16]: PWD=/home/numerik/hpc/ss22/hpc0/session10/envp envp[17]: RSYNC_RSH=/usr/bin/ssh envp[18]: LOGNAME=lehn envp[19]: IMAGE_DIR=/home/www/htdocs/numerik/hpc/ss22/hpc0/Images envp[20]: QMAILNAME=Michael Lehn envp[21]: JS_SCRIPT=/home/numerik/pub/hpc0/DocTool/Templates/graph.js envp[22]: SLIDE_FOOTER=/home/numerik/pub/hpc0/DocTool/Templates/SimpleFooter envp[23]: LATEX_HEADER=/home/numerik/pub/hpc0/DocTool/Templates/LatexHeader.tex envp[24]: TEMPLATE_DIR=/home/numerik/pub/hpc0/DocTool/Templates envp[25]: HOME=/home/numerik/lehn envp[26]: LANG=en_US.UTF-8 envp[27]: SOURCEFILE_FOOTER=/home/numerik/pub/hpc0/DocTool/Templates/SimpleFooter envp[28]: CODE_DIR=/home/numerik/hpc/ss22/hpc0 envp[29]: VIMRUNTIME=/usr/local/share/vim/vim74 envp[30]: LOGINPID=24121 envp[31]: DO_PROFILE=1 envp[32]: TMPDIR=/tmp envp[33]: LC_TERMINAL=iTerm2 envp[34]: TMP_DIR=/home/numerik/hpc/ss22/hpc0/tmp envp[35]: SSH_CONNECTION=46.223.15.8 54103 134.60.66.7 22 envp[36]: VIM=vim envp[37]: INDEX_HEADER=/home/numerik/hpc/ss22/hpc0/IndexHeader envp[38]: TERM=xterm-256color envp[39]: SHELL_HOME_DIR=/home/numerik/hpc/ss22/hpc0 envp[40]: USER=lehn envp[41]: DOCSRC_DIR=/home/numerik/hpc/ss22/hpc0 envp[42]: ARCH=i86pc envp[43]: VISUAL=/opt/ulm/dublin/bin/vim envp[44]: LC_TERMINAL_VERSION=3.4.10 envp[45]: LATEX_FORMULA=/home/numerik/pub/hpc0/DocTool/Templates/LatexFormula.tex envp[46]: SHLVL=3 envp[47]: PAGER=/usr/bin/less envp[48]: DOCTOOL=/home/numerik/pub/hpc0/DocTool envp[49]: LC_MESSAGES= envp[50]: DEFAULT_PROJECT_NAME=/home/numerik/hpc/ss22/hpc0 envp[51]: QMAILINJECT=if envp[52]: LC_CTYPE=en_US.UTF-8 envp[53]: SLIDE_HEADER=/home/numerik/hpc/ss22/hpc0/SimpleHeader envp[54]: DEFAULT_CSS=/home/numerik/pub/hpc0/DocTool/Templates/default.css envp[55]: PS2=(#PROMPT2#) envp[56]: PS1=(#PROMPT1#) envp[57]: SSH_CLIENT=46.223.15.8 54103 22 envp[58]: HTML_DIR=/home/www/htdocs/numerik/hpc/ss22/hpc0 envp[59]: LC_TIME= envp[60]: LATEX_FOOTER=/home/numerik/pub/hpc0/DocTool/Templates/LatexFooter.tex envp[61]: JS_TOOLTIP=/home/numerik/pub/hpc0/DocTool/Templates/wz_tooltip.js envp[62]: LC_ALL= envp[63]: PERL_BADLANG= envp[64]: LC_COLLATE= envp[65]: AUTHOR_WEBSITE=https://www.uni-ulm.de/mawi/mawi-numerik/institut/mitarbeiter/mlehn/ envp[66]: GCC_COLORS=error=01;31:warning=01;35:note=01;36:caret=01;32:locus=01:quote=01 envp[67]: XDG_DATA_DIRS=/opt/ulm/dublin/share envp[68]: PATH=.:/home/numerik/lehn/bin:/opt/ulm/dublin/cmd:/opt/ulm/dublin/bin:/usr/bin:/usr/perl5/5.12/bin:/home/numerik/pub/hpc0/DocTool/Executables:/home/numerik/pub/bin/:/home/numerik/pub/bin envp[69]: LOG_DIR=/home/numerik/hpc/ss22/hpc0/log envp[70]: DOWNLOAD_DIR=/home/www/htdocs/numerik/hpc/ss22/hpc0/download envp[71]: JS_CENTERWINDOW=/home/numerik/pub/hpc0/DocTool/Templates/tip_centerwindow.js envp[72]: MAIL=/var/mail/lehn envp[73]: OSFONTDIR=/opt/ulm/dublin/share/fonts:/usr/share/fonts/TrueType envp[74]: SSH_TTY=/dev/pts/69 envp[75]: TTY=/dev/pts/69 envp[76]: AUTHOR=Michael C. Lehn envp[77]: CONFIG_SHELL=/usr/bin/ksh envp[78]: A__z="*SHLVL envp[79]: LC_NUMERIC= envp[80]: OLDPWD=/home/numerik/lehn envp[81]: _=./envp theon$
Food for Thought: Counting Backwards with a For Loop
Here is a perfectly fine program which uses a for-loop to print the integer numbers from n - 1 down to :
1 2 3 4 5 6 7 8 9 10 11 | #include <stdio.h>
int
main()
{
int n = 5;
for (int i = n - 1; i >= 0; --i) {
printf("i = %d\n", i);
}
}
|
theon$ make building configuration 'gcc' make -C src/ Config=gcc -f ../config/Makefile.template make[1]: Entering directory '/home/numerik/hpc/ss22/hpc0/session10/hpc0_cprog_page4/src' Updating dependencies for xcountdown.c gcc -o ../build_gcc/xcountdown xcountdown.c ../deps_gcc/xcountdown.d ../deps_gcc/xcounting.d ../deps_gcc/xdangling_else.d ../deps_gcc/xempty_stmts.d ../deps_gcc/xfor.d ../deps_gcc/xpress_return.d ../deps_gcc/xshadow.d ../deps_gcc/xshadow2.d ../deps_gcc/xwhile.d ../deps_gcc/xwhile1.d ../deps_gcc/xwhile2.d make[1]: Leaving directory '/home/numerik/hpc/ss22/hpc0/session10/hpc0_cprog_page4/src' building configuration 'ucc' make -C src/ Config=ucc -f ../config/Makefile.template make[1]: Entering directory '/home/numerik/hpc/ss22/hpc0/session10/hpc0_cprog_page4/src' Updating dependencies for xcountdown.c ucc -o ../build_ucc/xcountdown xcountdown.c /home/numerik/pub/ulmcc/lib/libulm.a ../deps_ucc/xcountdown.d ../deps_ucc/xcounting.d ../deps_ucc/xdangling_else.d ../deps_ucc/xempty_stmts.d ../deps_ucc/xfor.d ../deps_ucc/xpress_return.d ../deps_ucc/xshadow.d ../deps_ucc/xshadow2.d ../deps_ucc/xwhile.d ../deps_ucc/xwhile1.d ../deps_ucc/xwhile2.d make[1]: Leaving directory '/home/numerik/hpc/ss22/hpc0/session10/hpc0_cprog_page4/src' theon$
theon$ ./build_ucc/xcountdown i = 4 i = 3 i = 2 i = 1 i = 0 theon$ ./build_gcc/xcountdown i = 4 i = 3 i = 2 i = 1 i = 0 theon$
So what is the problem? For some reason you are supposed to used an unsigned integer type (e.g. unsigned) for the variables i and n. Well, this does not seem to be too hard. You simple change the code into the following:
1 2 3 4 5 6 7 8 9 10 11 | #include <stdio.h>
int
main()
{
unsigned n = 5;
for (unsigned i = n - 1; i >= 0; --i) {
printf("i = %d\n", i);
}
}
|
theon$ make building configuration 'gcc' make -C src/ Config=gcc -f ../config/Makefile.template make[1]: Entering directory '/home/numerik/hpc/ss22/hpc0/session10/hpc0_cprog_page4/src' Updating dependencies for xcountdown_unsigned.c gcc -o ../build_gcc/xcountdown_unsigned xcountdown_unsigned.c ../deps_gcc/xcountdown_unsigned.d ../deps_gcc/xcountdown.d ../deps_gcc/xcounting.d ../deps_gcc/xdangling_else.d ../deps_gcc/xempty_stmts.d ../deps_gcc/xfor.d ../deps_gcc/xpress_return.d ../deps_gcc/xshadow.d ../deps_gcc/xshadow2.d ../deps_gcc/xwhile.d ../deps_gcc/xwhile1.d ../deps_gcc/xwhile2.d make[1]: Leaving directory '/home/numerik/hpc/ss22/hpc0/session10/hpc0_cprog_page4/src' building configuration 'ucc' make -C src/ Config=ucc -f ../config/Makefile.template make[1]: Entering directory '/home/numerik/hpc/ss22/hpc0/session10/hpc0_cprog_page4/src' Updating dependencies for xcountdown_unsigned.c ucc -o ../build_ucc/xcountdown_unsigned xcountdown_unsigned.c /home/numerik/pub/ulmcc/lib/libulm.a ../deps_ucc/xcountdown_unsigned.d ../deps_ucc/xcountdown.d ../deps_ucc/xcounting.d ../deps_ucc/xdangling_else.d ../deps_ucc/xempty_stmts.d ../deps_ucc/xfor.d ../deps_ucc/xpress_return.d ../deps_ucc/xshadow.d ../deps_ucc/xshadow2.d ../deps_ucc/xwhile.d ../deps_ucc/xwhile1.d ../deps_ucc/xwhile2.d make[1]: Leaving directory '/home/numerik/hpc/ss22/hpc0/session10/hpc0_cprog_page4/src' theon$
There actually seems to be no problem. Until you run the program ... you got stuck in an infinite loop.
-
Run the program
-
You can kill the process with Control-c
-
Explain why the condition i >= 0 is always true. For example, explain how ucc checks a condition of the form a >= b in general if a and b are unsigned integers. Write your answer in a file that_s_why.txt.
-
Fix the problem. Feel free to search the web. At least on stackoverflow you can find an answer with a typical pattern for dealing with this case. Also some pattern I don't like that much. But of course whatever working pattern you come up with is fine. It's just important that you are aware of the problem and know how to deal with it.
Some hint about where this might be needed: In numerical linear algebra it is a common problem to solve a system of linear equations where the coefficient matrix is upper triangular. The method for solving this problem is called back substitution. The algorithm for that is actually quite simple. And for implementing it in C you just need a loop that counts from n-1 down to . And because the integers refer here to matrix or vector indices that can not be negative it perfectly makes sense that this should work for unsigned integers.
We will discuss the problem in class and I will show my pattern for dealing with this problem.
Overview: Statements in C
Currently the ULM compiler does not support all kind of statements:
\[\begin{array}{rcl} \langle\text{statement}\rangle & \to & \langle\text{expression-statement}\rangle\; \textbf{;}\; \\ & \to & \langle\text{compound-statement}\rangle \\ & \to & \langle\text{if-statement}\rangle \\ & \to & \langle\text{while-loop}\rangle \\ & \to & \langle\text{do-while-loop}\rangle \\ & \to & \langle\text{for-loop}\rangle \\ & \to & \langle\text{break-statement}\rangle\; \\ & \to & \langle\text{continue-statement}\rangle\; \\ & \to & \langle\text{return-statement}\rangle\; \\ & \to & \langle\text{switch-statement}\rangle_\text{not supported by ucc}\; \textbf{;}\; \\ & \to & \langle\text{goto-statement}\rangle_\text{not supported by ucc}\; \textbf{;}\; \\ & \to & \langle\text{labeled-statement}\rangle_\text{not supported by ucc}\; \textbf{;}\; \\\end{array}\]expression-statement
The simplest kind of statement was pretty much covered Session 10 (Expressions in C). But maybe it suprises you that the expression is actualy optional:
\[\begin{array}{rcl}\langle\text{expression-statement}\rangle & \to & \langle\text{expression}\rangle_\text{opt}\; \textbf{;}\; \\\end{array}\]So this is perfectly legal code with lots of empty statements used for some kind of ASCII art in the source code:
1 2 3 4 5 6 7 8 9 | #include <stdio.h>
int
main()
{
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; puts("hello, world!") ;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
}
|
theon$ make building configuration 'gcc' make -C src/ Config=gcc -f ../config/Makefile.template make[1]: Entering directory '/home/numerik/hpc/ss22/hpc0/session10/hpc0_cprog_page4/src' mkdir -p ../deps_gcc Updating dependencies for xempty_stmts.c mkdir -p ../build_gcc gcc -o ../build_gcc/xempty_stmts xempty_stmts.c ../deps_gcc/xempty_stmts.d make[1]: Leaving directory '/home/numerik/hpc/ss22/hpc0/session10/hpc0_cprog_page4/src' building configuration 'ucc' make -C src/ Config=ucc -f ../config/Makefile.template make[1]: Entering directory '/home/numerik/hpc/ss22/hpc0/session10/hpc0_cprog_page4/src' mkdir -p ../deps_ucc Updating dependencies for xempty_stmts.c mkdir -p ../build_ucc ucc -o ../build_ucc/xempty_stmts xempty_stmts.c /home/numerik/pub/ulmcc/lib/libulm.a ../deps_ucc/xempty_stmts.d make[1]: Leaving directory '/home/numerik/hpc/ss22/hpc0/session10/hpc0_cprog_page4/src' theon$
theon$ ./build_ucc/xempty_stmts hello, world! theon$ ./build_gcc/xempty_stmts hello, world! theon$
A more useful application can be empty statements in a loop (see below). For example:
1 2 3 4 5 6 7 8 9 | #include <stdio.h>
int
main()
{
puts("Press 'enter' to continue");
while (getchar() != '\n');
puts("So you found 'enter' on your keyboard");
}
|
theon$ make building configuration 'gcc' make -C src/ Config=gcc -f ../config/Makefile.template make[1]: Entering directory '/home/numerik/hpc/ss22/hpc0/session10/hpc0_cprog_page4/src' Updating dependencies for xpress_return.c gcc -o ../build_gcc/xpress_return xpress_return.c ../deps_gcc/xempty_stmts.d ../deps_gcc/xpress_return.d make[1]: Leaving directory '/home/numerik/hpc/ss22/hpc0/session10/hpc0_cprog_page4/src' building configuration 'ucc' make -C src/ Config=ucc -f ../config/Makefile.template make[1]: Entering directory '/home/numerik/hpc/ss22/hpc0/session10/hpc0_cprog_page4/src' Updating dependencies for xpress_return.c ucc -o ../build_ucc/xpress_return xpress_return.c /home/numerik/pub/ulmcc/lib/libulm.a ../deps_ucc/xempty_stmts.d ../deps_ucc/xpress_return.d make[1]: Leaving directory '/home/numerik/hpc/ss22/hpc0/session10/hpc0_cprog_page4/src' theon$
Try for yourself.
And of course it is always fun to write some confusing code:
1 2 3 4 5 6 7 | #include <stdio.h>
int
main()
{
for (int i = 0; printf("i = %d\n", i), i < 10; ++i);
}
|
Here the eductional reason for showing this is actually the conditional expression of the for-loop
1 | printf("i = %d\n", i), i < 1
|
The value of an expression list is the value of the last expression item, i.e. i < 1. So it does not matter that the function call expression printf(...) does not have an integer type. This expression list evaluates to i < 10 but as a side-effect also printf gets called. So we have a proper for-loop.
theon$ make building configuration 'gcc' make -C src/ Config=gcc -f ../config/Makefile.template make[1]: Entering directory '/home/numerik/hpc/ss22/hpc0/session10/hpc0_cprog_page4/src' Updating dependencies for xcounting.c gcc -o ../build_gcc/xcounting xcounting.c ../deps_gcc/xcounting.d ../deps_gcc/xempty_stmts.d ../deps_gcc/xpress_return.d make[1]: Leaving directory '/home/numerik/hpc/ss22/hpc0/session10/hpc0_cprog_page4/src' building configuration 'ucc' make -C src/ Config=ucc -f ../config/Makefile.template make[1]: Entering directory '/home/numerik/hpc/ss22/hpc0/session10/hpc0_cprog_page4/src' Updating dependencies for xcounting.c ucc -o ../build_ucc/xcounting xcounting.c /home/numerik/pub/ulmcc/lib/libulm.a ../deps_ucc/xcounting.d ../deps_ucc/xempty_stmts.d ../deps_ucc/xpress_return.d make[1]: Leaving directory '/home/numerik/hpc/ss22/hpc0/session10/hpc0_cprog_page4/src' theon$
theon$ ./build_ucc/xcounting i = 0 i = 1 i = 2 i = 3 i = 4 i = 5 i = 6 i = 7 i = 8 i = 9 i = 10 theon$ ./build_gcc/xcounting i = 0 i = 1 i = 2 i = 3 i = 4 i = 5 i = 6 i = 7 i = 8 i = 9 i = 10 theon$
compound-statement
Compund statements begin with a open parenthesis constain some sequence of declarations and statements and end with a closed parenthesis:
\[\begin{array}{rcl}\langle\text{compound-statement} & \to & \textbf{\{}\; \langle\text{declaration-or-statement-list} \rangle_\text{opt} \; \textbf{\}} \\\langle\text{declaration-or-statement-list}\rangle & \to & \langle\text{declaration-or-statement}\rangle \\ & \to & \langle\text{declaration-or-statement}\rangle\; \langle\text{declaration-or-statement-list}\rangle \\\langle\text{declaration-or-statement} & \to & \langle\text{declaration}\rangle\; \\ & \to & \langle\text{statement}\rangle \\\end{array}\]Every function definition has at least one compound statement. If you declare a variable inside a compound statement it is a local variable (unless it has a static qualifier, but more about that later).
Scope or visibility of variables declared in a compound statement
The scope (or visibility) of a variable declared in a compound statement is limited to this block. So in this example the compiler would complain in the line with the second printf that no variable i was declared:
1 2 3 4 5 6 7 8 9 10 11 | #include <stdio.h>
int
main()
{
{
int i = 42; // only visible in this compund statement
printf("i = %d\n", i); // ok, would print 'i = 42'
}
printf("i = %d\n", i);
}
|
Shadowing previous declarations
It is legal and in many cases helpful that you can use identifiers in a declaration within a compound statement that was already used before in a declaration. In this case the previous declaration gets shadowed:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | #include <stdio.h>
int i = 42; // global variable i
int
main()
{
int i = 43; // new local variable i
printf("1) i = %d\n", i); // prints 'i = 43' not 'i = 42'
{
int i = 44; // new local variable i
printf("2) i = %d\n", i); // prints 'i = 44' not 'i = 43'
}
}
|
Once a previous declaration is shadowed there is no way back to refer to the previous declaration (in other languages you sometimes can refer to a global scoped variable).
theon$ make building configuration 'gcc' make -C src/ Config=gcc -f ../config/Makefile.template make[1]: Entering directory '/home/numerik/hpc/ss22/hpc0/session10/hpc0_cprog_page4/src' Updating dependencies for xshadow.c gcc -o ../build_gcc/xshadow xshadow.c ../deps_gcc/xcounting.d ../deps_gcc/xempty_stmts.d ../deps_gcc/xpress_return.d ../deps_gcc/xshadow.d make[1]: Leaving directory '/home/numerik/hpc/ss22/hpc0/session10/hpc0_cprog_page4/src' building configuration 'ucc' make -C src/ Config=ucc -f ../config/Makefile.template make[1]: Entering directory '/home/numerik/hpc/ss22/hpc0/session10/hpc0_cprog_page4/src' Updating dependencies for xshadow.c ucc -o ../build_ucc/xshadow xshadow.c /home/numerik/pub/ulmcc/lib/libulm.a ../deps_ucc/xcounting.d ../deps_ucc/xempty_stmts.d ../deps_ucc/xpress_return.d ../deps_ucc/xshadow.d make[1]: Leaving directory '/home/numerik/hpc/ss22/hpc0/session10/hpc0_cprog_page4/src' theon$
theon$ ./build_ucc/xshadow 1) i = 43 2) i = 44 theon$ ./build_gcc/xshadow 1) i = 43 2) i = 44 theon$
When does the shadowing begin?
Short answer: Once the declaration gets parsed.
Since C99 within a compound statement you can mix declarations and statements in what ever order you might think is helpful. Before it was required to do all the declarations first and then the statements. So you can have statements in the compound statement that still refer to the previously (on the outside) declared variable while others refer to the locally declared variable:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | #include <stdio.h>
int i = 42; // global variable i
int
main()
{
printf("1) i = %d\n", i); // prints '?????'
int i = 43; // new local variable i
// shadows
printf("2) i = %d\n", i); // prints 'i = 43'
{
printf("3) i = %d\n", i); // prints '?????'
int i = 44; // new local variable i
printf("4) i = %d\n", i); // prints 'i = 44'
}
}
|
theon$ make building configuration 'gcc' make -C src/ Config=gcc -f ../config/Makefile.template make[1]: Entering directory '/home/numerik/hpc/ss22/hpc0/session10/hpc0_cprog_page4/src' Updating dependencies for xshadow2.c gcc -o ../build_gcc/xshadow2 xshadow2.c ../deps_gcc/xcounting.d ../deps_gcc/xempty_stmts.d ../deps_gcc/xpress_return.d ../deps_gcc/xshadow.d ../deps_gcc/xshadow2.d make[1]: Leaving directory '/home/numerik/hpc/ss22/hpc0/session10/hpc0_cprog_page4/src' building configuration 'ucc' make -C src/ Config=ucc -f ../config/Makefile.template make[1]: Entering directory '/home/numerik/hpc/ss22/hpc0/session10/hpc0_cprog_page4/src' Updating dependencies for xshadow2.c ucc -o ../build_ucc/xshadow2 xshadow2.c /home/numerik/pub/ulmcc/lib/libulm.a ../deps_ucc/xcounting.d ../deps_ucc/xempty_stmts.d ../deps_ucc/xpress_return.d ../deps_ucc/xshadow.d ../deps_ucc/xshadow2.d make[1]: Leaving directory '/home/numerik/hpc/ss22/hpc0/session10/hpc0_cprog_page4/src' theon$
The compiler parses the code line-by-line. The compiler creates a new entry in its symbol table whenever a declaration is encountered. This symbol table is hierarchical in the following sense:
-
The compiler initially just has one global symbol table (for all global variables),
-
when a compound statement is entered a new local symbol table is created which has the previous symbol table as its parent.
If an expression refers to a variable the compiler does a loop-up in the current symbol table. If the identifier was not found it performs a look-up in the parent symbol table. This is done recursively until the identifier is either found or (and this would cause an “identifier not declared” error) not found.
Hence, you see the following output:
theon$ ./build_ucc/xshadow2 1) i = 42 2) i = 43 3) i = 43 4) i = 44 theon$ ./build_gcc/xshadow2 1) i = 42 2) i = 43 3) i = 43 4) i = 44 theon$
The output marked with 2) prints the value of the variable i found in the global symbol table. That's because i was not found in the current symbol table. The output marked with 3) comes from a statement that was parsed after the local symbol table already contained an identifier i.
if-statement
An if-statement can have an optional else-branch:
\[\begin{array}{rcl}\langle\text{if-statement}\rangle & \to & \textbf{if}\; \textbf{(}\; \langle\text{expression}\rangle\; \textbf{)}\; \langle\text{statement}\rangle\; \\ & \to & \textbf{if}\; \textbf{(}\; \langle\text{expression}\rangle\; \textbf{)}\; \langle\text{statement}\rangle\; \textbf{else}\; \langle\text{statement}\rangle\; \\\end{array}\]An expression is considered to be true if it evaluates to a non-zero value. Personally I prefer to always use a compound statement even if an expression statement would do. So instead of
1 2 3 4 | if (i > 42)
puts("i is greater than 42"); // expression statement
else
puts("i is not greater than 42"); // expression statement
|
I would write
1 2 3 4 5 | if (i > 42) {
puts("i is greater than 42"); // inside of a compound statement
} else {
puts("i is not greater than 42"); // expression statement
}
|
However, this is just for convenience (in case I want to add another statement to the then-branch or else-branch later). Note that this also avoids the dangling else Problem that will be shown below.
Flow chart for an if-statement without an else-branch
Consider this example without an else-branch
1 2 3 4 5 | puts("A");
if (i > 42) {
puts("B");
}
puts("C");
|
The assembly code generated by the ULM compiler can be indicated by the following flow chart:
Note that the condition i > 42 was negated to i <= 42.
Flow chart for an if-statement with an else-branch
Let's modify the previous example so that we have an else-branch
1 2 3 4 5 6 7 | puts("A");
if (i > 42) {
puts("B");
} else {
puts("C");
}
puts("D");
|
The assembly code generated by the ULM compiler can be indicated by the following flow chart on the left-hand side (the flow chart on the right-hand side would be a legal alternative):
Note that for the variant on the left-hand side the condition has to be negated, but not for the variant on the right-hand side. Consequently the order in with the then-branch and else-branch occurs in the generated code is interchanged.
Dangling else problem
Consider the following two code snippets and be aware that the compiler does not care about how you indent your code:
|
|
So one of this variants should not behave as the programmer seems to expect. Let's test this with a small test program:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | #include <stdio.h>
void
variant_left(int i)
{
puts("I am the code from the left-hand side");
puts("-------------------------------------");
if (i >= 42)
if (i > 42)
printf("%d is greater than 42", i);
else
printf("%d is less than 42", i);
puts("\n");
}
void
variant_right(int i)
{
puts("I am the code from the right-hand side");
puts("--------------------------------------");
if (i >= 42)
if (i > 42)
printf("%d is greater than 42", i);
else
printf("%d is equal to 42", i);
puts("\n");
}
void
test(int i)
{
printf("Test for i = %d\n", i);
printf("===============\n\n");
variant_left(i);
variant_right(i);
}
int
main()
{
test(41);
test(42);
test(43);
}
|
theon$ make building configuration 'gcc' make -C src/ Config=gcc -f ../config/Makefile.template make[1]: Entering directory '/home/numerik/hpc/ss22/hpc0/session10/hpc0_cprog_page4/src' Updating dependencies for xdangling_else.c gcc -o ../build_gcc/xdangling_else xdangling_else.c ../deps_gcc/xcounting.d ../deps_gcc/xdangling_else.d ../deps_gcc/xempty_stmts.d ../deps_gcc/xpress_return.d ../deps_gcc/xshadow.d ../deps_gcc/xshadow2.d make[1]: Leaving directory '/home/numerik/hpc/ss22/hpc0/session10/hpc0_cprog_page4/src' building configuration 'ucc' make -C src/ Config=ucc -f ../config/Makefile.template make[1]: Entering directory '/home/numerik/hpc/ss22/hpc0/session10/hpc0_cprog_page4/src' Updating dependencies for xdangling_else.c ucc -o ../build_ucc/xdangling_else xdangling_else.c /home/numerik/pub/ulmcc/lib/libulm.a ../deps_ucc/xcounting.d ../deps_ucc/xdangling_else.d ../deps_ucc/xempty_stmts.d ../deps_ucc/xpress_return.d ../deps_ucc/xshadow.d ../deps_ucc/xshadow2.d make[1]: Leaving directory '/home/numerik/hpc/ss22/hpc0/session10/hpc0_cprog_page4/src' theon$
theon$ ./build_ucc/xdangling_else Test for i = 41 =============== I am the code from the left-hand side ------------------------------------- I am the code from the right-hand side -------------------------------------- Test for i = 42 =============== I am the code from the left-hand side ------------------------------------- 42 is less than 42 I am the code from the right-hand side -------------------------------------- 42 is equal to 42 Test for i = 43 =============== I am the code from the left-hand side ------------------------------------- 43 is greater than 42 I am the code from the right-hand side -------------------------------------- 43 is greater than 42 theon$ ./build_gcc/xdangling_else Test for i = 41 =============== I am the code from the left-hand side ------------------------------------- I am the code from the right-hand side -------------------------------------- Test for i = 42 =============== I am the code from the left-hand side ------------------------------------- 42 is less than 42 I am the code from the right-hand side -------------------------------------- 42 is equal to 42 Test for i = 43 =============== I am the code from the left-hand side ------------------------------------- 43 is greater than 42 I am the code from the right-hand side -------------------------------------- 43 is greater than 42 theon$
From looking merely at the grammar the question “to which if does the dangling else blong” can not be answered. The specification for the correct behaviour is buried in the C standard which states “it belongs to the previous if”. Using compound statements instead of simple expression statements the meaning of the above code can be made more expressive as follows (the indentation was intentionally not adapted):
|
|
do-while-loop
\[\begin{array}{rcl}\langle\text{do-while-loop}\rangle & \to & \textbf{do}\; \langle\text{statement}\rangle\; \textbf{while}\; \textbf{(}\; \langle\text{expression}\rangle\; \textbf{)}\; \textbf{;}\; \\\end{array}\]The statement of a do-while-loop gets executed at least once. Then the expression gets evaluated, and if it evaluates to a non-zero value the statement gets executed again. It is a good exercise to draw a flow chart for the do-while-loop to see how it differs from a while-loop. For checking your flow chart consider some example programs and see if your flow chart describes the output.
The statement gets executed at least once
In this example the condition is always false (because a hard coded false which is a zero literal is used). Due to my personal coding style I would use a compound statement but of course you also can use an expression statement like here:
1 2 3 4 5 6 7 8 9 10 | #include <stdbool.h>
#include <stdio.h>
int
main()
{
do
puts("do-while-statement executed\n");
while (false);
}
|
theon$ make building configuration 'gcc' make -C src/ Config=gcc -f ../config/Makefile.template make[1]: Entering directory '/home/numerik/hpc/ss22/hpc0/session10/hpc0_cprog_page4/src' Updating dependencies for xwhile1.c gcc -o ../build_gcc/xwhile1 xwhile1.c ../deps_gcc/xcounting.d ../deps_gcc/xdangling_else.d ../deps_gcc/xempty_stmts.d ../deps_gcc/xpress_return.d ../deps_gcc/xshadow.d ../deps_gcc/xshadow2.d ../deps_gcc/xwhile1.d make[1]: Leaving directory '/home/numerik/hpc/ss22/hpc0/session10/hpc0_cprog_page4/src' building configuration 'ucc' make -C src/ Config=ucc -f ../config/Makefile.template make[1]: Entering directory '/home/numerik/hpc/ss22/hpc0/session10/hpc0_cprog_page4/src' Updating dependencies for xwhile1.c ucc -o ../build_ucc/xwhile1 xwhile1.c /home/numerik/pub/ulmcc/lib/libulm.a ../deps_ucc/xcounting.d ../deps_ucc/xdangling_else.d ../deps_ucc/xempty_stmts.d ../deps_ucc/xpress_return.d ../deps_ucc/xshadow.d ../deps_ucc/xshadow2.d ../deps_ucc/xwhile1.d make[1]: Leaving directory '/home/numerik/hpc/ss22/hpc0/session10/hpc0_cprog_page4/src' theon$
theon$ ./build_ucc/xwhile1 do-while-statement executed theon$ ./build_gcc/xwhile1 do-while-statement executed theon$
Typical use: Repeat the statement until a condition is met
In this example the statement gets repeated until getchar() could read an upper case latter ('A', ... 'Z') from standard input or returned EOF (end of file which you can type in with CONTROL-D). The condition for the loop is
1 | (ch = getchar()) != EOF && !(ch >= 'A' && ch <= 'Z')
|
and intensionally a bit more complex. If necessary, review parts of the previous page to convince yourself that this is actually an expression that evaluates to true if the condition is met. Furthermore, the side effect is here that variable ch either has the value EOF or 'A'.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | #include <stdbool.h>
#include <stdio.h>
int
main()
{
int ch;
do
printf("Give me some upper case letter!\n");
while ((ch = getchar()) != EOF && !(ch >= 'A' && ch <= 'Z'));
if (ch == EOF) {
puts("Did not get an upper case letter before end-of-file was reached");
} else {
printf("I got an '%c'\n", ch);
}
}
|
theon$ make building configuration 'gcc' make -C src/ Config=gcc -f ../config/Makefile.template make[1]: Entering directory '/home/numerik/hpc/ss22/hpc0/session10/hpc0_cprog_page4/src' Updating dependencies for xwhile2.c gcc -o ../build_gcc/xwhile2 xwhile2.c ../deps_gcc/xcounting.d ../deps_gcc/xdangling_else.d ../deps_gcc/xempty_stmts.d ../deps_gcc/xpress_return.d ../deps_gcc/xshadow.d ../deps_gcc/xshadow2.d ../deps_gcc/xwhile1.d ../deps_gcc/xwhile2.d make[1]: Leaving directory '/home/numerik/hpc/ss22/hpc0/session10/hpc0_cprog_page4/src' building configuration 'ucc' make -C src/ Config=ucc -f ../config/Makefile.template make[1]: Entering directory '/home/numerik/hpc/ss22/hpc0/session10/hpc0_cprog_page4/src' Updating dependencies for xwhile2.c ucc -o ../build_ucc/xwhile2 xwhile2.c /home/numerik/pub/ulmcc/lib/libulm.a ../deps_ucc/xcounting.d ../deps_ucc/xdangling_else.d ../deps_ucc/xempty_stmts.d ../deps_ucc/xpress_return.d ../deps_ucc/xshadow.d ../deps_ucc/xshadow2.d ../deps_ucc/xwhile1.d ../deps_ucc/xwhile2.d make[1]: Leaving directory '/home/numerik/hpc/ss22/hpc0/session10/hpc0_cprog_page4/src' theon$
For testing you either can use the program interactively (i.e. run it and type some characters) or use some files as input. For example:
-
With a file like this
1 2
abC BAGh
you get
theon$ ./build_ucc/xwhile2 < file_with_uppercase Give me some upper case letter! Give me some upper case letter! Give me some upper case letter! I got an 'C' theon$ ./build_gcc/xwhile2 < file_with_uppercase Give me some upper case letter! Give me some upper case letter! Give me some upper case letter! I got an 'C' theon$
-
And with this file
you get
theon$ ./build_ucc/xwhile2 < file_without_uppercase Give me some upper case letter! Give me some upper case letter! Give me some upper case letter! Give me some upper case letter! Give me some upper case letter! Give me some upper case letter! Give me some upper case letter! Give me some upper case letter! Give me some upper case letter! Did not get an upper case letter before end-of-file was reached theon$ ./build_gcc/xwhile2 < file_without_uppercase Give me some upper case letter! Give me some upper case letter! Give me some upper case letter! Give me some upper case letter! Give me some upper case letter! Give me some upper case letter! Give me some upper case letter! Give me some upper case letter! Give me some upper case letter! Did not get an upper case letter before end-of-file was reached theon$
while-loop
You saw quite a few examples already for using a while loop in C. So you are probably not surprised by its grammar:
\[\begin{array}{rcl}\langle\text{while-loop}\rangle & \to & \textbf{while}\; \textbf{(}\; \langle\text{expression}\rangle\; \textbf{)}\; \langle\text{statement}\rangle\;\end{array}\]Flow chart for understanding the semantics
Let's consider this example for explaining the semantic:
1 2 3 4 5 6 7 8 9 10 11 12 | #include <stdio.h>
int
main()
{
int i = 0;
while (i < 5) {
printf("i = %d\n", i);
++i;
}
printf("after the loop: i = %d\n", i);
}
|
theon$ make building configuration 'gcc' make -C src/ Config=gcc -f ../config/Makefile.template make[1]: Entering directory '/home/numerik/hpc/ss22/hpc0/session10/hpc0_cprog_page4/src' Updating dependencies for xwhile.c gcc -o ../build_gcc/xwhile xwhile.c ../deps_gcc/xcounting.d ../deps_gcc/xdangling_else.d ../deps_gcc/xempty_stmts.d ../deps_gcc/xpress_return.d ../deps_gcc/xshadow.d ../deps_gcc/xshadow2.d ../deps_gcc/xwhile.d ../deps_gcc/xwhile1.d ../deps_gcc/xwhile2.d make[1]: Leaving directory '/home/numerik/hpc/ss22/hpc0/session10/hpc0_cprog_page4/src' building configuration 'ucc' make -C src/ Config=ucc -f ../config/Makefile.template make[1]: Entering directory '/home/numerik/hpc/ss22/hpc0/session10/hpc0_cprog_page4/src' Updating dependencies for xwhile.c ucc -o ../build_ucc/xwhile xwhile.c /home/numerik/pub/ulmcc/lib/libulm.a ../deps_ucc/xcounting.d ../deps_ucc/xdangling_else.d ../deps_ucc/xempty_stmts.d ../deps_ucc/xpress_return.d ../deps_ucc/xshadow.d ../deps_ucc/xshadow2.d ../deps_ucc/xwhile.d ../deps_ucc/xwhile1.d ../deps_ucc/xwhile2.d make[1]: Leaving directory '/home/numerik/hpc/ss22/hpc0/session10/hpc0_cprog_page4/src' theon$
theon$ ./build_ucc/xwhile i = 0 i = 1 i = 2 i = 3 i = 4 after the loop: i = 5 theon$ ./build_gcc/xwhile i = 0 i = 1 i = 2 i = 3 i = 4 after the loop: i = 5 theon$
The are two possible flow-charts for describing the logic (i.e. semantic) of the while loop. This also the two principle variants for generating assembly code:
Exercis
Look into the assembler code generated by ucc to figure out which of the flow chart is visualizing its code generation for while loops.
Some example for bad taste
Just as a remainder for the meaning of the postfix increment operator consider this example:
1 2 3 4 5 6 7 8 9 10 11 | #include <stdio.h>
int
main()
{
int i = 0;
// with this code you sooner or later will shot your own leg ...
while (i < 5)
printf("i = %d\n", i++);
printf("after the loop: i = %d\n", i);
}
|
Doing the increment within the function call is certainly legit. However, it is unreadable code because you easily might oversee it in that place. This in turn means that your code is error prone to modifications. And this in turn means you sooner or later get trouble from that code.
theon$ make building configuration 'gcc' make -C src/ Config=gcc -f ../config/Makefile.template make[1]: Entering directory '/home/numerik/hpc/ss22/hpc0/session10/hpc0_cprog_page4/src' Updating dependencies for xwhile2.c gcc -o ../build_gcc/xwhile2 xwhile2.c ../deps_gcc/xcounting.d ../deps_gcc/xdangling_else.d ../deps_gcc/xempty_stmts.d ../deps_gcc/xpress_return.d ../deps_gcc/xshadow.d ../deps_gcc/xshadow2.d ../deps_gcc/xwhile.d ../deps_gcc/xwhile1.d ../deps_gcc/xwhile2.d make[1]: Leaving directory '/home/numerik/hpc/ss22/hpc0/session10/hpc0_cprog_page4/src' building configuration 'ucc' make -C src/ Config=ucc -f ../config/Makefile.template make[1]: Entering directory '/home/numerik/hpc/ss22/hpc0/session10/hpc0_cprog_page4/src' Updating dependencies for xwhile2.c ucc -o ../build_ucc/xwhile2 xwhile2.c /home/numerik/pub/ulmcc/lib/libulm.a ../deps_ucc/xcounting.d ../deps_ucc/xdangling_else.d ../deps_ucc/xempty_stmts.d ../deps_ucc/xpress_return.d ../deps_ucc/xshadow.d ../deps_ucc/xshadow2.d ../deps_ucc/xwhile.d ../deps_ucc/xwhile1.d ../deps_ucc/xwhile2.d make[1]: Leaving directory '/home/numerik/hpc/ss22/hpc0/session10/hpc0_cprog_page4/src' theon$
theon$ ./build_ucc/xwhile2 i = 0 i = 1 i = 2 i = 3 i = 4 after the loop: i = 5 theon$ ./build_gcc/xwhile2 i = 0 i = 1 i = 2 i = 3 i = 4 after the loop: i = 5 theon$
Actually some note here
Due to my coding style I only write i++ if writing ++i would not have the same effect. This means in my code I rarely see any use of i++ or i--. It is actually that rare that my subconsciousness triggers alarm and puts me in fight or flight mode when there is ++i or --i in the same room. This means I certainly would never oversee the i++ in the printf("i = %d\n", i++) expression.
Still, I would not use this code. However, in the quiz you will see that in a few cases C might kind of force you to use the postfix operand. So usage of i++ or i-- means that I am applying some established pattern in this piece of code.
for-loop
The grammar of a for-loop is formally
\[\begin{array}{rcl}\langle\text{for-loop}\rangle & \to & \textbf{for}\; \textbf{(}\; \langle\text{initial-clause}\rangle_\text{opt}\; \textbf{;}\; \langle\text{expression}\rangle_\text{opt}\; \textbf{;}\; \langle\text{expression}\rangle_\text{opt}\; \textbf{)}\; \langle\text{statement}\rangle\; \\\langle\text{initial-clause}\rangle\; & \to & \langle\text{expression}\rangle\; \\ & \to & \langle\text{declaration}\rangle_\text{since C99}\; \\\end{array}\]For describing the semantics let's just give the expressions and the statement distinct (and meaningful) names:
\[\begin{array}{rcl}\langle\text{for-loop}\rangle & \to & \textbf{for}\; \textbf{(}\; \langle\text{initial-clause}\rangle_\text{opt}\; \textbf{;}\; \langle\text{condition}\rangle_\text{opt}\; \textbf{;}\; \langle\text{update}\rangle_\text{opt}\; \textbf{)}\; \langle\text{loop-body}\rangle\; \\\langle\text{initial-clause}\rangle\; & \to & \langle\text{expression}\rangle\; \\ & \to & \langle\text{declaration}\rangle_\text{since C99}\; \\\langle\text{condition}\rangle\; & \to & \langle\text{expression}\rangle\; \\\langle\text{update}\rangle\; & \to & \langle\text{expression}\rangle\; \\\langle\text{loop-body}\rangle\; & \to & \langle\text{statement}\rangle\; \\\end{array}\]Then the semantic of a for-loop can be described by the following flow-charts:
The initial-clause is evaluated exactly once before the condition gets evaluated. If the condition is true the loop-body is executed. Each time the loop-body was executed the update-expression gets evaluated. After the update the condition of the loop is checked again. Some common use-pattern is therefor:
-
Initialize some counter with the initial-clause,
-
Check if the counter is within some valid rang in the condition, and
-
increment/decrement the counter in the update-expression.
Before C99 the initial-clause could only be an expression. Since C99 it can also be an declaration, like in this code snippets:
-
for-loop with an expression statement as loop-body:
1 2 3 4 5
for (int i = 0; i < n; ++i) /* i declared in the initial clause is only visible within this statement */ ;
-
for-loop with a compound statement as loop-body:
1 2 3 4 5 6
for (int i = 0; i < n; ++i) { /* i declared in the initial clause is only visible within this compound statement */ }
Leaving the initial-clause, condition or update empty
Expressions can be empty (see page 3). Hence, the grammar allows that the initial-clause, condition and update are empty expressions. In case the initial-clause or update the compiler just does not generate any instructions for these parts. If the expression for the condition is empty then the condition is always true.
Hence this is a perfectly legal infinite-loop:
1 2 | for (;;)
puts("I will print this forever");
|
There is also no requirement that a counter is incremented with the update expression. For counting from to \(n-1\) this code might be unusual but legal:
1 2 3 4 5 6 7 8 9 10 11 | #include <stdbool.h>
#include <stdio.h>
int
main()
{
int n = 5;
for (int i = -1; ++i < n;) {
printf("i= %d\n", i);
}
}
|
theon$ make building configuration 'gcc' make -C src/ Config=gcc -f ../config/Makefile.template make[1]: Entering directory '/home/numerik/hpc/ss22/hpc0/session10/hpc0_cprog_page4/src' Updating dependencies for xfor.c gcc -o ../build_gcc/xfor xfor.c ../deps_gcc/xcounting.d ../deps_gcc/xdangling_else.d ../deps_gcc/xempty_stmts.d ../deps_gcc/xfor.d ../deps_gcc/xpress_return.d ../deps_gcc/xshadow.d ../deps_gcc/xshadow2.d ../deps_gcc/xwhile.d ../deps_gcc/xwhile1.d ../deps_gcc/xwhile2.d make[1]: Leaving directory '/home/numerik/hpc/ss22/hpc0/session10/hpc0_cprog_page4/src' building configuration 'ucc' make -C src/ Config=ucc -f ../config/Makefile.template make[1]: Entering directory '/home/numerik/hpc/ss22/hpc0/session10/hpc0_cprog_page4/src' Updating dependencies for xfor.c ucc -o ../build_ucc/xfor xfor.c /home/numerik/pub/ulmcc/lib/libulm.a ../deps_ucc/xcounting.d ../deps_ucc/xdangling_else.d ../deps_ucc/xempty_stmts.d ../deps_ucc/xfor.d ../deps_ucc/xpress_return.d ../deps_ucc/xshadow.d ../deps_ucc/xshadow2.d ../deps_ucc/xwhile.d ../deps_ucc/xwhile1.d ../deps_ucc/xwhile2.d make[1]: Leaving directory '/home/numerik/hpc/ss22/hpc0/session10/hpc0_cprog_page4/src' theon$
theon$ ./build_ucc/xfor i= 0 i= 1 i= 2 i= 3 i= 4 theon$ ./build_gcc/xfor i= 0 i= 1 i= 2 i= 3 i= 4 theon$
break-statement and continue-statement
\[\begin{array}{rcl}\langle\text{break-statement}\rangle & \to & \textbf{break}\; \textbf{;}\; \\\langle\text{continue-statement}\rangle & \to & \textbf{continue}\; \textbf{;}\; \\\end{array}\]Break and continue statements are allowed within a loop (for-loop, while-loop or do-while-loop):
-
a break-statement causes a jump to the end of the loop, i.e. the first instruction that follows the loop.
-
continue-statement causes a jump to the first instruction of the loop condition which checks whether a loop-body gets executed another time.
return-statement
\[\begin{array}{rcl}\langle\text{return-statement}\rangle & \to & \textbf{return}\; \langle\text{expression}\rangle_\text{opt}\; \textbf{;}\; \\\end{array}\]A return statements jumps to the end of the current function.
Statements not supported by ucc
Currently not supported by ucc are switch and goto statements and everything around these, i.e.
\[\begin{array}{rcl}\langle\text{statement}\rangle & \to & \langle\text{switch-statement}\rangle_\text{not supported by ucc}\; \textbf{;}\; \\ & \to & \langle\text{goto-statement}\rangle_\text{not supported by ucc}\; \textbf{;}\; \\ & \to & \langle\text{labeled-statement}\rangle_\text{not supported by ucc}\; \textbf{;}\; \\\langle\text{switch-statement}\rangle & \to & \textbf{switch}\; \textbf{(}\; \langle\text{expression}\rangle\; \textbf{)}\; \langle\text{statement}\rangle \\\langle\text{goto-statement}\rangle & \to & \textbf{goto}\; \langle\text{named-label}\rangle\; \textbf{;}\; \\\langle\text{labeled-statement}\rangle & \to & \langle\text{label}\rangle\; \textbf{:}\; \langle\text{statement}\rangle\; \\\langle\text{label}\rangle & \to & \langle\text{named-label}\rangle\; \textbf{:}\; \langle\text{statement}\rangle\; \\ & \to & \langle\text{case-label}\rangle\; \textbf{:}\; \langle\text{statement}\rangle\; \\ & \to & \langle\text{default-label}\rangle\; \textbf{:}\; \langle\text{statement}\rangle\; \\\langle\text{named-label}\rangle & \to & \langle\text{identifier}\rangle\; \\\langle\text{case-label}\rangle & \to & \textbf{case}\; \langle\text{constant-expression}\rangle\; \\\langle\text{default-label}\rangle & \to & \textbf{default}\; \\\end{array}\]Consider this as kind of a to-do list.