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

Making the C-to-Fortran Connection


AUG89: MAKING THE C-TO-FORTRAN CONNECTION

Mike is a technical editor for DDJ and can be contacted at 501 Galveston Drive, Redwood City, CA 94063. On CompuServe 76703,4057, or on MCI Mail as MFLOYD.


If you've ever had to make a connecting flight to get to your final destination, you know it takes more than just getting off one plane and onto another. Among other things, someone has to check that your baggage follows, your arriving flight has to be on time, and you have to get to the right gate at the right time.

Making the C-to-Fortran connection is much the same. As a programmer, you must ensure that arguments get from one language to the next, and that any excess baggage is handled. Ultimately, you gain the benefits of both languages. With huge Fortran libraries such as IMSL and the wealth of available third-party C tools, there's no telling how far you'll be able to take it.

This article shows you how to call a Lahey FORTRAN subprogram from a C main program, and how to call a C function from a Fortran program. For the purposes of this article, I'm using Borland's Turbo C 1.5 with Lahey's F77L. This connection works equally well with other C compilers, including Microsoft C.

Calling Fortran from C

The procedure for calling Fortran subprograms varies slightly between the different versions of C. In particular, Microsoft C main programs cannot call F77L subroutines and functions directly. Instead, you must create a main program in F77L that immediately calls the MSC main( ) as if it was a subprogram. Once loaded, main( ) can call the appropriate Fortran subprograms. Turbo C and Lattice C, on the other hand, can call F77L subprograms directly.

Because F77L references data by address, all arguments passed by the C program must be pointers. In the case of numerical constants, for instance, it is necessary to assign the constant's value to a C variable and reference the variable's address. Table 1 provides a list of C data types that may be passed to F77L along with their corresponding Fortran data types.

Table 1: Data types that can be passed from C functions to Lahey FORTRAN subprograms

     C Type                                Fortran Type
-----------------------------------------------------------------
     int *                                 INTEGER*2
     long int *                            INTEGER*4
     float *                               REAL*4
     double *                              REAL*8 (double precision)

     float[2] (real portion is stored in   COMPLEX
               the first array element)
     Struct Complex *                      COMPLEX
     double[2] (real portion is stored in  COMPLEX*16
                the first array element)
     Struct DoubleComplex*                 COMPLEX*16
     char *                                LOGICAL*1
     struct CHARACTER {                    CHARACTER*(*)
         char *text;
         int length;
     }

In addition, Fortran handles strings differently than C does. In F77L, CHARACTER*(*) refers to a descriptor structure that contains the address (4 bytes) and size (2 bytes) of the string. The string size, however, may or may not be specified at compile time. Therefore, the CHARACTER*(*) declaration must indicate that a variable-length string is being passed, the length of which is determined by the caller. Example 1 shows how to pass a string, as well as an integer and a real, from C to F77L.

Example 1: Passing a string from C to F77L

  /* Shows how to pass integer, float, and string parameters
     from C to F77L. */

  typedef
     struct {
       char *text;
       int length;
     } Str;

  extern void fortran_sub (int *, float *, Str *);

  main()
  {
    int number;
    float realNumber;
    Str charStr;

    number = 20;
    realNumber = 23.9;
    charStr.text = "Passing a text string";

    fortran_sub (&number, &realNumber, &charStr);
  }

  c
  c   Fortran subroutine to take three arguments (integer, real,
  c   and character string) passed from C, and print the results.
  c
        SUBROUTINE FORTRAN_SUB (IntVal, RVal, CharStr)
           BCEXTERNAL FORTRAN_SUB
           INTEGER*2 IntVal
           REAL RVal
           CHARACTER*(*) CharStr

           PRINT *, Intval, RVal, CharStr
        END

On the Fortran side of Example 1, notice the BCEXTERNAL statement immediately following the subroutine header. Lahey provides three external statements, BCEXTERNAL, MSCEXTERNAL, and LCEXTERNAL, to resolve C's external references to names at link time. Your brand of C determines which external statement you'll use. Example 1 assumes that Turbo C is being used. Replace the BCEXTERNAL statement with MSCEXTERNAL if you're using Microsoft C, or LCEXTERNAL if you're using Lattice C.

