[html,12pt,a4]article
C
[1]#1
safe-c
[2]#1#2
[1]"#1"
htmlonly
[1]"#1"
htmlonly
The User Guide
Barry McMullin
(Last Revised: 12th January 1996)
Introduction
is a module for safe (or, at least, safer)
programming in C. It provides a collection of functions,
with related data types and constants. These permit the
development of basic programs, without recourse to the
standard library. Instead, provides an insulating
layer between the novice programmer and the standard library
functions. adds a variety of additional runtime checks
to guard against typical novice errors, and provide intelligible
runtime failure reports.
is not intended to be applied in production
software. It is strictly a pedagogical stepping-stone,
to be displaced by direct use of the standard library when
the student is ready.
This document provides a User Guide to .
Specifically, it describes each constant, data type,
and function, provided by . It does not
provide any information on the rationale for the particular
features included in , nor does it discuss the internal
architecture or implementation of .
What is a Module anyway?
A module normally consists of three separate files:
A header file, normally having the extension .h .
This provides a specification of the external
interface to the module. That is, it specifies exactly how
other modules can invoke or use the facilities of this module.
A source file, normally having the extension .c
. This is the actual source code, or definition, of the module.
It specifies the internal implementation of the module.
An object file, normally having the extension
.o . This is the file produced by using the compiler
to translate the source code in the source file into the
specific, binary, instruction codes (so-called machine code)
which can be directly
processed by the CPU in the computer on which programs will be
executed.
A user of a module, who wishes to use its facilities in her
programs, needs access to the header file and the object file.
The header file is necessary so that, while the compiler
is translating
your source code, it can check that it is interfacing in a
correct and valid manner with the external module. The object
file is necessary so that a complete executable file can be
created by combining or linking together the object code from
your source file, with the object code of the external
module.
The user of a module does not require access to the
source code of that module. The source code is only required if
someone wants to modify the module in some way - correct
some defect, or add new facilities etc.
Using
As with any free-standing module, two separate issues or
problems arise in using the module:
How can the compiler discover, or be informed about, the
facilities offered by (the interface to it)?
This is necessary because, in order to compile your code,
which is supposed to mesh with the code, the compiler
must know what sort of hooks or connections are available in the
code.
How can the compiler know, or be told, to combine the
object code (compiled) version of the module, with the
compiled version of your code, in order to produce a
complete, executable, file?
The first problem, of telling the compiler what the allowed
interface to is, is solved by inserting the following
preprocessor directive close to the top of your source file,
before any other reference to entities:
The effect of this directive is that, during the compilation,
the file safe-c.h is searched for in certain
directories. Assuming it is found, then its text is incorporated
into the compilation exactly as it it had been part of your
source file. The file safe-c.h is the header file for
the module and, as such, contains declarations of all the
entities (constants, data types, functions) provided by .
Thus, when the compiler subsequently encounters references to
these entities in your code, it can recognise or decide
whether they are valid. For example, safe-c.h contains a
prototype for each function. This warns the compiler of
the existence, in the module, of the function, and also
specifies how many arguments (if any), of what types, the
function can accept. Then when the compiler encounters an
attempt to invoke the function, it can check whether the correct
number of arguments, of the correct types, are present.
It might seem that the second problem - that of subsequently
finding the compiled version of and combining it with the
compiled version of your own file, in order to produce the
complete executable program - simply should not arise. Surely,
if the compiler is told, by the inclusion of safe-c.h, that
your program makes use of facilities of , that should
prompt the compiler to also look for the compiled version of
, and incorporate it into the executable file?
However, few things in computer programming are as simple
as they seem.
The include
directive specifies that the text of another file must be
incorporated
into the translation phase of the compile - i.e. while
translating your source file into object code. It does not
stipulate anything about what object files should subsequently be
linked together to create the executable file. Indeed, it
must not. This is because, in the general case, there is no
simple or necessary one-to-one relationship between header files
and object files. So, in the general case, stipulations of what
header files to include during translation, and what object files
to combine during linkage, must be kept separate.
Specifying to the compiler to include the object file in
the linkage phase simply requires an additional parameter to be
placed in the compiler command line. However: there are a couple
of other special stipulations that also have to be made to the
compiler, and also a little postprocessing/tidying up to be done
after the compilation. Therefore, is accompanied by a
small batch file called scc.bat. A batch file
is simply a text file containing a series of DOS commands,
to be executed in sequence. So, if your file is called, say,
hello.c, then the command:
will actually cause a whole series of commands in
scc.bat to be executed. These will automatically invoke
the compiler in the correct way to incorporate the object
file into the executable file.
However: note that scc.bat is only appropriate for
compiling programs which make use of . When you progress
to the point of directly using the standard library,
then you must also learn how to directly invoke the compiler in
the appropriate way, rather than relying on scc.bat to do it.
Overview
introduces three distinct data types. A data type
is a particular format for storing information of some particular
kind. The three data types are:
booleantype: This only has two possible values,
denoted by the symbolic constants
TRUE and FALSE. It is used particularly when the
behaviour of the program is to depend on whether some condition
is satisfied - whether it is true or not.
integertype: For dealing with positive or negative
whole, or integer, numbers. This type cannot be used to
deal with non-integer numbers, such as fractions.
floatingpointtype: This is a special kind of format
which can handle non-integer, rational,
numbers (i.e. there is a decimal
point) but the position of the decimal point is not fixed. That
is, these numbers have a fixed degree of precision - a
fixed number of significant digits - but the decimal point can be
located anywhere. The decimal point is said to float.
Floating points formats are essentially similar to conventional
scientific notation such as .
stringtype: For storing strings of characters, of
variable length.
provides functions - or subprograms if you like -
to carry out various standard manipulations on objects of these
standard types. Specifically, there are functions to display
strings in the MS-DOS window, and to read strings in from
the keyboard; and to convert between string representations and
the internal binary representations of booleantype,
integertype and floatingpointtype. In this way,
objects of these types can be read in from the keyboard (as
stringtype), converted to one of the internal binary
representations, operated upon in any appropriate way (i.e.
performing boolean or arithmetic operations) and the results can
be displayed again.
Constants and Data Types
booleantype
Objects of booleantype may take on only logical or
boolean values. The two possible boolean values are denoted by
the symbolic constants TRUE and FALSE.
integertype
Objects of integertype may take on values representing
positive or negative integer numbers. Internally these are
stored in a 32-bit, 2's complement notation. This means that the
range of numbers that can be represented is approximately
. The maximum (most positive) and minimum
(most negative) values that can be represented are denoted by the
symbolic constants INTEGERMAX and INTEGERMIN.
Constants of integertype may be represented in your
programs in normal, decimal, notation, such as 42 or
-4561237 etc. You must not embed commas or spaces in
these constants - something like 1,000,000 would not be
acceptable. Nor (since these are integer values) should
you include a decimal point - not even if the fractional part is
given as zero. So, again, something like 45.0 is not
regarded as a valid integer value.
floatingpointtype
Objects of floatingpointtype may take on values representing
non-integer, rational, numbers. Internally these are
represented in so called floating point format. This is
somewhat like normal scientific notation - such as
. That is, there is a mantissa ( in this
example) and a separate exponent ( in this example).
The mantissa has a some finite range which, in effect, determines
the number of significant digits that can be represented. The
exponent also has a finite range which determines the maximum and
minimum absolute values which can be represented. Of course,
internally in the computer, both the mantissa and the exponent are
represented in binary notation; and the exponent is a power of 2
(normally) rather than a power of 10.
In the particular case of floatingpointtype, the mantissa is
equivalent to approximately 15 decimal digits; and the exponent
is equivalent to approximately .
Constants of floatingpointtype may be represented in your
programs in normal decimal notation, except that the decimal
point must be present, even if the fractional part is
intended as zero. Thus, 3.414 is a perfectly good
floatingpointtype value, whereas 24 is not - it
would be interpreted as an integertype value. If you
mean the floatingpointtype value corresponding to the
number 24, then code it as 24.0 instead. Very large or
small floatingpointtype constants can be represented using
scientific notation like this: -23.86e54 (for
) or 0.7433681e-123 (for
).
stringtype
Objects of stringtype consist of a sequence of arbitrary
characters, of arbitrary length, up to a certain maximum.
This maximum length is denoted by the symbolic constant
STRINGMAXLEN (which is of type integertype).
String constants are enclosed by double quote characters, and may
not be broken across multiple lines:
Within a string constant, certain special characters are denoted
by so-called escape sequences as follows:
n : This is the newline character.
When printed on the screen it moves the cursor down one line
and back to the left margin. When stored in a file it marks the
break between one line of text and the next.
" : This allows you to embed a
double quote character in a string (a double quote on its own
won't work because it would be mistaken for the end of the
string).
: Since a backslash character
plays a special role in introducing the escape sequences already
mentioned, there is a problem if you actually want to have a
backslash character itself in your string. The solution is
that a double backslash is treated as denoting literally that
you want a (single) backslash in the string.
Functions
putstring()
putstring() accepts exactly one argument which must be of
stringtype. This may be either a string
constant or a string variable. If it is a variable, its value
will not be changed by putstring().
putstring() outputs the given string on the standard
output stream, or stdout for short. By default, stdout
simply corresponds to the MS-DOS window
in which the program is
being executed. However, when the program is being
started, stdout can be redirected into a file on
disk. Suppose the executable program is called foo.exe;
then the following way of executing the program under DOS
would result in stdout being redirected to a file
called output.txt (any previous contents of this file
being overwritten):
putstring does not produce any return value.
getstring()
getstring() accepts exactly one argument
which must be of
stringtype, and must be a variable.
getstring() reads, or inputs, a string from the standard
input stream, or stdin for short, and stores it in the string
variable given as an argument. Exactly one line is read - that is,
reading of the string is terminated when a newline character is
encountered.
By default, stdin
simply corresponds to the keyboard. Whatever characters are
typed in are also echoed in the MS-DOS window in which the
program is being executed; pressing the Enter key generates
the newline character which terminates reading.
Alternatively, when the program is being
started, stdin can be redirected to read from a file on
disk. Suppose the executable program is called foo.exe;
then the following way of executing the program under DOS
would result in stdin being redirected to read from a file
called input.txt:
getstring() produces a return value of
booleantype. This will normally be the value
TRUE indicating that a string has been successfully read.
However, if, for some reason, a string cannot be read, the value
FALSE will be produced instead. The usual reason why this
might arise would be that stdin has been redirected to come
from a disk file, and the end of the file is encountered -
naturally, in that case, getstring cannot actually read
in a further string.
assignstring()
assign() accepts exactly two arguments, which must
both be of stringtype. The first must be a variable; the
second may be a variable or a constant.
assignstring() copies or assigns the value of the
second argument to the string variable specified by the first
argument. The previous value of that variable is overwritten.
assignstring() does not produce any return value.
stringequal()
stringequal() accepts exactly two arguments, which must
both be of stringtype. Each can be either a variable or
a constant. Neither will be altered by stringequal().
stringequal() produces a return value of
booleantype.
The two string arguments will be compared, character by
character. If both strings are exactly alike (including the
case of letters) then stringequal() will produce a
return value of TRUE; conversely, if there is
any difference between the two strings, stringequal()
will produce a return value of FALSE.
stringtofloatingpoint()
stringtofloatingpoint() accepts exactly one argument, which
must be of stringtype. This may be a variable or a
constant. However, in practise, it is normally a variable
(since, otherwise, a constant of floatingpointtype could
simply be used directly, instead of invoking
stringtofloatingpoint() to process a string constant).
In any case, if the argument is a variable, it will not be
altered by stringtofloatingpoint().
stringtofloatingpoint() produces a return value of
floatingpointtype.
stringtofloatingpoint() examines the string given as an
argument, and interprets it (if possible) as a
decimal (base 10) number, possibly in scientific notation (e.g.
2.3e5 would denote etc.). Providing that
the string
can be interpreted successfully in this way,
stringtofloatingpoint() will produce the internal binary
representation (i.e. of floatingpointtype) of this number as
the return value. If the string cannot be interpreted in
this way (e.g. rhubarb or forty-two or (01)
704-5000 etc.) then a runtime exception will be generated and
the program will terminate abnormally.
floatingpointtostring()
floatingpointtostring() accepts exactly two arguments. The
first must be a variable of stringtype. The second must
be of floatingpointtype, and may be either a constant or a
variable. However, in practise it will normally be a variable
(since, otherwise, a constant of stringtype could simply
be used directly, instead of invoking
floatingpointtostring()). In any case, if this second
argument is a variable, it will not be altered by
floatingpointtostring().
floatingpointtostring() takes the value of
floatingpointtype given by the second argument, and converts
it into a corresponding stringtype value, in decimal (base 10)
notation, and stores this string in the variable given as the
first argument.
floatingpointtostring() does not produce any return
value.
stringtointeger()
stringtointeger() accepts exactly one argument, which
must be of stringtype. This may be a variable or a
constant. However, in practise, it is normally a variable
(since, otherwise, a constant of integertype could
simply be used directly, instead of invoking
stringtointeger() to process a string constant).
In any case, if the argument is a variable, it will not be
altered by stringtointeger().
stringtointeger() produces a return value of
integertype.
stringtointeger() examines the string given as an
argument, and interprets it (if possible) as a
decimal (base 10) integer number. Providing that
the string
can be interpreted successfully in this way,
stringtointeger() will produce the internal binary
representation (i.e. of integertype) of this number as
the return value. If the string cannot be interpreted in
this way (e.g. rhubarb or forty-two or
3.414 etc.) then a runtime exception will be generated and
the program will terminate abnormally.
integertostring()
integertostring() accepts exactly two arguments. The
first must be a variable of stringtype. The second must
be of integertype, and may be either a constant or a
variable. However, in practise it will normally be a variable
(since, otherwise, a constant of stringtype could simply
be used directly, instead of invoking
integertostring()). In any case, if this second
argument is a variable, it will not be altered by
integertostring().
integertostring() takes the value of
integertype given by the second argument, and converts
it into a corresponding stringtype value, in decimal (base 10)
notation, and stores this string in the variable given as the
first argument.
integertostring() does not produce any return
value.
stringtoboolean()
stringtoboolean() accepts exactly one argument, which
must be of stringtype. This may be a variable or a
constant. However, in practise, it is normally a variable
(since, otherwise, a constant of booleantype could
simply be used directly, instead of invoking
stringtoboolean() to process a string constant).
In any case, if the argument is a variable, it will not be
altered by stringtodouble.
stringtoboolean() produces a return value of
booleantype.
stringtoboolean examines the string given as an
argument, and interprets it (if possible) as a string
representation of a boolean value. That is, the string
must have the value TRUE or FALSE - except that
leading or trailing whitespace will be ignored.
Providing that
the string
can be interpreted successfully in this way,
stringtoboolean() will produce the internal binary
representation (i.e. of booleantype) of this value as
the return value. If the string cannot be interpreted in
this way (e.g. rhubarb or True or wrong
etc.) then a runtime exception will be generated and
the program will terminate abnormally.
booleantostring()
booleantostring() accepts exactly two arguments. The
first must be a variable of stringtype. The second must
be of booleantype, and may be either a constant or a
variable. However, in practise it will normally be a variable
(since, otherwise, a constant of stringtype could simply
be used directly, instead of invoking
booleantostring()). In any case, if this second
argument is a variable, it will not be altered by
booleantostring().
booleantostring() takes the value of
booleantype given by the second argument, and converts
it into a corresponding stringtype value - i.e. either
the string TRUE or the string FALSE. It then
stores this string in the variable given as the
first argument.
booleantostring() does not produce any return
value.
clearscreen()
clearscreen() clears the MS-DOS window in which the
program is being executed, and places the cursor in the top left
position.
clearscreen() accepts no arguments, and produces no
return value.
setcursor()
setcursor() accepts exactly two arguments, both of
integertype. The first denotes a desired column number,
and must be in the range 1 to 80; the second denotes a desired
row (or line) number and must be in the range 1 to 25. These can
be constants or variables. If they are variables they will not
be altered by setcursor().
setcursor() moves the cursor of the MS-DOS window
in which the program is running to the specified column and row.
Nothing already displayed in the window will be modified. Column
1, row 1 is the top left position of the window.
If either the requested column or row is outside the allowed
range, setcursor() will generate a runtime exception and
the program will be terminated abnormally.
setcursor() does not produce any return value.
Copyright
This Hypermedia Document is copyrighted, 1994, 1995, by
Barry McMullinhttp://www.eeng.dcu.ie/7Emcmullin/home.html.
Permission is hereby granted to access, copy, or store this work, in
whole or in part, for purposes of individual private study only. The
work may not be accessed or copied, in whole or in part, for
commercial purposes, except with the prior written permission of the
author.