0% found this document useful (0 votes)
34 views

Advantages of Using UNIX With C

The document discusses advantages of using UNIX with C programming language. Key advantages include: 1) Portability across machines since UNIX is available on many machines and C programs should run with little difficulty. 2) Multi-user and multi-tasking capabilities allowing many programs to share processing power simultaneously. 3) Powerful file handling system with hierarchical structure and file handling routines. 4) Powerful shell programming and ability to run commands, UNIX programs, and custom programs via pipes to pass output as input.
Copyright
© Attribution Non-Commercial (BY-NC)
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
34 views

Advantages of Using UNIX With C

The document discusses advantages of using UNIX with C programming language. Key advantages include: 1) Portability across machines since UNIX is available on many machines and C programs should run with little difficulty. 2) Multi-user and multi-tasking capabilities allowing many programs to share processing power simultaneously. 3) Powerful file handling system with hierarchical structure and file handling routines. 4) Powerful shell programming and ability to run commands, UNIX programs, and custom programs via pipes to pass output as input.
Copyright
© Attribution Non-Commercial (BY-NC)
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 16

Advantages of using UNIX with C

 Portability -- UNIX, or a variety of UNIX, is available on many


machines. Programs written in standard UNIX and C should run on any of
them with little difficulty.
 Multiuser / Multitasking -- many programs can share a machines
processing power.
 File handling -- hierarchical file system with many file handling
routines.
 Shell Programming -- UNIX provides a powerful command interpreter
that
understands over 200 commands and can also run UNIX and user-defined
programs.

 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.

Integer Functions, Random Number, String


Conversion, Searching and Sorting: <stdlib.h>
To use all functions in this library you must:

   #include <stdlib.h>

There are three basic categories of functions:

 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:

int abs(int number);


long int labs(long int number);

div_t div(int numerator,int denominator);


ldiv_t ldiv(long int numerator, long int denominator);

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;

(ldiv_t is similarly defined).

Thus:

#include <stdlib.h>
....

int num = 8, den = 3;


div_t ans;

ans = div(num,den);