One of the real strengths of the Fortran Language is its ability to handle complex numbers. It's an ideal time to think of calling an F77L subprogram from C. The C-to-F77L interface supports two complex data types; COMPLEX and COMPLEX*16. The corresponding C structure for a single precision complex number is as follows:

  struct Complex {
      float real;
      float imaginary;
}

Double precision complex numbers are handled using the following C structure:

  struct DoubleComplex {
      double real;
      double imaginary;
}

Alternatively, you can reference complex numbers using a two-element array where the first element holds the real portion and the second element contains the imaginary portion of the complex number.

You can also access the return value of a Fortran function from your C programs. In C, you do this by passing the address of where the function value is to be stored as the first parameter in the call. Example 2 demonstrates how to access the return value of an F77L function.

Example 2: Accessing the return value of an F77L function

  /* Shows how to access the return value of a Fortran function. */

  extern void cube(int *, int *);

  main()
  {

     int InVal, ReturnVal;
     InVal = 2;

     cube (&ReturnVal, &InVal);
  }

  c
  c Fortran function to calculate the cube of a given number.
  c
      FUNCTION CUBE (X)
         BCEXTERNAL CUBE

         INTEGER*2 X, CUBE
         CUBE = X*X*X
      END

Going the Other Way

As you might have guessed, it's also possible to call C routines from F77L. There are, however, a few considerations in designing the Fortran program. First, an external statement must declare the names of all C functions used in the module. This guarantees that F77L will use the C calling conventions. In Addition, the external statement tells the compiler to prefix each function name with an underbar to correctly resolve externals at link time. Therefore, be sure to leave the "Generate Underbars" switch on (the default for both MSC and TC) when compiling on the C side.

Passing arguments from F77L to a C function can be a little tricky, so watch out! The first potential problem is that C does not check to see if the correct number of arguments have been passed to it. An illegal number of parameters can have unpredictable results, and it is the programmer's responsibility to check this.

Also, C gives you the option of passing by value or by reference. Because you are working with a copy, passing by value in a sense puts up a "fire wall" that prevents you from incorrectly altering the actual value of an argument. You lose this benefit in the Fortran-to-C connection because F77L requires that all arguments be passed by reference. Lahey has therefore included the CARG function to allow the Fortran program to pass most arguments by value. Table 2 provides a list of Fortran data types that can be passed to C.

Table 2: Data types that can be passed from Lahey FORTRAN subprograms to C functions

     Fortran Type                    C Type
---------------------------------------------------------------------------
     INTEGER*2                       int *
     CARG(INTEGER*2)                 int
     INTEGER*4                       long int *
     CARG(INTEGER*4)                 long int
     REAL*4                          float *
     CARG(REAL*4)                    float (double pushed on stack)
     REAL*8(double precision)        double *
     CARG(REAL*8)                    double
     COMPLEX                         struct complex *

     COMPLEX                         float[2](real portion stored in first
                                     array element)

     COMPLEX*16                      struct

     COMPLEX*16                      double[2]

     LOGICAL*1                       char * (Boolean value)
     LOGICAL*4                       --
     CHARACTER                       char[]
     CARG(CHARACTER)                 char[] (null-terminated)
     label                           --
     EXTERNAL                        --

You should be aware of a couple of restrictions. Most importantly, CARG cannot be used with C function return values. Also, CARG cannot be used with LOGICAL*1 nor COMPLEX data types.

You'll find CARG useful in one particular case. As mentioned earlier, F77L references a string through a descriptor structure, while C expects a pointer to a null-terminated sequence of characters. But F77L does not include a null byte at the end of a string. You can, however, use CARG to create a null-terminated copy of the string and pass it by value.

One final consideration involves the use of the prototypes in C. Because F77L defines both single- and double-precision arguments as DOUBLE, floats must be handled differently. For example, consider the following prototype:

  void func (double x)

In order to handle a float instead of a double, the definition for func must be changed to:

  void func(x)
      float x;

