Casting tells the compiler what a data type is, and can be
used to change a data type. For example, consider the following;
#include <stdio.h>
void main()
{
int x;
int y;
x = 10;
y = 3;
printf ("\n%lf",x / y);
}
The printf
() function has been told to expect a double. However, the compiler sees the
variables 'x' and 'y' as integers, and an error occurs! To make this example
work we must tell the compiler that the result of the expression x / y is a
double, this is done with a cast thus;
#include <stdio.h>
void main()
{
int x;
int y;
x = 10;
y = 3;
printf ("\n%lf",(double)(x / y));
}
Notice the data type 'double' is enclosed by parenthesis, and so is the expression to convert. But now, the compiler knows that the result of the expression is a double, but it still knows that the variables 'x' and 'y' are integers and so an integer division will be carried out. We have to cast the constants thus;
#include <stdio.h>
void main()
{
int x;
int y;
x = 10;
y = 3;
printf ("\n%lf",(double)(x) / (double)(y));
}
Because both of the constants are doubles, the compiler knows
that the outcome of the expression will also be a double.
Prototyping a function involves letting the compiler know in
advance what type of values a function will receive and return. For example,
lets look at strtok(). This has the prototype;
char *strtok(char *s1, const char *s2);
This prototype tells the compiler that strtok() will return a character pointer, the first received parameter will be a pointer to a character string, and that string can be changed by strtok(), and the last parameter will be a pointer to a character string that strtok() cannot change.
The compiler knows how much space to allocate for the return parameter, sizeof(char *), but without a prototype for the function the compiler will assume that the return value of strtok() is an integer, and will allocate space for a return type of int, that is sizeof(int). If an integer and a character pointer occupy the same number of bytes on the host computer no major problems will occur, but if a character pointer occupies more space than an integer, then the compiler wont have allocated enough space for the return value and the return from a call to strtok() will overwrite some other bit of memory. If, as so often happens the return value is returned via the stack, the results of confusing the compiler can be disastrous!
Thankfully most C compilers will warn the programmer if a call to a function has been made without a prototype, so that you can add the required function prototypes.
Consider the following example that will not compile on most
modern C compilers due to the nasty error in it;
#include <stdio.h>
int FUNCA(int x, int y)
{
return(MULT(x,y));
}
double MULT(double x, double y)
{
return(x * y);
}
main()
{
printf ("\n%d",FUNCA(5,5));
}
When the compiler first encounters the function MULT() it is
as a call from within FUNCA(). In the absence of any prototype for MULT() the
compiler assumes that MULT() returns an integer. When the compiler finds the
definition for function MULT() it sees that a return of type double has been
declared. The compiler then reports an error in the compilation saying something
like;
"Type mismatch in redclaration of function 'MULT'"
What the compiler is really trying to say is, prototype your functions before using them! If this example did compile, and was then run it probably would crash the computer's stack and cause a system hang.
C allows a pointer to point to the address of a function, and this pointer to be called rather than specifying the function. This is used by interrupt changing functions and may be used for indexing functions rather than using switch statements. For example;
#include <stdio.h>
#include <math.h>
double (*fp[7])(double x);
void main()
{
double x;
int p;
fp[0] = sin;
fp[1] = cos;
fp[2] = acos;
fp[3] = asin;
fp[4] = tan;
fp[5] = atan;
fp[6] = ceil;
p = 4;
x = fp[p](1.5);
printf ("\nResult %lf",x);
}
This example program defines an array of pointers to functions, (*fp[])() that are then called dependant upon the value in the indexing variable p. This program could also be written;
#include <stdio.h>
#include <math.h>
void main()
{
double x;
int p;
p = 4;
switch (p)
{
case 0 : x = sin(1.5);
break ;
case 1 : x = cos(1.5);
break ;
case 2 : x = acos(1.5);
break ;
case 3 : x = asin(1.5);
break ;
case 4 : x = tan(1.5);
break ;
case 5 : x = atan(1.5);
break ;
case 6 : x = ceil(1.5);
break ;
}
puts("\nResult %lf",x);
}
The first example, using pointers to the functions, compiles into much smaller code and executes faster than the second example.
The table of pointers to functions is a useful facility when writing language interpreters, the program compares an entered instruction against a table of key words that results in an index variable being set and then the program simply needs to call the function pointer indexed by the variable, rather than wading through a lengthy switch () statement.
One of the most dangerous pitfalls can occur with the use of gets(). This function accepts input from the stream stdin until it receives a newline character, which it does not pass to the program. All the data it receives is stored in memory starting at the address of the specified string, and quite happily overflowing into other variables! This danger can be avoided by using fgets() that allows a maximum number of characters to be specified, so you can avoid overflow problems. Notice though that fgets() does retain the newline character scanf() is another function best avoided. It accepts input from stdin and stores the received data at the addresses provided to it. If those addresses are not really addresses where the data ends up is anybodys guess!
This example is okay, since scanf() has been told to store the
data at the addresses occupied by the two variables 'x' and 'y'.
void main()
{
int x;
int y;
scanf("%d%d",&x,&y);
}
But in this example scanf() has been told to store the data at
the addresses suggested by the current values of 'x' and 'y'! An easy and common
mistake to make, and yet one that can have very peculiar effects.
void main()
{
int x;
int y;
scanf("%d%d",x,y);
}
The answer is, don't use scanf(), use fgets() and parse your string manually using the standard library functions strtod(), strtol() and strtoul().
Here is the basis of a simple input string parser that returns the individual input fields from an entered string;
#include <stdio.h>
#include <string.h>
void main()
{
char input[80];
char *p;
puts("\nEnter a string ");
fgets(input,79,stdin
);
/* now parse string for input fields */
puts("The fields entered are:");
p = strtok(input,", ");
while(p)
{
puts(p);
p = strtok(NULL,", ");
}
}
A preprocessor instruction, 'sizeof', returns the size of an
item, be it a structure, pointer, string or whatever. However! take care when
using 'sizeof'. Consider the following program;
#include <stdio.h>
#include <mem.h>
char string1[80]; char *text = "This is a string of
data" ;
void main()
{
/* Initialise string1 correctly */
memset(string1,0,sizeof(string1));
/* Copy some text into string1 ? */
memcpy(string1,text,sizeof(text));
/* Display string1 */
printf ("\nString 1 = %s\n",string1);
}
What it is meant to do is initialise all 80 elements of string1 to zeroes, which it does alright, and then copy the constant string 'text' into the variable 'string1'. However, variable text is a pointer, and so the sizeof(text) instruction returns the size of the character pointer (perhaps two bytes) rather than the length of the string pointed to by the pointer! If the length of the string pointed to by 'text' happened to be the same as the size of a character pointer then no error would be noticed.
The IBM PC BIOS and DOS contain functions that may be called by a program by way of the function's interrupt number. The address of the function assigned to each interrupt is recorded in a table in RAM called the interrupt vector table. By changing the address of an interrupt vector a program can effectively disable the original interrupt function and divert any calls to it to its own function. This was done by the critical error handler described in the section on error handling.
Borland's Turbo C provides two library functions for reading and changing an interrupt vector. These are: setvect() and getvect(). The corresponding Microsoft C library functions are: _dos_getvect() and _dos_setvect().
getvect() has the function prototype;
void interrupt(*getvect(int interrupt_no))();
setvect() has the prototype;
void setvect(int interrupt_no, void interrupt(*func)());
To read and save the address of an existing interrupt a
program uses getvect() thus;
/* Declare variable to record old interrupt */
void interrupt(*old)(void);
main()
{
/* get old interrupt vector */
old = getvect(0x1C);
.
.
.
}
Where 0x1C is the interrupt vector to be retrieved.
To then set the interrupt vector to a new address, our own function, we use setvect() thus;
void interrupt new(void)
{
.
.
/* New interrupt function */
.
.
.
}
main()
{
.
.
.
setvect(0x1C,new);
.
.
.
.
}
There are two important points to note about interrupts;
First, if the interrupt is called by external events then before changing the vector you MUST disable the interrupt callers using disable() and then re-enable the interrupts after the vector has been changed using enable(). If a call is made to the interrupt while the vector is being changed ANYTHING could happen!
Secondly, before your program terminates and returns to DOS you must reset any changed interrupt vectors! The exception to this is the critical error handler interrupt vector that is restored automatically by DOS, so your program needn't bother restoring it.
This example program hooks the PC clock timer interrupt to
provide a background clock process while the rest of the program continues to
run. If included with your own program that requires a constantly displayed
clock on screen, you need only amend the display coordinates in the call to
puttext(). Sincle the closk display code is called by a hardware issued
interrupt, your program can start the clock and forget it until it terminates.
/* Compile in LARGE memory model */
#include <stdio.h>
#include <dos.h>
#include <time.h>
#include <conio.h>
#include <stdlib.h>
enum { FALSE, TRUE };
#define COLOUR (BLUE << 4) | YELLOW
#define BIOS_TIMER 0x1C
static unsigned installed = FALSE;
static void interrupt (*old_tick) (void);
static void interrupt tick (void)
{
int i;
struct tm *now;
time_t this_time;
char time_buf[9];
static time_t last_time = 0L;
static char video_buf[20] =
{
' ', COLOUR, '0', COLOUR, '0', COLOUR, ':', COLOUR, '0', COLOUR,
'0', COLOUR, ':', COLOUR, '0', COLOUR, '0', COLOUR, ' ', COLOUR
};
enable ();
if (time (&this_time) != last_time)
{
last_time = this_time;
now = localtime(&this_time);
sprintf(time_buf, "%02d:%02d.%02d",now->tm_hour,now->tm_min,now->tm_sec);
for (i = 0; i < 8; i++)
{
video_buf[(i + 1) << 1] = time_buf[i];
}
puttext (71, 1, 80, 1, video_buf);
}
old_tick ();
}
void stop_clock (void)
{
if (installed)
{
setvect (BIOS_TIMER, old_tick);
installed = FALSE;
}
}
void start_clock (void)
{
static unsigned first_time = TRUE;
if (!installed)
{
if (first_time)
{
atexit (stop_clock);
first_time = FALSE;
}
old_tick = getvect (BIOS_TIMER);
setvect (BIOS_TIMER, tick);
installed = TRUE;
}
}
Links: Home C Programming Guide C++ programming Guide