Advantages of Using UNIX With C
Advantages of Using UNIX With C
Pipe -- where the output of one program can be made the input of
another. This can done from command line or within a C program.
UNIX utilities -- there over 200 utilities that let you accomplish many
routines without writing new programs. e.g. make, grep, diff, awk, more ....
System calls -- UNIX has about 60 system calls that are at the heart of
the operating system or the kernel of UNIX. The calls are actually written in
C. All of them can be accessed from C programs. Basic I/0, system clock
access are examples. The function open() is an example of a system call.
Library functions -- additions to the operating system.
#include <stdlib.h>
Arithmetic
Random Numbers
String Conversion
The use of all the functions is relatively straightforward. We only consider them briefly in turn in
this Chapter.
Arithmetic Functions
There are 4 basic integer functions:
Essentially there are two functions with integer and long integer compatibility.
abs
functions return the absolute value of its number arguments. For
example, abs(2) returns 2 as does abs(-2).
div
takes two arguments, numerator and denominator and produces a
quotient and a remainder of the integer division. The div_t structure is
defined (in stdlib.h) as follows:
typedef struct {
int quot; /* quotient */
int rem; /* remainder */
} div_t;
Thus:
#include <stdlib.h>
....
ans = div(num,den);
Answer:
Quotient = 2
Remainder = 2
Random Numbers
Random numbers are useful in programs that need to simulate random events, such as games,
simulations and experimentations. In practice no functions produce truly random data -- they
produce pseudo-random numbers. These are computed form a given formula (different
generators use different formulae) and the number sequences they produce are repeatable. A
seed is usually set from which the sequence is generated. Therefore is you set the same seed all
the time the same set will be be computed.
One common technique to introduce further randomness into a random number generator is to
use the time of the day to set the seed, as this will always be changing. (We will study the
standard library time functions later in Chapter 20).
There are many (pseudo) random number functions in the standard library. They all operate on
the same basic idea but generate different number sequences (based on different generator
functions) over different number ranges.
int rand(void);
void srand(unsigned int seed);
rand() returns successive pseudo-random numbers in the range from 0 to (2^15)-1.
srand() is used to set the seed. A simple example of using the time of the day to initiate a seed
is via the call:
srand( (unsigned int) time( NULL ));
The following program card.c illustrates the use of these functions to simulate a pack of cards
being shuffled:
/*
** Use random numbers to shuffle the "cards" in the deck. The second
** argument indicates the number of cards. The first time this
** function is called, srand is called to initialize the random
** number generator.
*/
#include <stdlib.h>
#include <time.h>
#define TRUE 1
#define FALSE 0
/*
** Seed the random number generator with the current time
** of day if we haven't done so yet.
*/
if( first_time ){
first_time = FALSE;
srand( (unsigned int)time( NULL ) );
}
/*
** "Shuffle" by interchanging random pairs of cards.
*/
for( i = n_cards - 1; i > 0; i -= 1 ){
int where;
int temp;
where = rand() % i;
temp = deck[ where ];
deck[ where ] = deck[ i ];
deck[ i ] = temp;
}
}
There are several other random number generators available in the standard library:
double drand48(void);
double erand48(unsigned short xsubi[3]);
long lrand48(void);
long nrand48(unsigned short xsubi[3]);
long mrand48(void);
long jrand48(unsigned short xsubi[3]);
void srand48(long seed);
unsigned short *seed48(unsigned short seed[3]);
void lcong48(unsigned short param[7]);
Functions lrand48() and nrand48() return non-negative long integers uniformly distributed over
the interval [0, 2**31).
Functions mrand48() and jrand48() return signed long integers uniformly distributed over the
interval [-2**31, 2**31).
Functions srand48(), seed48(), and lcong48() set the seeds for drand48(), lrand48(), or mrand48()
and one of these should be called first.
String Conversion
There are a few functions that exist to convert strings to integer, long integer and float values.
They are:
int i;
float f;
i = atoi(str1); /* i = 100 */
f = atof(str2); /* f = 55.44 */
i = atoi(str3); /* i = 1234 */
i = atoi(str4); /* i = 123 */
i = atoi(str5); /* i = 0 */
Note:
The qsort standard library function is very useful function that is designed to sort an array by a
key value of any type into ascending order, as long as the elements of the array are of fixed type.
Similarly, there is a binary search function, bsearch() which is prototyped (in stdlib.h) as:
Using the same Record structure and record_compare function as the qsort() example (in
Chapter 11.3):
typedef struct {
int key;
struct
other_data;
} Record;
Also, Assuming that we have an array of array_length Records suitably filled with date we
can call bsearch() like this:
Record key;
Record *ans;
The function bsearch() return a pointer to the field whose key filed is filled with the matched
value of NULL if no match found.
Note that the type of the key argument must be the same as the array elements (Record above),
even though only the key.key element is required to be set.
Your programs will need to include the standard I/O header file so do:
#include <stdio.h>
Reporting Errors
Many times it is useful to report errors in a C program. The standard library perror() is an easy
to use and convenient function. It is used in conjunction with errno and frequently on
encountering an error you may wish to terminate your program early. Whilst not strictly part of
the stdio.h library we introduce the concept of errno and the function exit() here. We will
meet these concepts in other parts of the Standard Library also.
perror()
errno
errno is a special system variable that is set if a system call cannot perform its set task. It is
defined in #include <errno.h>.
It can be manually reset within a C program (although this is uncommon practice) otherwise it
simply retains its last value returned by a system call or library function.
exit()
Exit simply terminates the execution of a program and returns the exit status value to the
operating system. The status value is used to indicate if the program has terminated properly:
Streams
Streams are a portable way of reading and writing data. They provide a flexible and efficient
means of I/O.
A Stream is a file or a physical device (e.g. printer or monitor) which is manipulated with a
pointer to the stream.
There exists an internal C data structure, FILE, which represents all streams and is defined in
stdio.h. We simply need to refer to the FILE structure in C programs when performing I/O with
streams.
then access it
Stream I/O is BUFFERED: That is to say a fixed ``chunk'' is read from or written to a file via
some temporary storage area (the buffer). This is illustrated in Fig. 17.1. NOTE the file pointer
actually points to this buffer.
Fig. Stream I/O Model This leads to efficient I/O but beware: data written to a buffer does
not appear in a file (or device) until the buffer is flushed or written out. ( n does this). Any
abnormal exit of code can cause problems.
Predefined Streams
UNIX defines 3 predefined streams (in stdio.h):
stdin, stdout, stderr
stdin and stdout can be used with files, programs, I/O devices such as keyboard, console, etc..
stderr always goes to the console or screen.
The console is the default for stdout and stderr. The keyboard is the default for stdin.
This is not part of C but operating system dependent. We will do redirection from the command
line.
So if we are expecting input from the keyboard for a program, in we can read similar input from
a file
prog1 | prog2
out | lpr
Basic I/O
There are a couple of function that provide basic I/O facilities.
probably the most common are: getchar() and putchar(). They are defined and used as
follows:
int ch;
ch = getchar();
(void) putchar((char) ch);
Related Functions:
Formatted I/O
We have seen examples of how C uses formatted I/O already. Let's look at this in more detail.
Printf
The function is defined as follows:
Table: Printf/scanf format characters
Format Spec (%) Type Result
c char single character
i,d int decimal number
o int octal number
x,X int hexadecimal number
lower/uppercase notation
u int unsigned int
s char * print string
terminated by 0
f double/float format -m.ddd...
e,E " Scientific Format
-1.23e002
g,G " e or f whichever
is most compact
% - print % character
- (minus sign)
-- left justify.
integer number
-- field width.
m.d
-- m = field width, d = precision of number of digits after decimal point or number of chars from
a string.
So:
printf("%-2.3f n",17.23478);
17.235
and:
printf("VAT=17.5%% n");
...outputs:
VAT=17.5%
scanf
This function is defined as follows:
int scanf(char *format, args....) -- reads from stdin and puts input in address of
variables specified in args list. Returns number of chars read.
scanf(``%d'',&i);
We can just give the name of an array or string to scanf since this corresponds to the start address
of the array/string.
char string[80];
scanf(``%s'',string);
Files
Files are the most common form of a stream.
The first thing we must do is open a file. The function fopen() does this:
fopen returns a pointer to a FILE. The name string is the name of the file on disc that we wish to
access. The mode string controls our type of access. If a file cannot be accessed for any reason a
NULL pointer is returned.
To open a file we must have a stream (file pointer) that points to a FILE structure.
correctly:
``r'')) == NULL)
``myfile.dat'');
exit(1);
......
These are similar to printf and scanf except that data is read from the stream
that must have been opened with fopen().
char *string[80]
FILE *stream, *fopen();
if ( (stream = fopen(...)) != NULL)
fscanf(stream,``%s'', string);
fscanf(stdin,``%s'',string);
For Example:
feof()
-- returns true if the stream is currently at the end of the file. So to read a stream,fp, line by line
you could do:
while ( !feof(fp) )
fscanf(fp,"%s",line);
ferror()
-- reports on the error state of the stream and returns true if an error has occurred.
clearerr()
-- resets the error indication for a given stream.
fileno()
-- returns the integer file descriptor associated with the named stream.
This means we are now using binary (and not text) files.
Instead of file pointers we use low level file handle or file descriptors which give a
unique integer number to identify each file.
int open(char *filename, int flag, int perms) -- this returns a file descriptor or -1 for
a fail.
The flag controls file access and has the following predefined in fcntl.h:
O_APPEND, O_CREAT, O_EXCL, O_RDONLY, O_RDWR, O_WRONLY + others see online man
pages or reference manuals.
The function:
are used to read/write a specific number of bytes from/to a file (handle) stored or to be put in the
memory location specified by buffer.
read and write return the number of bytes read/written or -1 if they fail.