Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

C/C++

Going From K&R to ANSI C


AUG89: GOING FROM K&R TO ANSI C

Scott Robert Ladd is a full-time computer journalist, who lives in the mountains of Colorado. He can be contacted through MCI Mail (ID: 369-4376) or at 705 W Virginia, Gunnison, CO 81230.


Last September, the ANSI X3J11 committee finalized the ANSI standard for C and sent it up one level for final approval (which should occur this year). Culminating five years of study and discussion, the standard is becoming the basis for C compilers on many hardware platforms. In the MS-DOS world, every compiler has incorporated at least some of the ANSI features and many are coming close to full compliance with the standard.

The committee began with the classic "White Book" of C, Brian Kernighan and Dennis Ritchie's The C Programming Language, also known by the author's initials of K&R. This thin white book defined the C language and was used by both implementors and users of C. Unfortunately, it was an incomplete standard. Many decisions about the language's facilities were left up to the compiler implementor. As C became more popular, it became clear that certain aspects of the language caused problems when debugging or porting programs. Because of these factors, different C compilers included disparate extensions and features.

In designing a standard for C, the ANSI committee had several goals in mind. First, they wanted to maintain compatibility with K&R. This meant minimizing changes that would invalidate existing K&R-compatible programs. In addition, they wanted to codify and include common language extensions and programming practices. Finally, they wanted to improve the utility and portability of the language. This was not an easy task.

In the end they did an admirable job. While some old-line C programmers may grumble about having "their" language changed, the end result is that the ANSI standard is compatible with K&R and it enhances the language.

This article is not designed to be a complete tutorial on ANSI C. Its goal is to explain how ANSI has added to C, and what changes may affect existing programs and programming practices. Several new facilities have been added to the language and can be exploited to improve program functionality, readability, and maintainability. In spite of the committee's best efforts, there are some subtle changes that may occasionally catch the programmer who is unaware of them.

The article is divided into three sections that focus on the preprocessor, the language, and the standard library. In each section I'll discuss subtle changes and additions to that part of the language. Subtle changes are those that may "sneak" up on the unsuspecting programmer. Additions are new ANSI features that the programmer should be aware of.

Preprocessor

The preprocessor was changed significantly, and some of these changes can lead to problems when porting code between ANSI and K&R compilers. Many compiler vendors have not yet fully implemented the ANSI changes for fear that they would break existing code.

A new preprocessor operator has been added: defined(name). Supplied with the name of a macro, defined( ) returns a true value if that macro has been defined, or false if it has not. This duplicates the function of the ifdef directive but allows for the use of Boolean operators for checking the definition of multiple macros.

Two more preprocessor operators are new with ANSI: # and ## which affect the replacement of tokens in the replacement lists of macros. When a parameter in the replacement list of a function-like macro is preceded by a # operator, the corresponding argument is inserted at that point as a string literal. The ## operator concatenates adjacent tokens in the replacement list; these newly-built tokens are then available for further macro replacement.

Due to operating system differences, the ANSI standard does not define a specific search method for a file specified in a #include directive. K&R used Unix-style directory structures and file names when specifying how files were located, but many operating systems do not have similar or equivalent facilities.

Under K&R it was possible to define the same macro in several places. How this was handled was implementation-dependent -- most compilers merely used the last definition encountered. This practice can lead to problems, especially when the definitions are stored in header files. ANSI's solution was to disallow any macro redefinitions, unless they were identical.

K&R allowed for recursive macros that contained their own names in their expansion. This was a dangerous practice, leading to run-away preprocessing and logic problems. ANSI has made recursive macros illegal; the macro's own definition is ignored while it is being expanded. This change can cause problems in code, which relied upon recursive macros.

Two new directives were added: #error is used to output an error message during preprocessing. The standard strongly suggests that #error should also halt compilation. To pass configuration information to the compiler the #pragma directive was invented. Whatever follows the #pragma is interpreted in an implementation-defined manner. Any #pragmas that are not understood by the compiler are ignored.

Another facility added was the #elif (for else if) directive. It eliminates many of the massive if ... endif structures found in some programs.

