Thus, the following program will create a new file called "data.txt" and open
it for reading and writing;
#include <stdio.h>
void main()
{
FILE *fp;
fp = fopen("data.txt","w+");
}
To close a stream C provides the function fclose() which accepts the stream's
file pointer as a parameter;
fclose(fp);
If an error occurs in closing the file stream, fclose() returns non zero.
There are four basic functions for receiving and sending data to and from
streams; fgetc(), fputc(), fgets() and fputs().
fgetc() simply reads a single character from the specified input stream;
char fgetc(FILE *fp);
Its opposite number is fputc() which simply writes a single character to the
specified input stream;
char fputc(char c, FILE *fp);
fgets() reads a string from the input stream;
char *fgets(char s, int numbytes, FILE *fp);
It stops reading when either numbytes - 1 bytes have been read, or a newline
character is read in. A null terminating byte is appended to the read string,
s. If an error occurs, fgets() returns NULL.
fputs() writes a null terminated string to a stream;
int fputs(char *s, FILE *fp);
With the exception of fgets() which returns a NULL pointer if an error occurs,
all the other functions described above return EOF (defined in stdio.h) if an
error occurs during the operation.
The following program creates a copy of the file "data.dat" as "data.old" and
illustrates the use of fopen(), fgetc(), fputc() and fclose();
#include <stdio.h>
int main()
{
FILE *in;
FILE *out;
in = fopen("data.dat","r");
if (in == NULL){
printf("\nUnable to open file data.dat for reading\n");
return(0);
}
out = fopen("data.old","w+");
if (out == NULL){
printf("\nUnable to create file data.old\n");
return(0);
}
/* Loop reading and writing one byte at a time untill end-of-file */
while(!feof(in))
fputc(fgetc(in),out);
/* Close the file streams */
fclose(in);
fclose(out);
return(0);
}
Example program using fputs() to copy text from stream stdin (usually typed in
at the keyboard) to a new file called "data.txt".
#include <stdio.h>
int main()
{
FILE *fp;
char text[100];
fp = fopen("data.txt","w+");
do{
gets(text);
fputs(text,fp);
}while(*text);
fclose(fp);
}
Random access using streams:
Random file access for streams is provided for by the fseek() function which
has the following prototype;
int fseek(FILE *fp, long numbytes, int fromwhere);
fseek() repositions a file pointer associated with a stream previously opened
by a call to fopen(). The file pointer is positioned 'numbytes' from the
location 'fromwhere', which may be the file beginning, the current file
pointer position, or the end of the file, symbolised by the constants
SEEK_SET, SEEK_CUR and SEEK_END respectively. If a call to fseek() succeeds, a
value of zero is returned.
Associated with fseek() is ftell() which reports the current file pointer
position of a stream, and has the following function prototype;
long int ftell(FILE *fp);
ftell() returns either the position of the file pointer, measured in bytes
from the start of the file, or -1 in the event of an error occuring.
Handles:
File handles are opened with the open() function which has the prototype;
int open(char *filename,int access[,unsigned mode]);
If open() is successful, the number of the file handle is returned. Otherwise
open() returns -1.
The access integer is comprised from bitwise oring togther of the symbolic
constants declared in fcntl.h. These vary from compiler to compiler but may
be;
O_APPEND If set, the file pointer will be set to the end of the
file prior to each write.
O_CREAT If the file does not exist it is created.
O_TRUNC Truncates the existing file to a length of zero bytes.
O_EXCL Used with O_CREAT
O_BINARY Opens the file in binary mode
O_TEXT Opens file in text mode
The optional mode parameter is comprised by bitwise oring of the symbolic
constants defined in stat.h. These vary from C compiler to C compiler but may
be;
S_IWRITE Permission to write
S_IREAD Permission to read
Once a file handle has been assigned with open(), the file may be accessed
with read() and write().
Read() has the function prototype;
int read(int handle, void *buf, unsigned num_bytes);
It attempts to read 'num_bytes' and returns the number of bytes actually read
from the file handle 'handle', and stores these bytes in the memory block
pointed to by 'buf'.
Write() is very similar to read() and has the same function prototype and
return values, but writes 'num_bytes' from the memory block pointed to by
'buf'.
Files opened with open() are closed using close() which has the function
prototype;
int close(int handle);
close() returns zero on success, and -1 if an error occurs trying to close the
handle.
Random access is provided by lseek() which is very similar to fseek(), except
that it accepts an integer file handle as the first parameter rather than a
stream FILE pointer.
This example uses file handles to read data from stdin (usually the keyboard)
and copy the text to a new file called "data.txt".
#include <io.h>
#include <fcntl.h>
#include <sys\stat.h>
int main()
{
int handle;
char text[100];
handle = open("data.txt",O_RDWR|O_CREAT|O_TRUNC,S_IWRITE);
do{
gets(text);
write(handle,&text,strlen(text));
}while(*text);
close(handle);
}
THE C PREPROCESSOR
C allows for commands to the compiler to be included in the source code. These
commands are then refered to as preprocessor commands and are defined by the
ANSI standard to be;
#if
#ifdef
#ifndef
#else
#elif
#include
#define
#undef
#line
#error
#pragma
All preprocessor commands start with a hash symbol, "#", and must be on a line
on their own (although comments may follow).
#define:
The #define command specifies an identifier and a string which the compiler
will substitute every time it comes accross the identifier within that source
code module. For example;
#define FALSE 0
#define TRUE !FALSE
The compiler will replace any subsequent occurence of 'FALSE' with '0' and any
subsequent occurence of 'TRUE' with '!0'. The substitution does NOT take place
if the compiler finds that the identifier is enclosed by quote marks, so
printf("TRUE");
would NOT be replaced, but
printf("%d",FALSE);
would be.
The #define command can also be used to define macros which may include
parameters. The parameters are best enclosed in parenthesis to ensure that
correct substitution occurs.
This example declares a macro 'larger()' which accepts two parameters and
returns the larger of the two;
#include <stdio.h>
#define larger(a,b) (a > b) ? (a) : (b)
int main()
{
printf("\n%d is largest",larger(5,7));
}
#error:
The #error command causes the compiler to stop compilation and to display the
text following the #error command. For example;
#error REACHED MODULE B
will cause the compiler to stop compilation and display;
REACHED MODULE B
#include:
The #include command tells the compiler to read the contents of another source
file. The name of the source file must be enclosed either by quotes or by
angular brackets thus;
#include "module2.c"
#include <stdio.h>
Generally, if the file name is enclosed in angular brackets, then the compiler
will search for the file in a directory defined in the compiler's setup.
Whereas if the file name is enclosed in quotes then the compiler will look for
the file in the current directory.
#if, #else, #elif, #endif
The #if set of commands provide conditional compilation around the general
form;
#if constant_expression
statements
#else
statements
#endif
#elif stands for '#else if' and follows the form;
#if expression
statements
#elif expression
statements
#endif
#ifdef, #ifndef:
These two commands stand for '#if defined' and '#if not defined' respectively
and follow the general form;
#ifdef macro_name
statements
#else
statements
#endif
#ifndef macro_name
statements
#else
statements
#endif
where 'macro_name' is an identifier declared by a #define statement.
#undef:
Undefines a macro previously defined by #define.
#line:
Changes the compiler declared global variables __LINE__ and __FILE__. The
general form of #line is;
#line number "filename"
where number is inserted into the variable '__LINE__' and 'filename' is
assigned to '__FILE__'.
#pragma:
This command is used to give compiler specific commands to the compiler. The
compiler's manual should give you full details of any valid options to go with
the particular implementation of #pragma that it supports.
STRINGS
The C language has one of the most powerful string handling capabilities of
any computer language.
A string is a single dimension array of characters terminated by a zero byte.
Strings may be initialised in two ways. Either in the source code where they
may be assigned a constant value, as in;
int main()
{
char *p = "System 5";
char name[] = "Test Program" ;
}
or at run time by the function strcpy() which has the function prototype;
char *strcpy(char *destination, char *source);
strcpy() copies the string pointed to by source into the location pointed to
by destination as in the following example;
#include<stdio.h>
int main()
{
char name[50];
strcpy(name,"Servile Software");
printf("\nName equals %s",name);
}
C also allows direct access to each individual byte of the string, so the
following is quite permissable;
#include<stdio.h>
int main()
{
char name[50];
strcpy(name,"Servile Software");
printf("\nName equals %s",name);
/* Replace first byte with lower case 's' */
name[0] = 's';
printf("\nName equals %s",name);
}
The ANSI standard on the C programming language defines the following
functions for use with strings;
char *strcat(char *dest, char *source) Appends string source to the end of
string destination.
char *strchr(char *s, int c) Returns a pointer to the first
occurence of character 'c' within s.
int strcmp(char *s1, char *s2) Compares strings s1 and s2 returning
< 0 if s1 is less than s2
== 0 if s1 and s2 are the same
> 0 if s1 is greater than s2
int strcoll(char *s1, char *s2) Compares strings s1 and s2 according
to the collating sequence set by
setlocale() returning
< 0 if s1 is less than s2
== 0 if s1 and s2 are the same
> 0 if s1 is greater than s2
char *strcpy(char *dest, char *src) Copies string src into string dest.
unsigned strcspn(char *s1, char *s2) Returns the length of string s1 which
consists entirely of characters not in
string s2.
unsigned strlen(char *s) Returns the length of string s.
char *strncat(char *dest, char *src, Copies at most 'len' characters from
unsigned len) string src into string dest.
int strncmp(char *s1, char *s2, Compares at most 'len' characters from
unsigned len) string s1 with string s2 returning
< 0 if s1 is less than s2
== 0 if s1 and s2 are the same
> 0 if s1 is greater than s2
char *strncpy(char *dest, char *src, Copies 'len' characters from string
unsigned len) src into string dest, truncating or
padding with zero bytes as required.
char *strpbrk(char *s1, char *s2) Returns a pointer to the first
character in string s1 which occurs in
string s2.
char *strrchr(char *s, int c) Returns a pointer to the last
occurence of 'c' within string s.
unsigned strspn(char *s1, char *s2) Returns the length of the initial
segment of string s1 which consists
entirely of characters in string s2.
char *strstr(char *s1, char *s2) Returns a pointer to the first
occurence of string s2 within string
s1, or NULL if string s2 is not found
in string s1.
char *strtok(char *s1, char *s2) Returns a pointer to the token found
in string s1 which is defined by
delimiters in string s2. Returns NULL
if no tokens are found.
The ANSI standard also defines various functions for converting strings into
numbers and numbers into strings.
Some C compilers include functions to convert strings to upper and lower case,
but these functions are not defined in the ANSI standard. However, the ANSI
standard does define the functions; toupper() and tolower() which return an
integer parameter converted to upper and lowercase respectively. By using
these functions we can create our own ANSI compatible versions;
#include<stdio.h>
void strupr(char *source)
{
char *p;
p = source;
while(*p){
*p = toupper(*p);
p++;
}
}
void strlwr(char *source)
{
char *p;
p = source;
while(*p){
*p = tolower(*p);
p++;
}
}
int main()
{
char name[50];
strcpy(name,"Servile Software");
printf("\nName equals %s",name);
strupr(name);
printf("\nName equals %s",name);
strlwr(name);
printf("\nName equals %s",name);
}
C does not impose a maximum length which a string may be, unlike other
computer languages. However, some CPUs impose restrictions on the maximum size
a block of memory can be. For example, the 8088 family of CPUs impose a limit
of 64K bytes on a segment of memory.
An example program to reverse the order of characters in a string.
#include <stdio.h>
#include <string.h>
char *strrev(char *s)
{
/* Reverses the order of all characters in a string except the null */
/* terminating byte */
char *start;
char *end;
char tmp;
/* Set pointer 'end' to last character in string */
end = s + strlen(s) - 1;
/* Preserve pointer to start of string */
start = s;
/* Swop characters */
while(end >= s){
tmp = *end;
*end = *s;
*s = tmp;
end--;
s++;
}
return(start);
}
main()
{
char text[100];
char *p;
strcpy(text,"This is a string of data");
p = strrev(text);
printf("\n%s",p);
}
TIME
C provides a function, time(), which reads the computer's system clock to
return the system time as a number of seconds since midnight on January the
first, 1970. However, this value can be converted to a useful string by the
function ctime() as illustrated in the following example;
#include <stdio.h>
#include <time.h>
int main()
{
/* Structure to hold time, as defined in time.h */
time_t t;
/* Get system date and time from computer */
t = time(NULL);
printf("Today's date and time: %s\n",ctime(&t));
}
The string returned by ctime() is comprised of seven fields;
Day of the week,
Month of the year,
Date of the day of the month,
hour,
minutes,
seconds,
century of the year
terminated by a newline character and null terminating byte. Since the fields
always occupy the same width, slicing operations can be carried out on the
string with ease. The following program defines a structure 'time' and a
function gettime() which extracts the hours, minutes and seconds of the
current time and places them in the structure;
#include <stdio.h>
#include <time.h>
struct time{
int ti_min; /* Minutes */
int ti_hour; /* Hours */
int ti_sec; /* Seconds */
};
void gettime(struct time *now)
{
time_t t;
char temp[26];
char *ts;
/* Get system date and time from computer */
t = time(NULL);
/* Translate dat and time into a string */
strcpy(temp,ctime(&t));
/* Copy out just time part of string */
temp[19] = 0;
ts = &temp[11];
/* Scan time string and copy into time structure */
sscanf(ts,"%2d:%2d:%2d",&now->ti_hour,&now->ti_min,&now->ti_sec);
}
int main()
{
struct time now;
gettime(&now);
printf("\nThe time is %02d:%02d:%02d",now.ti_hour,now.ti_min,now.ti_sec);
}
The ANSI standard on C does actually provide a function ready made to convert
the value returned by time() into a structure;
#include <stdio.h>
#include <time.h>
int main()
{
time_t t;
struct tm *tb;
/* Get time into t */
t = time(NULL);
/* Convert time value t into structure pointed to by tb */
tb = localtime(&t);
printf("\nTime is %02d:%02d:%02d",tb->tm_hour,tb->tm_min,tb->tm_sec);
}
The structure 'tm' is defined in time.h as;
struct tm{
int tm_sec;
int tm_min;
int tm_hour;
int tm_mday;
int tm_mon;
int tm_year;
int tm_wday;
int tm_yday;
int tm_isdst;
};
HEADER FILES
Function prototypes for library functions supplied with the C compiler, and
also standard macros are declared in header files.
The ANSI standard on the C programming language lists the following header
files;
Header file Description
assert.h Defines the assert debugging macro
ctype.h Character classification and conversion macros
errno.h Constant mnemonics for error codes
float.h Defines implementation specific macros for dealing with
floating point mathematics
limits.h Defines implementation specific limits on type values
locale.h Country specific parameters
math.h Prototypes for mathematics functions
setjmp.h Defines typedef and functions for setjmp/longjmp
signal.h Constants and declerations for use by signal() and raise()
stdarg.h Macros for dealing with argument lists
stddef.h Common data types and macros
stdio.h Types and macros required for standard I/O
stdlib.h Prototypes of commonly used functions and miscellany
string.h String manipulation function prototypes
time.h Structures for time conversion routines
Servile Software
5 Longcroft Close
Basingstoke
Hampshire
RG21 1XG
England
0256 478576