It is important to note that the following definition is not allowed in the Fortran-to-C connection:

  void func(float x)

Arrays

The difference in the way the two languages handle arrays can be the cause for some confusion. In particular, C and Fortran treat array subscripting and sequential order differently. C begins its subscripting at element 0 while Fortran normally begins subscripting at element 1. F77L, however, allows you to create arrays that are 0 relative. Therefore, you must remember to declare all appropriate arrays to begin at element 0.

In addition, arrays in F77L behave much like the pointers in a parameter list. For example, consider the following code fragment:

  BCEXTERNAL
  INTEGER*2 I(0:9)

       ...
   CALL C_FUNCTION(I, I(3))

In C, the corresponding definition is as follows:

  void c_function(i, i3)
      int i[10], *i3;       ...

C and Fortran also handle multi-dimensional arrays differently. Fortran stores arrays in column-major format while C stores arrays in row-major format. The solution is to simply reverse the subscripts in one of the two languages. For example, consider the following two-dimensional array declared in Fortran:

  integer*2 A(3:0, 4:0)

The array is sequentially referenced as A(0, 0), A(1, 0), A(2, 0), A(0, 1), A(1, 1), A(2, 1), and so on. To ensure compatibility, the corresponding C definition must be as follows:

  int A[4][3];

Obviously, the language you choose to reverse subscripts in is not important. To avoid confusion, however, you may wish to choose one language to consistently switch subscripts throughout all of your programs. That way you will never doubt whether your arrays are row-major or column-major.

File I/0

You must also be aware of differences in the way files are handled between the two languages. F77L, in particular, adds header and indexing information to a file. Normally this information is transparent to the programmer. C, on the other hand, adds no such header nor indexing information. In addition, F77L uses unit numbers to access files, while C uses file handles and stream pointers. The heuristic, then, is to open, read, write, and close a file all from within the same language. The exception, of course, is the standard device I/O performed on unformatted files. In this case, it is important to remember that a given file cannot be opened simultaneously by both languages.

Who's the Boss?

When it's time to compile and link your modules together, the first question you should ask yourself is "who's the boss?"

The language in which the main program is written is the boss. The boss controls the environment that your program will run in. Environmental considerations include how memory is managed and language calling conventions. When the main program is written in C, all of the F77L runtime routines are available except SYSTEM and CHAIN. Of course, all of the C runtime routines are available. When the main is written in F77L, all of the Fortran run-time routines are available. On the C side, functions not requiring the C environment are accessible. Some memory management functions such as malloc, calloc, and free are supported. Many others, including farmalloc and sbrc, are not.

Now it's time to bring the components of your program together into an executable file. F77L uses the large memory model, so remember to compile your C modules under the large model as well. This also means that far pointers are always used. And be sure that stack checking is turned off when compiling on the C modules. On the Fortran side, use the /NI switch to turn off interface checking. And because floating-point calculations are handled by C, the NDP compiler option is unnecessary. If you need floating-point emulation, select the /E option.

For the link step, Lahey provides a set of interface routines in two object modules that coordinate the different language environments. BCF77L is used when C is the boss, and F77LBC is used when Fortran is the boss. In addition, you'll have to include C's initialization module, COL, when C is the boss. No matter who the boss is, you'll always include both languages' run-time libraries. The connection relies on C's libraries for floating-point calculations, so you'll either use C's floating point or emulation library. I've included the link command line for the C to F77L connection at the top of Listing One and the F77L to C connection at the top of Listing Three.

Graphically Speaking

DOT.C (Listing One) is a Turbo C program that uses the Borland Graphics Interface (BGI) routines to randomly plot pixels on the screen. The C module calls FRAND.FOR (Listing Two) to generate the random locations.

The BGI initialization routine Initialize( ) was taken directly from Borland's BGIDEMO program, and demonstrates an easy method for supporting multiple hardware configurations.

Specifically, RANDDOT performs hardware detection and supports Hercules graphics, CGA, EGA, and VGA hardware. The interface demonstrates how both a Fortran subroutine and a function can be called from C.