Some compilers allowed white space around the #, introducing a preprocessor statement, but others did not. ANSI determined that the white space didn't interfere with anything, so they decided to allow it. This means that white space can exist on either side of the # and can be used to format nested preprocessor statements.

Language

The C language itself has undergone many changes. Most of the changes are additions, but some are clarifications of ambiguities in K&R or changes that may affect programming practices.

The keywords entry, fortran, and asm have been deleted by ANSI. No one ever used entry, and both fortran and asm were considered non-portable. ANSI does list fortran and asm in the common extensions, though.

An ANSI-conformant compiler must allow for case-sensitive internal identifiers, which can be unique through their 31st character. The original C compilers could only handle six to eight character identifiers. Because of linkages to other languages and older hardware architectures, however, external names are only required to be six characters long and are not case sensitive. In a future ANSI standard, the restriction on external names will be lifted. Programmers should be aware of this change because some older programs may rely upon a lesser number of significant characters in an internal identifier.

One area in which several changes have occurred is the declaration and definition of functions. Using a concept from C++, the ANSI committee added function prototypes to C. A prototype is a function declaration that defines the types of parameters for a function. For example

     int func1(char * str, unsigned int 1amount);

This statement declares that the function func1( ) returns an int and accepts two parameters: a char pointer and an unsigned integer. The identifiers str and amount are optional; their inclusion can improve the understanding of the nature of the function. When the compiler processes a call to func1( ), it can check to see if the types of the actual arguments match those specified in the prototype. This check prevents many common C errors, which occur when incorrect data types are passed to functions. It should be noted that this type checking can be circumvented through the use of proper casts.

Prototypes have made minor changes in how some parameters work. In K&R C, float values were automatically promoted to doubles when passed as parameters. Function prototypes can be used to force a float to be passed as a float. With the new rules that allow floats to be used in calculations without being promoted to doubles, library functions can be created to work entirely with floats. This eliminates the possible overhead of doubles when the extra precision is not needed.

The prototype style can also be used in function definition headers. This makes the headers resemble those from a Pascal or Modula-2 program. Under the new style func1( ) would be defined as

     int func1(char * str, unsigned int amount)
              {
        /* program code */
        }

whereas under the old style (still acceptable under ANSI) it would have been written as

    int func1(str, amount)
    char * str;
    unsigned int amount;
    {
    /* program code */
    }

Under ANSI, if the parameter list in a function prototype ends with ellipses, that function can accept a variable parameter list. This function

     int func2(int val,...);

accepts one int parameter (type-checked) and an implementation-defined number of untyped parameters. The macros defined in stdarg.h are used to extract the untyped parameters.

A function prototyped with an empty parameter list can accept any number of parameters of any type, and calls to it are not type-checked. You should be aware that many ANSI compilers enforce proper prototyping through warnings and errors. It is to your advantage to use the type-checking capabilities found in ANSI C.

ANSI has added some new types. The most significant of these additions is the void type. void represents the NULL set, in other words, an object of type void has no values. While this might seem to be a rather senseless type it does have some important uses.

For example, a function that does not return a value can be defined with a return value of void. This eliminates the old problem in K&R where these functions implicitly returned a random int, the value of which was meaningless. Also, a function can be prototyped with void in its parameter list, indicating that the function does not accept any parameters.

A void pointer is a pointer to anything. K&R C used char pointers for generic pointers. Any type of pointer can be assigned to a void * -- and vice versa -- without a cast. void pointers can be used to make functions that access generic-area memory more conveniently. Under K&R C, malloc( ) returned a char * pointer, which then needed to be cast to the pointer type it was assigned to. The ANSI version of malloc( ) returns a void *thereby eliminating the need for a cast.

The keyword signed has been added by ANSI. This was in response to the need to explicitly declare a signed char (or in this case, a very small integer) type on implementations where the default char type was unsigned. To keep the language consistent, signed is now allowed as a qualifier for all integral types. ANSI also made official the ability to apply the unsigned qualifier to long and short types.

A new floating-point type is the long double. It must be at least as precise as a double. Some MS-DOS implementations have already included this type in 10-byte IEEE format. long float is, however, no longer a valid synonym for double.