printf("Answer:\n\t Quotient = %d\n\t Remainder =


%d\n", \
ans.quot,ans.rem);

Produces the following output:

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.

The simplest set of functions is:

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

void shuffle( int *deck, int n_cards )


{
int i;
static int first_time = TRUE;

/*
** 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]);

This family of functions generates uniformly distributed pseudo-random numbers.

Functions drand48() and erand48() return non-negative double-precision floating-point values


uniformly distributed over the interval [0.0, 1.0).

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.

Further examples of using these functions is given is Chapter 20.

String Conversion
There are a few functions that exist to convert strings to integer, long integer and float values.
They are:

double atof(char *string) -- Convert string to floating point value.


int atoi(char *string) -- Convert string to an integer value
int atol(char *string) -- Convert string to a long integer value.
double strtod(char *string, char *endptr) -- Convert string to a floating point value.
long strtol(char *string, char *endptr, int radix) -- Convert string to a long integer
using a given radix.
unsigned long strtoul(char *string, char *endptr, int radix) -- Convert string to
unsigned long.
Most of these are fairly straightforward to use. For example:

char *str1 = "100";


char *str2 = "55.444";
char *str3 = " 1234";
char *str4 = "123four";
char *str5 = "invalid123";

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:

 Leading blank characters are skipped.


 Trailing illegal characters are ignored.
 If conversion cannot be made zero is returned and errno (See
Chapter 17) is set with the value ERANGE.

Searching and Sorting


  The stdlib.h provides 2 useful functions to perform general searching and sorting of data on
any type. In fact we have already introduced the qsort() function in Chapter 11.3. For
completeness we list the prototype again here but refer the reader to the previous Chapter for an
example.

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.

qsort is prototyped (in stdlib.h):

void qsort(void *base, size_t num_elements, size_t element_size,


int (*compare)(void const *, void const *));

Similarly, there is a binary search function, bsearch() which is prototyped (in stdlib.h) as:

void *bsearch(const void *key, const void *base, size_t nel,


size_t size, int (*compare)(const void *, const void *));

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;

int record\_compare(void const *a, void const *a)


{ return ( ((Record *)a)->key - ((Record *)b)->key );
}

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;

key.key = 3; /* index value to be searched for */


ans = bsearch(&key, array, arraylength, sizeof(Record), record_compare);

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.

Input and Output (I/O):stdio.h


  This chapter will look at many forms of I/O. We have briefly mentioned some forms before will look at
these in much more detail here.

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()

The function perror() is prototyped by:

void perror(const char *message);


perror() produces a message (on standard error output -- see Section 17.2.1), describing the last
error encountered, returned to errno (see below) during a call to a system or library function.
The argument string message is printed first, then a colon and a blank, then the message and a
newline. If message is a NULL pointer or points to a null string, the colon is not printed.

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>.

To use errno in a C program it must be declared via:

   extern int errno;

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()

The function exit() is prototyped in #include <stdlib> by:

void exit(int status)

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:

 it exist with a EXIT_SUCCESS value on successful termination


 it exist with a EXIT_FAILURE value on unsuccessful termination.

On encountering an error you may frequently call an exit(EXIT_FAILURE) to terminate an


errant program.

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.

We just need to declare a variable or pointer of this type in our programs.


We do not need to know any more specifics about this definition.

We must open a stream before doing any I/O,

then access it

and then close 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

They all use text a the method of I/O.

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.

Predefined stream are automatically open.


Redirection

This how we override the UNIX default predefined I/O defaults.

This is not part of C but operating system dependent. We will do redirection from the command
line.

> -- redirect stdout to a file.

So if we have a program, out, that usually prints to the screen then

   out > file1

will send the output to a file, file1.

< -- redirect stdin from a file to a program.

So if we are expecting input from the keyboard for a program, in we can read similar input from
a file

   in < file2.

| -- pipe: puts stdout from one program to stdin of another

   prog1 | prog2

e.g. Sent output (usually to console) of a program direct to printer:

   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 getchar(void) -- reads a char from stdin


 int putchar(char ch) -- writes a char to stdout, returns character written.

   int ch;
 
ch = getchar();
(void) putchar((char) ch);
Related Functions:

   int getc(FILE *stream),


int putc(char ch,FILE *stream)

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:

int printf(char *format, arg list ...) --


prints to stdout the list of arguments according specified format string. Returns number of
characters printed.

The format string has 2 types of object:

 ordinary characters -- these are copied to output.


 conversion specifications -- denoted by % and listed in Table 17.1.

  
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

Between % and format char we can put:

- (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);

The output on the screen is:

          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.

Format control string similar to printf

Note: The ADDRESS of variable or a pointer to one is required by scanf.

   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:

   FILE *fopen(char *name, char *mode)

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.

Modes include: ``r'' -- read,


``w'' -- write and
``a'' -- append.

To open a file we must have a stream (file pointer) that points to a FILE structure.

So to open a file, called myfile.dat for reading we would do:

  FILE *stream, *fopen();


/* declare a stream and prototype fopen */
 
stream = fopen(``myfile.dat'',``r'');
 
it is good practice to to check file is opened

correctly:

if ( (stream = fopen( ``myfile.dat'',

``r'')) == NULL)

{  printf(``Can't open %s n'',

``myfile.dat'');

  exit(1);

......

Reading and writing files

The functions fprintf and fscanf a commonly used to access files.

int fprintf(FILE *stream, char *format, args..)


int fscanf(FILE *stream, char *format, args..)

These are similar to printf and scanf except that data is read from the stream
that must have been opened with fopen().

The stream pointer is automatically incremented with ALL file read/write


functions. We do not have to worry about doing this.

  char *string[80]
FILE *stream, *fopen();
 
if ( (stream = fopen(...)) != NULL)
fscanf(stream,``%s'', string);

Other functions for files:

int getc(FILE *stream), int fgetc(FILE *stream)


int putc(char ch, FILE *s), int fputc(char ch, FILE *s)

These are like getchar, putchar.

getc is defined as preprocessor MACRO in stdio.h. fgetc is a C library


function. Both achieve the same result!!

   fflush(FILE *stream) -- flushes a stream.

   fclose(FILE *stream) -- closes a stream.

We can access predefined streams with fprintf etc.

   fprintf(stderr,``Cannot Compute!! n'');

    fscanf(stdin,``%s'',string);

sprintf and sscanf


These are like fprintf and fscanf except they read/write to a string.

int sprintf(char *string, char *format, args..)

int sscanf(char *string, char *format, args..)

For Example:

  float full_tank = 47.0; /* litres */


float miles = 300;
char miles_per_litre[80];
 
sprintf( miles_per_litre,``Miles per litre
= %2.3f'', miles/full_tank);

Stream Status Enquiries

There are a few useful stream enquiry functions, prototyped as follows:

int feof(FILE *stream);


int ferror(FILE *stream);
void clearerr(FILE *stream);
int fileno(FILE *stream);

Their use is relatively simple:

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.

Low Level I/O


This form of I/O is UNBUFFERED -- each read/write request results in accessing disk (or
device) directly to fetch/put a specific number of bytes.

There are no formatting facilities -- we are dealing with bytes of information.

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.

To Open a file use:

   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.

perms -- best set to 0 for most of our applications.

The function:

   creat(char *filename, int perms)

can also be used to create a file.

int close(int handle) -- close a file

int read(int handle, char *buffer,


unsigned length)
int write(int handle, char *buffer, unsigned length)

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.

The sizeof() function is commonly used to specify the length.

read and write return the number of bytes read/written or -1 if they fail.

/* program to read a list of floats from a binary file */


/* first byte of file is an integer saying how many */
/* floats in file. Floats follow after it, File name got from */
/* command line */
 
#include<stdio.h>
#include<fcntl.h>
 
float bigbuff[1000];
 
main(int argc, char **argv)
 
{  int fd;
int bytes_read;
int file_length;
 
if ( (fd = open(argv[1],O_RDONLY)) = -1)
{ /* error file not open */....
perror("Datafile");
exit(1);
}
if ( (bytes_read = read(fd,&file_length,
sizeof(int))) == -1)
{ /* error reading file */...
exit(1);
}
if ( file_length > 999 ) {/* file too big */ ....}
if ( (bytes_read = read(fd,bigbuff,
file_length*sizeof(float))) ==
-1)
{ /* error reading open */...
exit(1);
}
}

You might also like