After initializing the system to graphics mode, RandomDot( ) is called and the viewport settings [established by Initialize( )] are retrieved. Next, the Fortran subroutine SEED_RAND is called to get the initial seed value that will be used by F77L's random number generator. SEED_RAND calls RRAND, which references the system clock to generate the initial pseudorandom value. RRAND also generates and stores the seed value, which the random number generator function RND will later use. The initial value generated by RRAND is returned to the caller in RandomDot( ).

Next, a for loop is used to generate and plot 500 randomly placed (and colored) dots on the screen. The random values are gotten from the Fortran function FRAND which, in turn, calls RND. RND retrieves the seed value generated by RRAND. This process of seeding RND is completely transparent to the programmer, and is one of the side benefits of using the Fortran RND function as opposed to C's random( ) function. It is also worth noting that, from the C side, it is difficult to distinguish a subroutine from a function because the Fortran function must return its value as the first argument in the parameter list.

Sorted Partners

As a final example, consider SORT.FOR (see Listing Three). SORT uses RND to generate an array of random integer values that are then passed to C's qsort routine (see Listing Four) for sorting in ascending and descending order. Once sorted, the values are displayed and the user is prompted for a value to search for. The input value is passed to C's bsearch function and the results of the search are displayed. The sort could have been done using a bubble sort in Fortran, but a quick sort is much faster. C's bsearch function is used for similar reasons. Add to that C's ability to manipulate pointers, and things start moving significantly faster. Finally, qsort and bsearch are part of the run-time library, so there's no need to reinvent the wheel.

Final Note on Versions

As mentioned at the beginning of this article, I used Borland's Turbo C 1.5. TC 2.0 was not supported at the time of this writing, although Lahey plans to release a new version of F77L by the time this article reaches print. Lahey also supports Microsoft C 3.2 and up, although there are problems passing character lengths in MSC 4.0. The connection also works with early versions of Lattice C, but Lahey no longer supports that compiler. On the Fortran side, Lahey provides a 32-bit version of F77L (F77L-EM/32) that is compatible with Metaware's High C 386. On the low end, Lahey provides a student version, LP77, that is compatible with the C compilers mentioned earlier. LP77, however, requires routines from the LP77 toolkit, which is available separately from Lahey Computer Systems.

Author's Note: I want to thank everyone on the Lahey technical staff for their assistance while preparing this article.

Availability

All source code for articles in this issue is available on a single disk. To order, send $14.95 (Calif. residents add sales tax) to Dr. Dobb's Journal, 501 Galveston Dr., Redwood City, CA 94063, or call 800-356-2002 (from inside Calif.) or 800-533-4372 (from outside Calif.). Please specify the issue number and format (MS-DOS, Macintosh, Kaypro).

THE C-TO-FORTRAN CONNECTION by Michael Floyd

[LISTING ONE]

<a name="016c_0012">

/* DOT.C - uses Lahey FORTRAN's random number generator to randomly
      place pixels on the screen.
           This example demonstrates how to call F77L functions and
      subroutines from a C program.
           Uses Initialize() from Borland's BGIDEMO example to
      perform hardware detection, load the appropriate BGI
      driver and initialize the system to graphics mode.
           Link line using Borland's TLINK is as follows:
           link bcf77l.150+c0l+frand+do_frand,frand,,emu+mathl+cl+f77l
*/

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

char *DriverNames[] = {
  "Detect",
  "CGA",
  "EGA",
  "HercMono",
  "VGA"
};

struct PTS {
  int x, y;
};   /* Structure to hold vertex points   */

int    GraphDriver;      /* The Graphics device driver      */
int    GraphMode;      /* The Graphics mode value      */
double AspectRatio;      /* Aspect ratio of a pixel on the screen*/
int    MaxX, MaxY;      /* The maximum resolution of the screen */
int    MaxColors;      /* The maximum # of colors available   */
int    ErrorCode;      /* Reports any graphics errors      */
struct palettetype palette;      /* Used to read palette info   */

/*                           */
/*   Function prototypes                  */
/*                           */

extern void frand (int *, int *);
extern void seed_rnd (int *);
void Initialize(void);
void RandomDot(void);

/* Begin main() */