const and volatile are two new qualifiers. Adopted from C++, the const qualifier locks in the value of a data item, preventing its value from being modified. For example,

     const double pi = 3.1415927;

would prevent the value of pi from being changed. When used in a function prototype and definition, the const qualifier prevents the function from changing that argument. Several ANSI prototypes use const to safeguard the data accessed through pointer arguments.

Under some circumstances, a data item may be changed by forces outside the scope of a program. The volatile keyword was invented to inform the compiler that a value may change asynchronously. This is required for optimizing C compilers that can make assumptions about the value of a data item; volatile prevents these assumptions from being made.

The enumeration type has been in common use for years in some C dialects. Designated by the enum keyword, an enumerated type defines a special set of related integer values. For example

     enum rank (first, second, third);
     enum rank my_rank;

     my_rank = first;

The value of type rank can be one of three possible values, first, second, and third. first has an integral value of 0, second a value of 1, and third a value of 2. An enumerated value can be used anywhere an int can. The compiler can make checks to be sure that a value of type rank is assigned only those values listed in the definition of rank (known as enumeration constants).

By default, enumeration constants are assigned consecutive integral values beginning with zero. Explicit assignments can be made, however, to preset the values of the constants. The enumeration type coin shows this facility:

     enum coin (penny=1, nickel=5, dime=10, quarter=25);

Several changes have been made in numeric constants. Integral constants are automatically stored as int, long, or unsigned long values. The smallest integral type that can hold the constant is used. A new constant suffix, U(or u), can be used to specify that a constant is unsigned. As in K&R, a suffix of L(or l) can be used to force the constant into a long value.

Floating point constants are automatically stored as doubles, unless the constant has an F (or f) suffix. The F suffix forces the constant to be stored as a float.

Many pre-ANSI implementations of C extended the uses of structured types. These additions have been adopted in the ANSI standard, and structures may now be passed in function parameters by value. K&R did not allow this, and all structures had to be referenced by pointer parameters. In addition, ANSI allows functions to have structures as return values, and assignments can be made between structures of the same type.

Early C compilers allowed both the <operator>= and = <operator> forms of the short-cut assignment operators. The latter form is somewhat ambiguous because, for example, the = operator may indicate either subtraction or the assignment of a negative value. Therefore, the ANSI committee has specified the <operator>= format as the only valid forms of these operators.

You will find that there are several defined types in ANSI C. These types were created to aid in portability. The K&R version of the sizeof operator returned an int; ANSI's sizeof returns a value defined as size_t. size_t is defined as an implementation-specific integral type.

K&R defined a loose relationship between pointers and integers. Pointer values could be assigned to integers and back again. Comparisons between pointers and integers were allowed but the results from such comparisons varied from implementation to implementation.

The ANSI standard no longer defines integers and pointers as interchangeable. The only integral value that has any validity when compared to a pointer is 0. In fact, the standard explicitly defines a NULL pointer as having an integral value of 0. Programs that rely upon pointer arithmetic and point integer conversions may not be ANSI compatible.

The const qualifier can be used to control changes to the pointer and the data it points to. A const int*, for example, would be a pointer to a constant integer. The pointer can be changed but not the value it points to. The declaration int * const denotes an unmodifiable pointer to a changeable integer value.

The only significant change made by ANSI to executable statements affects the switch statement. Under K&R, only int values were allowed for the controlling expression, and now they may be of any integral type, including long and unsigned values.

Library

K&R did not specify a complete library for C. It discusses the standard I/O functions commonly found in stdio.h and several functions from the Unix library. The latter primarily consists of low-level I/O functions, which have been dropped from the ANSI standard for portability reasons. An example of a dynamic memory allocator is included but malloc( ) and company are absent. No math or string functions are discussed.

In order to promote portable programs, ANSI created a standard library and a standard set of headers to go with it. There are a total of 15 ANSI headers. Some functions that were defined by K&R in stdio.h are now found in other headers like stdlib.h. The following is a list of the headers and a short description of the functions found in them.

assert.h -- Diagnostics macro

The assert macro is commonly defined for most existing C implementations. It is used to place diagnostic tests in programs. assert accepts a single parameter. When the parameter is false (a zero value), assert displays the name of the source file and the current line number to the standard error device. The abort( ) function is then called.

