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
if
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
while
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
for
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
case
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
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
getchar
#include
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
printf
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:
%c single char
%s string of chars
%d decimal integer
%o octal integer
%h hexadecimal integer
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
isalpha
#include
int isalpha(int)
Gives a Boolean value saying whether the character is alphabetic
if (isalpha(c))
alpha_count++;
Add comment
Functions
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!
Explanation
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.
Example
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
Example:
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
Example:
The following program reads sets of 3 integers, stopping on end-of-file. For
each set it prints them out in descending order.
#include
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
Summary
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