This lecture deals with the control flow constructs of if..., while... and for... . It looks at the standard library functions that you can use at any time. It then deals with how to define your own functions, and looks at parameter passing mechanisms.

Control flow

C uses the semicolon to terminate statements (like Ada, unlike Pascal). Statements may be grouped in {...}, just like BEGIN...END. Add comment


The if statement has the form if (expression) statement Note the brackets around the expression. The expression is anything that can be evaluated to a Boolean value of 0 (false) or other number (true). In particular, it may contain executable functions, assignments, etc. if ((ch = getchar()) != EOF) ... The if..then..else form is if (expr) statement else statement as in if (x == 1) x++; else x--; Add comment


The while loop has syntax while (expression) statement Two keywords inside a loop are ``break'' and ``continue''. break is similar to the Ada exit, and terminates the loop. continue ceases execution of the current pass through the loop and returns to the loop condition. /* count the number of even chars and odd chars */ while (1) { if ((ch = getchar()) == EOF) break; if (ch % 2 == 0) { evens++; continue; } odds++; }

Any statement may be empty. This can lead to syntactically correct but erroneous code. This is in fact correct, to copy stdin to stdout: while ((ch = getchar()) != EOF && putchar(ch) != EOF) ; /* empty body */

Add comment


The for loop is the most general loop construct for (initial; continue; increment) statement where initial, continue and increment can be any expressions (including empty). for (i = 0; i < 20; i++) ... for (ch = getchar(); ch != EOF; ch = getchar()) ...

/* forever */ for (;;) ... Add comment


The case statement is of the form switch (expression) { case const: ... case const: ... default: ... } The constants can be any integer values (including enumerated values). NB: each branch should be terminated with ``break'', or it will ``fall'' into the next branch. switch (ch) { case 'a': case 'e': case 'i': case 'o': case 'u': vowel_count++; break; default: other_char_count++; }

Add comment

Standard library functions

C has a large standard library of functions covering areas of:


#include <stdio.h> int putchar(int) putchar prints the character argument to standard output. The value of the function is the character printed if it was successful, or EOF if it was not /* no error check */ putchar('X'); /* with error check */ if (putchar('X') == EOF) /* problem with output device? */ Add comment


#include <stdio.h> int getchar(void) Read a character from standard input. Return a char if successful, EOF if not. Note that the return must in fact be an int, because EOF is not a char. int ch; ch = getchar(); if (ch == EOF) ... Add comment


int printf(char *format, args-list...) Formatted print statement to standard output. ``format'' is a string that is printed after substitutions using the ``args-list'' are made. Special codes are used: The actual values come from the list: n = 20; printf("%d in octal is %o\n", n, n);

Escaped characters can be used in these strings such as
\n new line
\t tab Add comment


#include <ctype.h> int isalpha(int) Gives a Boolean value saying whether the character is alphabetic if (isalpha(c)) alpha_count++; Add comment


C has no procedures, only functions. By returning no value, a function acts like a procedure. By ignoring the function return value you treat a function as though it was a procedure.

Inside a function the ``return'' statement immediately terminates the function. The value (if any) is the value following the ``return'' keyword.

int sum(int m, int n) { return m+n; } There can be any number of return statements int isalpha(int ch) { if ('a' <= ch && ch <= 'z') return 1; if ('A' <= ch && ch <= 'Z') return 1; return 0; } Function definitions may not be nested. Functions with no return value are declared as type ``void''. Functions with no arguments have the argument list declared as ``void''. (Note: this use of void is not the same as the data type void.) void hello_message(void) { printf("Hello there\n"); } Function parameters are all value parameters (Ada IN parameters, Pascal value parameters). There are no OUT (Ada) or VAR (Pascal) parameters. The normal way of changing a value is by using the function value ch = tolower(ch); To change the value of a parameter you have to do your own ``call by reference'' and pass the address of the variable as the parameter. Example: The sum of two ints as a procedure rather than a function: void sumof(int x, int y, int *z) { *z = x + y; } and use it by int n; sumof(1, 2, &n);

There is a very high potential for error in reference parameters. Change int n to int *n , &n to n and printf(.., n) to printf(.., *n) and see if you get strange results. If not, you fluked out lucky!


In order to change the value of a variable, you must pass a pointer to the address of that variable. So the function declaration needs a pointer (here int *).

When you call the function, you must pass an address. The assumption made by the function is that the address is of the right type. If it isn't, then garbage happens. Here, int *n is a variable with unassigned contents. It probably points to a kinky, garbage, address. Maybe you can write to that address, maybe not.

Make it really fail by

int *n = NULL; since you can't write to the NULL address.

Where a function declares a pointer parameter, check (from the doco) to see if the parameter must be the address of existing memory of that type. Often the doco is vague on this.


From the standard library, the function ``frexp'' splits a real number into a fraction and an integer power of two, as in
24 = 0.75 x 2^5

The specification for this function is

double frexp(double value, int *exp) The description says that the function return is the value of the fraction and the power of two is returned in the int pointed to by ``exp''. That means you have to supply an integer, and pass its address to the function, as in int power; long fraction, number; number = 24.0; fraction = frexp(number, &power); Add comment


The ``scanf'' function is used for formatted input just like ``printf'' is used for formatted output. It uses the same substitution sequences ``%d'' for an integer, ``%c'' for a character, etc. However, when it reads a value, it stores it in a variable. The call by value semantics means that it must be given the address of a variable. To read a character: int ch; scanf("%c", &ch); To read an integer: int n; scanf("%d", &n); Note that this is wrong, just like before: int *n; scanf("%d", n); (This is one of the main reasons we don't teach C as first programming language.) Add comment


The following program reads sets of 3 integers, stopping on end-of-file. For each set it prints them out in descending order. #include <stdio.h> void swap( int *m, int *n) { int k; k = *m; *m = *n; *n = k; } int main(int argc, char *argv[]) { int a, b, c; while(scanf("%d %d %d", &a, &b, &c) != EOF) { if (b < c) swap(&b, &c); if (a < b) swap(&a, &b); if (b < c) swap(&b, &c); printf("%d %d %d", a, b, c); } exit(0); }

Add comment


This lecture dealt with the control flow constructs of if..., while... and for... . It looked at the standard library functions of C. It then dealt with how to define your own functions, and looked at parameter passing mechanisms. Some very common problems with addressing were considered.

A crude rule of thumb might be: see what you would write using Ada OUT parameters. For every use of an OUT parameter in a function call, replace it with the address of the variable. DO NOT declare the variable as an address type. Within the function, declare the formal parameter as an address type. Throughout the function dereference every ocurrence of the formal parameter. Add comment, copyright Jan Newmarch.
It is maintained by Jan Newmarch.
Last modified: 10 August, 1996