ctype.h -- Character handling

These functions not only test characters to see if they are in a certain range but also change the case of certain characters. For example, the isalpha( ) function tests to see if its parameter is an alphabetic character. toupper( ) converts a lowercase letter to an uppercase one. The locale (see locale.h later in this text) setting can affect how these functions work. In K&R, these facilities were defined in stdio.h.

errno.h -- Standard errors

Several library functions return error code in a value called errno, which is defined in this header along with its possible values. These error values are highly implementation-dependent.

float.h -- Floating-point limits

There are several macros in this header that expand to values for limits and ranges for floating-point values.

limits.h -- Integer limits

This header is similar to float.h but defines limits and ranges for integral values.

locale.h -- Localization

In making C an international language, it became clear that a mechanism was needed to allow country- or location-specific information to be set. Things such as decimal point characters and currency characters change from place to place. The macros, structures, and functions found in locale.h help make writing portable programs easier.

math.h -- Mathematics

Mathematical functions have always been a part of C but until ANSI they were largely implementation-defined. This header declares functions for trigonometric, hyperbolic, logarithmic, and utility operations.

setjmp.h -- Non-local jumps

Using the setjmp( ) macro and the longjmp( ) function, a program can literally jump from any location within itself to any other. While the capability may violate the principles of structured program design, it can be useful when an exception condition occurs in a deeply-nested portion of a program.

signal.h -- Signal handling

A signal is an exception condition. The functions signal( ) and raise( ) provide a portable method for handling exceptions, such as program breaks and floating-point errors. These ANSI functions are based on those defined for Unix. raise( ) replaces the Unix kill( ) and eliminates the latter's support for multiprocessing. ANSI implemented a subset of the signals defined for Unix and allows each implementation to define signals of their own.

stdarg.h -- Variable arguments

Defined in this header are a set of macros that can be used to write portable functions accepting variable numbers of parameters. Many compilers provide a different set of macros based on the Unix V compile. In my experience, the ANSI macros are clearer and easier to use than the Unix macros.

stddef.h -- Common definitions

ANSI defined a number of standard types and macros most of them are here and some may be repeated in other headers as well. Among the definitions in stddef.h are: NULL, size_t(the return value type of the sizeof operator), and the offsetof macro. An invention of ANSI, offsetof is a macro that determines the byte-offset of a member within a structure.

stdio.h -- output

This header defines and prototypes most of the functions listed in Chapter 7 of K&R. Few, if any, changes were made in the definitions found in K&R but several new functions have been added. There are now functions for working with temporary files (with random, unique names). remove( ) deletes files and replaces the Unix-specific unlink( ). Using setvbuf( ) (borrowed from Unix V), the programmer can control the buffering of I/O streams. Two new file positioning functions, fgetpos( )and fsetpos( ), have been added to handle files longer than fseek( ) can cope with.

stdlib.h -- General utilities

This is the "kitchen sink" header, containing definitions and prototypes for a wide variety of unrelated functions. Included are numeric-to-string conversions: the malloc( ) family of memory allocation functions, random number functions, abort and exit facilities, binary search and quicksort functions, multibyte character functions (for working with foreign alphabets), and miscellaneous integer functions. Many existing compilers have their own headers for these functions, and you may need to determine how many of the non-standard headers are still required.

string.h -- String handling

Here is where you'll find the functions for manipulating strings. Most of these functions have been around as long as C has, and several are defined in K&R. Also included in the header are prototypes for the memory functions, which work similar to the string copy/move/set functions. The memory functions are designed for modifying memory through generic (non-character) pointers.

time.h -- Date and time

While date and time functions exist in most C libraries, there is almost no standardization between implementations. ANSI designed the functions declared in this header to provide a full spectrum of date and time facilities. Conversion functions are provided to convert integral times to structures to text strings.

Converting to ANSI may mean making minor changes in the library facilities you use and how you use them.

Conclusions

I hope that this overview gives you a sense of the effects the ANSI standard will have on your programming style and existing applications. Converting old programs to ANSI C should not be an arduous task. We should all be grateful to the X3J11 committee for the work that they have done; their standard will help move C into the 1990s.