main()
{
  Initialize();         /* Set system into Graphics mode   */
  RandomDot();                  /* Place pixels at random locations     */
  closegraph();         /* Return the system to text mode   */

} /* End main() */

/*   INITIALIZE: Initializes the graphics system and reports      */
/*   any errors which occured.               */

void Initialize(void)
{
  int xasp, yasp;         /* Used to read the aspect ratio*/

  GraphDriver = DETECT;         /* Request auto-detection   */
  initgraph( &GraphDriver, &GraphMode, "" );
  ErrorCode = graphresult();      /* Read result of initialization*/
  if( ErrorCode != grOk ){      /* Error occured during init   */
    printf(" Graphics System Error: %s\n", grapherrormsg( ErrorCode ) );
    exit( 1 );
  }

  getpalette( &palette );      /* Read the palette from board   */
  MaxColors = getmaxcolor() + 1;   /* Read maximum number of colors*/


  MaxX = getmaxx();
  MaxY = getmaxy();                     /* Read size of screen */

  getaspectratio( &xasp, &yasp );   /* read the hardware aspect   */
  AspectRatio = (double)xasp / (double)yasp; /* Get correction factor   */

} /* End Initialize */

void RandomDot(void)
{
  int seed;
  int i, x, y, height, width, rand_val, color, temp;
  struct viewporttype vp;

  getviewsettings( &vp );
  height = vp.bottom - vp.top;
  width = vp.right   - vp.left;

  seed_rnd( &seed );     /* Seed F77L's Random # Gen. Output discarded */

  for( i=0 ; i<1000 ; ++i ){      /* Put 1000 pixels on screen   */
    temp = width - 1;
    frand( &rand_val, &temp );   /* Call F77L's RND function     */
    x = rand_val + 1;
    temp = height - 1;
    frand( &rand_val, &temp );
    y = rand_val + 1;
    frand( &rand_val, &MaxColors );
    color = rand_val;
    putpixel( x, y, color );
  } /* End for loop */

} /* End RandomDot() */





<a name="016c_0013"><a name="016c_0013">
<a name="016c_0014">
[LISTING TWO]
<a name="016c_0014">

c
c FRAND.FOR - Calls F77L's random Number generator RND.
c             Demonstrates how to call a FORTRAN function from C
c
c             Inputs :  None
c             Outputs: RETVAL

        FUNCTION FRAND(N)

        BCEXTERNAL FRAND
        INTEGER*2 N, FRAND

          FRAND = INT(RND() * N )
          RETURN
      END

c
c SEED_RND - Used to seed F77L's random Number generator.
c            Demonstrates how to call a FORTRAN subroutine from C
c
c            Inputs :  None
c            Outputs: RETVAL

      SUBROUTINE SEED_RND(RETVAL)

        BCEXTERNAL SEED_RND
        INTEGER*2 RETVAL

        RETVAL = INT(RRAND())
        RETURN
      END






<a name="016c_0015"><a name="016c_0015">
<a name="016c_0016">
[LISTING THREE]
<a name="016c_0016">

c
c     SEARCH.FOR uses rnd() to generate a list of random values
c     that are then passed to C's qsort routine for sorting in
c     ascending and descending order. Once sorted, the values
c     are dislayed and the user is prompted for a value to search
c     for. The input value is passed to C's bsearch function and
c     the results of the search are displayed
c
c     To link, use the following command line:
c
c     tlink f77lbc.150+search+do_srch,search,,emu+mathl+cl+f77l
c
      PROGRAM SEARCH

      BCEXTERNAL q_sort, bin_search
      INTEGER*2 A(0:20), B(0:20), C(0:20), I, J
      INTEGER*2 FOUND, bin_search, VAL, R

      DO 10 I = 0, 19
         A(I) = 0
         B(I) = 0
         C(I) = 0
10     CONTINUE

         R = rrand()
         PRINT *, R
      DO 20 I = 0, 19

         A(I) = 32767.0 * rnd()
         B(I) = A(I)
         C(I) = A(I)

