An alternative input function is gets() which reads a string of characters
from the stream stdin until a newline character is detected. The newline
character is replaced by a null (0 byte) in the target string. This function
has the advantage of allowing whitespace to be read in. The following program
is a modification to the earlier one, using gets() instead of scanf().
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
main()
{
char data[80];
char *p;
char name[30];
int age;
printf("\nEnter your name and age ");
/* Read in a string of data */
gets(data);
/* P is a pointer to the last character in the input string */
p = &data[strlen(data) - 1];
/* Remove any trailing spaces by replacing them with null bytes */
while(*p == ' '){
*p = 0;
p--;
}
/* Locate last space in the string */
p = strrchr(data,' ');
/* Read age from string and convert to an integer */
age = atoi(p);
/* Terminate data string at start of age field */
*p = 0;
/* Copy data string to name variable */
strcpy(name,data);
/* Display results */
printf("\nName is %s age is %d",name,age);
}
The most common output function is printf() which is very similar to scanf()
except that it writes formatted data out to the standard output stream stdout.
Printf() takes a list of output data fields and applies format specifiers to
each and outputs the result. The format specifiers are basicaly the same as
for scanf() except that flags may be added to the format specifiers. These
flags include;
- Which left justifies the output padding to the right with spaces.
+ Which causes numbers to be prefixed by their sign
The width specifier is also slightly different for printf(). In its most
useful form is the precision specifier;
width.precision
So, to print a floating point number to three decimal places you would use;
printf("%.3f",x);
And to pad a string with spaces to the right or truncate the string to twenty
characters if it is longer, to form a fixed twenty character width;
printf("%-20.20s",data);
Special character constants may appear in the printf() parameter list. These
are;
\n Newline
\r Carriage return
\t Tab
\b Sound the computer's bell
\f Formfeed
\v Vertical tab
\\ Backslash character
\' Single quote
\" Double quote
\? Question mark
\O Octal string
\x Hexadecimal string
Thus, to display "Hello World", surrounded in quote marks and followed by a
newline you would use;
printf("\"Hello World\"\n");
The following program shows how a decimal integer may be displayed as a
decimal, hexadecimal or octal integer. The 04 following the % in the printf()
format tells the compiler to pad the displayed figure to a width of at least
four digits padded with leading zeroes if required.
/* A simple decimal to hexadecimal and octal conversion program */
#include <stdio.h>
main()
{
int x;
do{
printf("\nEnter a number, or 0 to end ");
scanf("%d",&x);
printf("%04d %04X %04o",x,x,x);
}while(x != 0);
}
There are associated functions to printf() which you should be aware of.
fprintf() has the prototype;
fprintf(FILE *fp,char *format[,argument,...]);
This variation on printf() simply sends the formatted output to the specfied
file stream.
sprintf() has the function prototype;
sprintf(char *s,char *format[,argument,...]);
and writes the formatted output to a string. You should take care using
sprintf() that the target string has been declared long enough to hold the
result of the sprintf() output. Otherwise other data will be overwritten in
memory.
An example of using sprintf() to copy multiple data to a string;
#include<stdio.h>
int main()
{
char buffer[50];
sprintf(buffer,"7 * 5 == %d\n",7 * 5);
puts(buffer);
}
An alternative to printf() for outputting a simple string to the stream stdout
is puts(). This function sends a string to the stream stdout followed by a
newline character. It is faster than printf(), but far less flexible. Instead
of;
printf("Hello World\n");
You can use;
puts("Hello World");
PROGRAM CONTROL STATEMENTS
As with any computer language, C includes statements which test the outcome of
an expression. The outcome of the test is either TRUE or FALSE. The C language
defines a value of TRUE as being non-zero, and FALSE as being zero.
Selection statements:
The general purpose selection statement is "if" which follows the general
form;
if (expression)
statement
else
statement
Where "statement" may be a single statement, or a code block enclosed in curly
braces. The "else" is optional. If the result of the expression equates to
TRUE, then the statement(s) following the if() will be evaluated. Otherwise
the statement(s) following the else, if there is one, will be evaluated.
An alternative to the if....else combination is the ?: command which takes the
form;
expression ? true_expression : false_expression
Where if the expression evaluates to TRUE, then the true_expression will be
evaluated, otherwise the false_expression will be evaluated. Thus we get;
#include <stdio.h>
main()
{
int x;
x = 6;
printf("\nx is an %s number", x % 2 == 0 ? "even" : "odd");
}
C also provides a multiple branch selection statement, switch, which
successively tests a value of an expression against a list of values and
branches program execution to the first match found. The general form of
switch is;
switch (expression){
case value1 : statements
break;
case value2 : statements
break;
case valuen : statements
break;
default : statements
The break statement is optional, but if ommited, program execution will
continue down the list.
#include <stdio.h>
main()
{
int x;
x = 6;
switch(x){
case 0 : printf("\nx equals zero");
break;
case 1 : printf("\nx equals one");
break;
case 2 : printf("\nx equals two");
break;
case 3 : printf("\nx equals three");
break;
default : printf("\nx is larger than three");
}
}
Switch statements may be nested within one another. This is a particuarly
useful feature for confusing people who read your source code!
Iteration statements:
C provides three looping or iteration statements; for, while, and do-while.
The for loop has the general form;
for(initialization;condition;increment)
and is useful for counters such as in this example which displays the entire
ascii character set;
#include <stdio.h>
main()
{
int x;
for(x = 32; x < 128; x++)
printf("%d\t%c\t",x,x);
}
An infinite for loop is also quite valid;
for(;;){
statements
}
Also, C allows empty statements. The following for loop removes leading spaces
from a string;
for(; *str == ' '; str++)
;
Notice the lack of an initializer, and the empty statement following the loop.
The while loop is somewhat simpler than the for loop and follows the general
form;
while (condition)
statements
The statement following the condition, or statements enclosed in curly braces
will be executed untill the condition is FALSE. If the condition is false
before the loop commences, the loop statements will not be executed. The do-
while loop on the other hand is always executed at least once. It takes the
general form;
do{
statements
}while(condition);
Jump statements:
The "return" statement is used to return from a function to the calling
function. Depending upon the declared return data type of the function it may
or may not return a value;
int MULT(int x, int y)
{
return(x * y);
}
or;
void FUNCA()
{
printf("\nHello World");
return;
}
The "break" statement is used to break out of a loop or from a switch
statement. In a loop it may be used to terminate the loop prematurely, as
shown here;
#include <stdio.h>
main()
{
int x;
for(x = 0; x < 256; x++){
if (x == 100)
break;
printf("%d\t",x);
}
}
In contrast to "break" is "continue", which forces the next iteration of the
loop to occur, effectively forcing program contol back to the loop statement.
C provides a function for terminating the program prematurely, "exit()".
Exit() may be used with a return value to pass back to the calling program;
exit(return_value);
POINTERS
A pointer is a variable which holds the memory address of an item of data.
Hence it points to another item. A pointer is declared like an ordinary
variable, but its name is prefixed by '*', thus;
char *p;
This declares the variable 'p' to be a pointer to a character variable.
Pointers are very powerful, and similarly dangerous! If only because a pointer
can be inadvertently set to point to the code segment of a program and then
some value assigned to the address of the pointer!
The following program illustrates a simple, though fairly useless application
of a pointer;
#include <stdio.h>
main()
{
int a;
int *x;
/* x is a pointer to an integer data type */
a = 100;
x = &a;
printf("\nVariable 'a' holds the value %d at memory address %p",a,x);
}
Here is a more useful example of a pointer illustrating how because the
compiler knows the type of data pointed to by the pointer, when the pointer is
incremented it is incremented the correct number of bytes for the data type.
In this case two bytes;
#include <stdio.h>
main()
{
int n;
int a[25];
int *x;
/* x is a pointer to an integer data type */
/* Assign x to point to array element zero */
x = a;
/* Assign values to the array */
for(n = 0; n < 25; n++)
a[n] = n;
/* Now print out all array element values */
for(n = 0; n < 25; n++ , x++)
printf("\nElement %d holds %d",n,*x);
}
To read or assign a value to the address held by a pointer you use the
indirection operator '*'. Thus in the above example, to print the value at the
memory address pointed to by variable x we have used '*x'.
Pointers may be incremented and decremented and have other mathematics applied
to them also. For example in the above program to move variable x along the
array one element at a time we put the statement 'x++' in the for loop. We
could move x along two elements by saying 'x += 2'. Notice that this doesn't
mean "add 2 bytes to the value of x", but rather it means "add 2 of the
pointer's data type size units to the value of x".
Pointers are used extensively in dynamic memory allocation. When a program is
running it is often necessary to temporarily allocate a block of data, say a
table, in memory. C provides the function malloc() for this purpose which
follows the general form;
any pointer type = malloc(number_of_bytes);
malloc() actually returns a void pointer type, which means it can be any type;
integer, character, floating point or whatever. This example allocates a table
in memory for 1000 integers;
#include <stdio.h>
#include <stdlib.h>
main()
{
int *x;
int n;
/* x is a pointer to an integer data type */
/* Create a 1000 element table, sizeof() returns the compiler */
/* specific number of bytes used to store an integer */
x = malloc(1000 * sizeof(int));
/* Check to see if the memory allocation succeeded */
if (x == NULL){
printf("\nUnable to allocate a 1000 element integer table");
exit(0);
}
/* Assign values to each table element */
for(n = 0; n < 1000; n++){
*x = n;
x++;
}
/* Return x to the start of the table */
x -= 1000;
/* Display the values in the table */
for(n = 0; n < 1000; n++){
printf("\nElement %d holds a value of %d",n,*x);
x++;
}
/* Deallocate the block of memory now its no longer required */
free(x);
}
Pointers are also extensively used with character arrays, called strings.
Since all C program strings are terminated by a zero byte we can count the
letters in a string using a pointer;
#include <stdio.h>
#include <string.h>
main()
{
char *p;
char text[100];
int len;
/* Initialise variable 'text' with some writing */
strcpy(text,"This is a string of data");
/* Set variable p to the start of variable text */
p = text;
/* Initialise variable len to zero */
len = 0;
/* Count the characters in variable text */
while(*p){
len++;
p++;
}
/* Display the result */
printf("\nThe string of data has %d characters in it",len);
}
FUNCTIONS
Functions are the source code procedures which comprise a C program. They
follow the general form;
return_type function_name(parameter_list)
{
statements
}
The return_type specifies the data type which will be returned by the
function; char, int, double, void &c.
The code within a C function is invisible to any other C function, and jumps
may not be made from one function into the middle of another, although
functions may call other functions. Also, functions cannot be defined within
functions, only within source modules.
Parameters may be passed to a function either by value, or by reference. If a
parameter is passed by value, then only a copy of the current value of the
parameter is passed to the function. A parameter passed by reference however,
is a pointer to the actual parameter which may then be changed by the
function.
The following example passes two parameters by value to a function, funca(),
which attempts to change the value of the variables passed to it. And then
passes the same two parameters by reference to funcb() which also attempts to
modify their values.