BENCHMARKING TURBO C AND QUICKC
by Scott Robert Ladd

[GRIND.C]

/*
     Program:  Grind

     Version:  1.11
     Date:     26-Oct-1988

     Language: ANSI C

     Tests all aspects of a C compiler's functions, including disk i/o,
     screen i/o, floating point, recursion, prototyping, and memory
     allocation. It should be a large enough program to test the advanced
     optimizers in some compilers.

     Developed by Scott Robert Ladd. This program is public domain.
*/

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#define MAXFLOATS 1000

struct tabent
     {
     double val, vsum, vsqr, vcalc;
     };

struct tabent table[MAXFLOATS];

char *errmsg[] = {
                 "GRIND.TBL cannot be created",
                 "GRIND.TBL cannot be closed properly",
                 "GRIND.IN cannot be found",
                 "GRIND.IN has been truncated",
                 "GRIND.IN cannot be closed properly"
                 };

/* function prototypes */
short main(void);
void  readfloats(void);
void  sortfloats(void);
void  quicksort(struct tabent *, short, short);
void  maketable(void);
void  writetable(void);
void  error(short);

short main(void)
     {
     puts("\nGrind (C) v1.10  -- A Benchmark Program\n");

     readfloats();

     sortfloats();

     maketable();

     writetable();

     puts("\7End run GRIND!!!");

     return(0);
     }

void readfloats()
     {
     register short i;
     char buf[12];
     FILE *fltsin;

     printf("--> Reading in floats. At #     ");
     if (NULL == (fltsin = fopen("GRIND.IN","r")))
          error(2);

     for (i = 0; i < MAXFLOATS; ++i)
          {
          printf("\b\b\b\b\b%5d",i);
          if (NULL == fgets(buf,12,fltsin))
               error(3);
          table[i].val = atof(buf);
          }

     if (fclose(fltsin))
          error(4);

     printf("\n");
     }

void sortfloats()
     {
     puts("--> Sorting data");
     quicksort(table,0,MAXFLOATS-1);
     }

void quicksort(struct tabent * item,
               short left,
               short right)
     {
     register short i, j;
     struct tabent x, y;

     i = left;
     j = right;
     x = item[(i+j)/2];

     do
          {
          while (item[i].val < x.val && i < right) i++;
          while (x.val < item[j].val && j > left)  j--;
          if (i <= j)
               {
               y = item[i];
               item[i] = item[j];
               item[j] = y;
               i++;
               j--;
               }
          }
     while (i <= j);

     if (left < j)  quicksort(item,left,j);
     if (i < right) quicksort(item,i,right);
     }

void maketable()
     {
     register short i;
     double sum = 0.0;

     puts("--> Calculating table values");

     for (i = 0; i < MAXFLOATS; ++i)
          {
          sum = sum + table[i].val;
          table[i].vsum  = sum;
          table[i].vsqr  = table[i].val * table[i].val;
          table[i].vcalc = sqrt(fabs(table[i].val)) * log10(fabs(table[i].val));
          }
     }

void writetable()
     {
     FILE *fd;
     register short i;

     if (NULL == (fd = fopen("GRIND.TBL","w+")))
          error(0);

     puts("--> Writing Table to File");

     for (i = 0; i < MAXFLOATS; i = i + 10)
          {
          fprintf(fd,
                  "val = %5.2f, sum = %5.2f, sqr = %5.2f, calc = %5.2f\n",
                  table[i].val,
                  table[i].vsum,
                  table[i].vsqr,
                  table[i].vcalc);
          }

     if (fclose(fd))
          error(1);

     }

void error(short err_no)
     {
     printf("\n\7GRIND ERROR: %s\n",errmsg[err_no]);
     exit(err_no);
     }

[DMATH.C]

/*
     Benchmark: DMath
     Version:   1.00     23-Jan-1989

     Language:  ANSI C

     Computes all of the sines of the angles between 0 and 360 degrees using
     doubles.

     Developed by Scott Robert Ladd. This program is public domain.
*/

#define dabs(a) (((a) < 0.0) ? (-(a)) : (a))