20    CONTINUE

      PRINT *, 'Input    Ascending    Descending'
      PRINT *, 'MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM'
      call q_sort(A,20,0)
      call q_sort(B,20,1)
      DO 30 J = 0, 19
         PRINT 40, C(J), A(J), B(J)

30    CONTINUE
40    FORMAT(I6,I13,I13)
      PRINT *, 'Enter value to search for: '
      READ *, VAL
      FOUND = bin_search(A, 20, VAL)
      IF (FOUND .NE. 0) THEN
         PRINT *, VAL, ' found in list!'
      ELSE
         PRINT *, VAL, ' NOT found!'
      ENDIF

      END






<a name="016c_0017"><a name="016c_0017">
<a name="016c_0018">
[LISTING FOUR]
<a name="016c_0018">

/*      do_srch.c
**   q_sort()
**   This function will take a one dimensional array of length n and
**   integer width and will sort it in ascending or descending order.
**   Nothing is returned -- status always equals zero if any checking
**   is done by the FORTRAN calling program.
**               inputs: array      ptr to array
**                       length     number of elements in array
**                       order      0 = ascending
**                                  1 = descending
**               output: none
*/
#include <stdio.h>
#include <stdlib.h>

int q_sort (void *array,int *length,int *order);
int ascending (void *first,void *second);
int descending (void *first,void *second);
int bin_search (void *array, int *length, int *key);

int q_sort (void *array,int *length,int *order)
{
int   status = 0; /* return value */

qsort (array,(size_t) *length,sizeof (int),
 (int(*)(const void *,const void *)) ((*order == 0) ? ascending : descending));
return (status);
}   /* end of do_sort() */

/*   ascending()
**   This function is used by qsort and/or bsearch to return a
**      value based on the comparison of two inputs. qsort uses this
**      function to perform an ascending sort.
**      inputs: first    ptr to first element
**              second   ptr to second element
**      return:          result of comparison
*/

int ascending (void *p1, void *p2)
{
return ((*(int *) p1 < *(int *) p2) ? (-1) : (*(int *) p1 == *(int *) p2) ? (0) : (1));
}

/*   descending()
**   This function is used by qsort and/or bsearch to return a
**      value based on the comparison of two inputs. qsort uses this
**      function to perform a descending sort.
**      inputs: first    ptr to first element
**              second   ptr to second element
**      return:          result of comparison
*/

int descending (void *p1, void *p2)
{
return ((*(int *) p1 < *(int *) p2) ? (1) : (*(int *) p1 == *(int *) p2) ? (0) : (-1));
}

/*       bin_search()
**       This function takes a sorted FORTRAN array and a key and
**       attempts to locate the key value using C's bsearch(). The
**       function passes back the value if found, or 0 if not found.
**       inputs: array    ptr to an array
**               length   length of the array
**               key      ptr to a key value
**       return:          result of search
*/

int bin_search (void *array, int *length, int *key)
{
   int *ptr;

   ptr = (int *) bsearch(key, array, (size_t) *length, sizeof(int), ascending);
   return(ptr != NULL);

}

[EXAMPLE 1]


/* Passing a string to FORTRAN from a C main() */
typedef struct {
   char *text;
   int length;
} CHARACTER;

extern void f_subroutine(int *, float *, CHARCTER *);

main() {
   int ival;
   float fval;
   CHARACTER cval;

   cval.text = "contents of variable";
   cval.length = strlen(cval.text);
   f_subroutine(&ival, &fval, &cval);
} /* End of C example */

c
c   FORTRAN subroutine to accept and print a string
c
SUBROUTINE F_SUBROUTINE(I, F, C)
   BCEXTERNAL F_SUBROUTINE
   INTEGER*2 I
   REAL F

   CHARACTER*(*) C
   PRINT I, F, C
END


[EXAMPLE 2]

/* C module to get FORTRAN function return value */
extern void f_function(double *, double *);

c_module_main() {
   double, dval, return_val;

   f_function(&return_val, &dval);
}

c
c FORTRAN function to calculate the cube of the input number
c
function f_function(x)
MSCEXTERNAL f_function
double precision x, f_function

f_function = x * x * x
return
end












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.