/* conversion factor for converting radian to degrees */

#define deg2rad 57.29577951

/* prototypes */
void main(void);
double fact(double);
double power(double, double);

void main()
    {
    double angle, radians, sine, worksine, temp, k;

    for (angle = 0.0; angle <= 360.0; angle += 1.0)
        {
        radians = angle / deg2rad;
        k = 0.0;
        worksine = 0.0;

        do  {
            sine = worksine;
            temp = (2.0 * k) + 1.0;
            worksine += (power(-1.0,k) / fact(temp)) * power(radians,temp);
            k += 1.0;
            }
        while (dabs(sine - worksine) > 1E-8);
        }
    }

/* Note: this function is designed for speed; it ONLY works when n is integral */
double fact(double n)
    {
    double res;

    res = 1.0;

    while (n > 0.0)
        {
        res *= n;
        n -= 1.0;
        }

    return res;
    }

/* Note: this function is designed for speed; it ONLY works when p is integral */
double power(double n, double p)
    {
    double res;

    res = 1.0;

    while (p > 0.0)
        {
        res *= n;
        p -= 1.0;
        }

    return res;
    }


[FXREF.C]

/*
     Program:  FXREF (File Cross-Reference)

     Version:  1.10
     Date:     21-Sep-1988

     Language: ANSI C

     Reads a file from standard input, and sorts and organizes each
     token (word) found using a binary tree, keeping track of the number
     of occurences of each token and their location by line and column.
     It then prints a report to stdout.

     Released into the public domain for "educational" purposes.
*/

#include "stdio.h"
#include "string.h"
#include "ctype.h"
#include "stdlib.h"

/* type definitions */
typedef unsigned short line_no;

typedef struct loc_s
     {
     line_no line;
     struct loc_s * next;
     }
     location;

typedef struct tok_s
     {
     struct tok_s * less, * more;
     char * text;
     struct loc_s *loc, *last;
     }
     token;

token * root;

char * err_msg[] = {
                  "token table root",
                  "token text",
                  "location references",
                  "token record"
                  };

/* function prototypes */
int main(void);
void parse_tokens(char *, line_no);
void add_tree(char *, line_no);
token * find_tree(char *);
void show_tree(token *);
void error(short);

int main()
     {
     char buf[256];
     line_no line=0;

     if (NULL == (root = ( token *)malloc(sizeof( token))))
          error(0);

     root->less = NULL;
     root->more = NULL;
     root->text = NULL;
     root->loc  = NULL;

     while (NULL != (fgets(buf,256,stdin)))
          {
          ++line;
          printf("%5u: %s",line,buf);
          parse_tokens(buf,line);
          }

     printf("\x0C\n");

     show_tree(root);

     return 0;
     }

void parse_tokens(char * buf, line_no line)
     {
     char tok[256];
     line_no pos;

     while (1)
          {
          while ((!isalpha(*buf)) && (*buf != 0))
               ++buf;
          if (*buf == 0)
               return;
          pos = 0;
          while (isalpha(*buf))
               tok[pos++] = *buf++;
          tok[pos] = 0;
          add_tree(tok,line);
          }
     }

void add_tree(char * tok, line_no line)
     {
     token *temp_tok, *new_tok;
     location *temp_loc;
     short comp;

     if (root->text == NULL)
          {
          if (NULL == (root->text = (char *)malloc((unsigned)strlen(tok)+1)))
               error(1);
          strcpy(root->text,tok);
          if (NULL == (root->loc = ( location *)malloc(sizeof( location))))
               error(2);
          root->loc->line = line;
          root->loc->next = NULL;
          root->last = root->loc;
          return;
          }

     temp_tok = find_tree(tok);

     if (comp = strcmp(tok,temp_tok->text))
          /* comp is true (non-zero) if they don't match */
          {
          if (NULL == (new_tok = ( token *)malloc(sizeof( token))))
               error(3);
          if (NULL == (new_tok->text = (char *)malloc((unsigned)strlen(tok)+1)))
               error(1);
          new_tok->less = NULL;
          new_tok->more = NULL;
          strcpy(new_tok->text,tok);
          if (NULL == (new_tok->loc = ( location *)malloc(sizeof( location))))
               error(2);
          new_tok->loc->line = line;
          new_tok->loc->next = NULL;
          new_tok->last = new_tok->loc;
          if (comp < 0)
               temp_tok->less = new_tok;
            else
               temp_tok->more = new_tok;
          }
       else
          /* if comp is false (0), the tokens match */
          {
          if (NULL == (temp_loc = ( location *)malloc(sizeof( location))))
               error(2);
          temp_loc->line = line;
          temp_loc->next = NULL;
          temp_tok->last->next = temp_loc;
          temp_tok->last = temp_loc;
          }
     }

 token *find_tree(char * tok)
     {
     short comp;
     token *node;

     node = root;

     while (1)
          {
          if (0 == (comp = strcmp(tok,node->text)))
               return node;
          if (comp < 0)
               if (node->less == NULL)
                    return node;
                 else
                    node = node->less;
            else
               if (node->more == NULL)
                    return node;
                 else
                    node = node->more;
          }
     }

void show_tree(token * node)
     {
     location *lloc;
     short pos;

     if (NULL == node) return;

     show_tree(node->less);
     printf("%-32s: ",node->text);
     pos = -1;
     lloc = node->loc;
     while (lloc != NULL)
          {
          if (++pos == 7)
               {
               pos = 0;
               printf("\n%32s: "," ");
               }
          printf("%5d ",lloc->line);
          lloc = lloc->next;
          }
     printf("\n");
     show_tree(node->more);
     }

void error(short err_no)
     {
     printf("\nFXREF ERROR: Cannot allocate space for %s\n",err_msg[err_no]);
     exit(err_no+1);
     }

[GRAPH_QC.C]

/*
    Benchmark:  GRAPH_QC
    Version:    1.00    29-Jan-1989

    Language:   Microsoft QuickC v2.00

    Purpose:    Tests the speed of QuickC's graphics functions.

    Written by Scott Robert Ladd. Released into the public domain.
*/

#include "time.h"
#include "stdio.h"
#include "graph.h"
#include "stddef.h"

short main(void);
void initialize(void);
void draw_lines(void);
void draw_ellipses(void);
void draw_and_fill(void);
void finalize(void);

short max_x, max_y;
short mid_x, mid_y;

struct videoconfig scrn_cfg ;

clock_t start;
float   line_time, ellipse_time, fill_time;

short main()
    {
    initialize();

    draw_lines();
    draw_ellipses();
    draw_and_fill();

    finalize();

    return 0;
    }

void initialize()
    {
    _setvideomode(_ERESCOLOR);

    _getvideoconfig(&scrn_cfg);

    max_x = scrn_cfg.numxpixels - 1;
    max_y = scrn_cfg.numypixels - 1;

    mid_x = max_x / 2;
    mid_y = max_y / 2;
    }

void draw_lines()
    {
    short i, x, y;

    start = clock();

    for (i = 0; i <= 5; ++i)
        {
        if ((i % 2) == 0)
            _setcolor(_BRIGHTWHITE);
        else
            _setcolor(_BLACK);

        for (x = 0; x <= max_x; x += 4)
            {
            _moveto(x,0);
            _lineto(max_x - x,max_y);
            }

        for (y = 0; y <= max_y; y += 2)
            {
            _moveto(max_x,y);
            _lineto(0,max_y - y);
            }
        }

    line_time = (clock() - start) / CLK_TCK;

    _clearscreen(_GCLEARSCREEN);
    }

void draw_ellipses()
    {
    short x, y;

    _setcolor(_BRIGHTWHITE);

    start = clock();

    for (x = 6; x < mid_x; x += 6)
        for (y = 10; y < mid_y; y += 10)
            _ellipse(_GBORDER, mid_x - x, mid_y - y, mid_x + x, mid_y + y);

    ellipse_time = (clock() - start) / CLK_TCK;

    _clearscreen(_GCLEARSCREEN);
    }

void draw_and_fill()
    {
    short i, color;

    _moveto(0,0);

    _lineto(20,0);
    _lineto(30,20);
    _lineto(10,40);
    _lineto(10,50);
    _lineto(100,50);
    _lineto(100,52);
    _lineto(50,52);
    _lineto(50,54);
    _lineto(102,54);
    _lineto(102,10);
    _lineto(630,120);
    _lineto(500,150);
    _lineto(620,180);
    _lineto(510,200);
    _lineto(630,250);
    _lineto(400,340);
    _lineto(5,300);
    _lineto(0,0);

    _setfillmask(NULL);

    start = clock();

    for (i = 0; i < 4; ++i)
        {
        for (color = 1; color < 15; ++color)
            {
            _setcolor(color);
            _floodfill(mid_x, mid_y, _BRIGHTWHITE);
            }
        }

    fill_time = (clock() - start) / CLK_TCK;

    _clearscreen(_GCLEARSCREEN);
    }

void finalize()
    {
    _setvideomode(_DEFAULTMODE);

    printf("line    time = %.1f\n",line_time);
    printf("ellipse time = %.1f\n",ellipse_time);
    printf("fill    time = %.1f\n",fill_time);
    }


[GRAPH_TC.C]

/*

    Benchmark:  GRAPH_TC
    Version:    1.00    29-Jan-1989

    Language:   Borland Turbo C v2.0

    Purpose:    Tests the speed of QuickC's graphics functions.

    Written by Scott Robert Ladd. Released into the public domain.
*/

#include "time.h"
#include "stdio.h"
#include "graphics.h"

short main(void);
void initialize(void);
void draw_lines(void);
void draw_ellipses(void);
void draw_and_fill(void);
void finalize(void);

short max_x, max_y;
short mid_x, mid_y;

clock_t start;
float   line_time, ellipse_time, fill_time;

int driver = EGA,
    mode = EGAHI;

short main()
    {
    initialize();

    draw_lines();
    draw_ellipses();
    draw_and_fill();

    finalize();

    return 0;
    }

void initialize()
    {
    initgraph(&driver, &mode, "D:\\TC\\BGI");

    max_x = 639;
    max_y = 349;

    mid_x = max_x / 2;
    mid_y = max_y / 2;
    }

void draw_lines()
    {
    short i, x, y;

    start = clock();

    for (i = 0; i <= 5; ++i)
        {
        if ((i % 2) == 0)
            setcolor(WHITE);
        else
            setcolor(BLACK);

        for (x = 0; x <= max_x; x += 4)
            {
            moveto(x,0);
            lineto(max_x - x,max_y);
            }

        for (y = 0; y <= max_y; y += 2)
            {
            moveto(max_x,y);
            lineto(0,max_y - y);
            }
        }

    line_time = (clock() - start) / CLK_TCK;

    cleardevice();
    }

void draw_ellipses()
    {
    short x, y;

    setcolor(WHITE);

    start = clock();

    for (x = 6; x < mid_x; x += 6)
        for (y = 10; y < mid_y; y += 10)
            ellipse(mid_x, mid_y, 0, 360, x, y);

    ellipse_time = (clock() - start) / CLK_TCK;

    cleardevice();
    }

void draw_and_fill()
    {
    short i, color;

    moveto(0,0);

    lineto(20,0);
    lineto(30,20);
    lineto(10,40);
    lineto(10,50);
    lineto(100,50);
    lineto(100,52);
    lineto(50,52);
    lineto(50,54);
    lineto(102,54);
    lineto(102,10);
    lineto(630,120);

    lineto(500,150);
    lineto(620,180);
    lineto(510,200);
    lineto(630,250);
    lineto(400,340);
    lineto(5,300);
    lineto(0,0);

    start = clock();

    for (i = 0; i < 4; ++i)
        {
        for (color = 1; color < 15; ++color)
            {
            setfillstyle(SOLID_FILL,color);
            floodfill(mid_x, mid_y, WHITE);
            }
        }

    fill_time = (clock() - start) / CLK_TCK;

    cleardevice();
    }

void finalize()
    {
    closegraph();

    printf("line    time = %.1f\n",line_time);
    printf("ellipse time = %.1f\n",ellipse_time);
    printf("fill    time = %.1f\n",fill_time);
    }












Copyright © 1989, Dr. Dobb's Journal


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.