C++ Notes


This document is merely a set of notes I refer when I have a quick programming question. The notes originally were for C, and are only slowly being converted to refer to C++. It is not a tutorial, just a place I record information I wish I had recorded before!

TABLE OF CONTENTS


Arrays: Vectors

An array contains several items of the same data type. An array is declared by specifying its type, name, and number of entries:

        int vector[3];
      
A particular entry in an array is accessed by specifying its index, inside of square brackets. Array indices are zero based, so the three elements of VECTOR are "vector[0]", "vector[1]", and "vector[2]".
        for ( i = 0; i < 3; i++ )
        {
          vector[i] = i;
        }
      

An array literal value is simply the set of values, separated by commas and enclosed in curly brackets. Thus, it is possible to include an initial value for an array in a declaration statement:

        int vector[3] = { 1, 2, 3};
      
However, it is NOT possible to use such values in executable statements. If you suddenly decide that you want to set the values of vector at run time, you must do something like this:
        vector[0] = 1;
        vector[1] = 2;
        vector[2] = 3;
      

An array name, by itself, with no brackets, is essentially a pointer, or address of the first element. Thus, a routine that expects to receive two real numbers x and y, may be called as follows:

        sum = add ( vector[1], 17.0 );
        sum = add ( *(vector+1), 17.0 );
      
but you would NOT want to say anything like:
        sum = add ( vector, 17.0 );
      
which is actually trying to add an address and a real number.

If a subroutine receives a whole vector as an argument, and the size of the vector is also a variable, that's no big deal. You might do this:

        total = add ( vector, n );
        ...
        float add ( float x[n], int n )
        {
          t = 0.0;
          for ( i = 0; i < n; i++ )
          {
            t = t + x[i];
          }

          return t;
        }
      


Arrays: Matrices

A two dimensional array is thought of as a vector of vectors. It is declared by naming its type, name, and the number of rows and columns, each in a separate pair of square brackets.

        float matrix[2][3];
      
A matrix literal is the vector of row vectors. Thus, in a declaration, it is possible to initialize the entries:
        float matrix[2][3] = { { 1,1, 1.2, 1.3}, {2.1, 2.2, 2.3} };
      
or to assign the values in an executable statement:
        a = { { 1,1, 1.2, 1.3}, {2.1, 2.2, 2.3} };
      

C arrays are stored in "row-major" order, so entries of MATRIX begin with MATRIX[0][0], followed by MATRIX[0][1] and end with MATRIX[1][2].

A particular entry of an array is specified by giving its index (or row and column indices) in square brackets:

        matrix[1][2] = 2.3;
      

When passing a doubly dimensioned array to a function, the function must know its second dimension in order to properly index the elements. Thus, the most natural way to set this up is also clumsy, because you don't have the freedom to allow an arbitrarily shaped array:

        #define MAXROW 10
        #define MAXCOL 20

        float a[MAXROW][MAXCOL];

        nrow = 3;
        ncol = 4;
        double_mat ( a, m, n );

        double_mat ( float a[][MAXCOL], int m, int n ) {
 
          for ( i = 0; i < m; i++ )
          {
            for ( j = 0; j < n; j++ )
            {
              a[i][j] = 2.0 * a[i][j];
            }
          }
        }
      

The alternative is to use pointer arithmetic, keeping in mind that a doubly dimensioned array is a pointer to pointers (God help us). However, C is really really uncomfortable with this, so it turns out that a float** is not equivalent to a float[][]. A float** actually includes a hidden auxilliary set of pointers to the rows, and so you can't have the friendly float[][] data type in your main program, and handle a variety of such types by pretending that they are float** in the subroutine. Oh, I don't know, this is a terribly serious problem with C.

An example of setting up and using a float** would be helpful here.


Array Names As Arguments

When a (one dimensional) array is passed to a procedure, it may be declared within that procedure using empty brackets:

        # define NDIM 10

        int main ( void );
        int add ( int a[], int n);

        int main ( void )
        {
          int a[NDIM];
          int i;
          int sum;

          for ( i = 0; i < NDIM; i++ )
          {
            a[i] = i;
          }

          sum = add ( a, NDIM );
          cout << "The sum is " << sum << ".\n";

          return 0;
        }

        int add ( int a[], int n)
        {
          int i;
          int sum;

          sum = 0;
          for ( i = 0; i < n; i++ )
          {
            sum = sum + a[i];
          }
          return ( sum ) ;
        }
      

However, the name of the array can also be regarded as a POINTER to the first entry. With that approach, the program can be rewritten:

        # define NDIM 10

        int main ( void );
        int add ( int *a, int n );              <--

        int main ( void )
        {
          int a[NDIM];
          int i;
          int sum;

          for ( i=0; i < NDIM; i++ )
          {
            a[i] = i;
          }

          sum = add ( a, NDIM);
          cout << "The sum is " << sum << ".\n";

          return 0;
        }

        int add ( int *a, int n )               <--
        {
          int i;
          int sum;

          sum = 0;
          for ( i=0; i < n; i++)
          {
            sum = sum + *(a+i) ;               <--
          }
          return ( sum ) ;
        }
      


The AUTO Storage Property

AUTO is a storage class specifier (along with EXTERN, REGISTER, STATIC and TYPEDEF).

Objects declared AUTO have automatic storage class, and may be used only WITHIN a function, never shared by more than one function. An example of a data declaration:

        auto int chairs;
      
Contrast AUTO with STATIC.


Boolean Variables

C++ has added a Boolean data type called bool, whose only legal values are true and false.

        bool even;

        even = false;
        if ( mod ( n, 2 ) == 0 )
        {
          even = true;
        }
      
which is equivalent to
        bool even
        even = ( mod ( n, 2 ) == 0 );
      


The BREAK Statement

The BREAK statement can be used to exit from a DO...WHILE, FOR, SWITCH or WHILE statement. If BREAK occurs inside a nest of loops, it causes the innermost loop to be terminated.

        // Find the first entry of X that is 0.
  
        ival = 0;

        for ( i = 0; i < n; i++ )
        {
          if ( x[i] == 0.0 )
          {
            ival = i;
            break;
           }
        }
      

Compare the BREAK statement with the the CONTINUE statement, which simply terminates the current iteration of the loop, jumping to the beginning of the next iteration.


The Cast Operators

Explicit type conversion can be forced in any expresion with a unary operator called a CAST. A cast has the form

        (type-name) expression
      
and converts expression into a value of the given type-name according to a set of conversion rules.

For example:

        dble d;
        int n;
        float x;
        int y;

        y = (int) sqrt(17.0+x);

        d = sqrt( (double) n)
      


Character Variables

A character variable is a variable that can hold a character value.

A character value is thought of as the information corresponding to a single computer byte, that is, 8 bits. Depending on the situation, we may think of this as being a literal character, such as 'a', an unsigned (very short) integer between 0 and 255, or a signed (very short) integer between -128 and 127.

A character variable is declared as having type char:

        char alfa;
        char beta = 'b';
        char gamma = 67;
      

Since most bytes don't represent a printable character, there are special codes for some of them, such as "\n" for the newline character. Thus, you may also see assignments like:

        char nl = '\n'
      

Arithmetic can be performed on a character variable, because it is simply treated as a very short integer:

        char beta = 'b'
        char gamma;
        gamma = beta + 1;
      

To print a character as an integer, use the "\d" code; to print it as a character, use the "\c" code:

        char alpha = 'a';
        cout << "The integer value" << ( int ) alpha << 
                " is the character value '" << alpha << "'.\n";
      

There is a related datatype, called a string, which allows C to process words and text efficiently.


Comments

Comments can appear within a statement (although usually at the end), or on one or more separate lines. Comments begin with the special string "//" and extend to the end of the line.

Here are examples of "in line" comments and separate comments:

        // Search for zero entries in X.
        ival = 0;

        for ( i = 0; i < n; i++ ) {  // Loop through the elements.
          if ( x[i] == 0.0 )
          {
            ival = i;
            break;
           }
        }
        //
        //  If IVAL is still 0,
        //  then we know we did not find a zero entry.
        //

      


Complex Data

C++ now includes complex data types, which can be used once the appropriate include file is invoked. When a variable is declared to be complex, the user also specifies the specific kind of real numbers to be used for the components of the complex number. Complex literal values are handled in a simple way as a parenthesized pair of values. Complex numbers are properly handled by the basic arithmetic operators. The mathematical functions can be used with complex arguments, and some more functions needed for complex arithmetic have been added. Complex numbers are properly handled by the input and output operators, and by the operators that convert from one type to another.

The COMPLEX Include File

If your file is going to use complex numbers, you must include the appropriate definition file:


        # include <complex>
      

Complex Data Declarations

The complex data type is actually a family of data types; a complex number can naturally be thought of as a pair of real numbers, and the user has an explicit choice of what kind of real numbers will be used in this representation. The choices available are the standard float, double and long double types. A declaration uses the word complex followed by the chosen real number data type enclosed in angle brackets.

Examples of such declarations are:


        complex <float> phaser;
        complex <double> photon;
        complex <long double> tachyon;
      

Complex Literals

To set a complex value to zero, or any value without an imaginary part, you simply write an integer or real literal as you have always done:


        complex <float> dahlia = 0;
        complex <float> poinsettia;

        poinsettia = 17.34;
      
Of course, in these cases, the value on the right hand side is automatically converted before being assigned to the variable. The point is, if your right hand side isn't complicated, there is no need to write a complicated expression for it.

To specify a general complex value literally, you write two numbers in parentheses, separated by a comma, and preceded by the appropriate "cast" operator. If either value is an integer, you may write an integer there.


        imaginary_unit = complex <float> ( 0, 1 );
        root1 = complex <double> ( 5.1, -3.7 );
      

Warning: You might try to write a complex literal simply as a parenthesized pair. This will not work. Instead, the second value inside the parentheses will be assigned to the real part of the complex number! The assignment


        root1 = ( 5.1, -3.7 );  //  Wrong, don't do this!
      
will silently set root1 to the real value -3.7.

Arithmetic Operators

C++ will automatically be able to properly handle arithmetic statements involving complex variables. In particular, you can add, subtract, multiply and divide them:


        complex <float> a = complex <float>( 1.0, 2.0);
        complex <float> b = complex <float>( 3.0, -4.0);
        complex <float> c;

        c = ( 1.0 + a ) / ( b * b - a );
      

Math Functions

You can apply most mathematical functions to complex data. Here, we use the exp and abs functions to determine the accuracy of a partial Taylor series.


        complex <float> a = complex <float>( 1.0, 2.0);
        complex <float> b;
        complex <float> c;

        c = 1.0 + a / 2.0 + a * a / 6.0 + a * a * a / 24.0;
        b = abs ( exp ( a ) - c );
      

You can similarly apply functions like cos, sin, sqrt, tan with complex arguments.

Exponentiation uses the pow(base,power) function. When base is complex, power cannot be an int; it must be a floating point or complex value.

The math library is also extended to include the following functions:

Input/Output

The input and output operators have been extended to handle complex data.

A general complex number must be input with the parenthesis notation. If, however, the complex number has no imaginary part, then the parentheses can be dispensed with, and just the real part specified.

Output works in the usual way, and will print out a complex number including parentheses and separating comma.


        cout << " " << z << "\n";
      
or, if you want to control the number of digits in the real and imanginary parts, say:

        cout << "  " << setw(8) << z << "\n";
      
However, you may find it more convenient to work with the real and imaginary parts of a compler number sometimes. You might wish, then, to include the parentheses and comma yourself:

        cout << "  (" << setw(8) << real ( z )
             << ",  " << setw(8) << imag ( z ) << ")\n";
      


Conditional Expressions

(Rant): The ? operator is a worthless and confusing feature of C. The only reason to discuss it is that you have to be able to figure out what it's doing when you see it in someone else's code.

The ? operator can be thought of as a compact form of the following:

        if ( condition )
          z = expression_1;
        else
          z = expression_2;
      

Using the obscure ? operator, we can transform that clear code into the following:

        z = condition ? expression_1 : expression_2;
      


The CONST Property

CONST is a type qualifier, along with VOLATILE. A quantity declared to be CONST may be initialized, but not thereafter assigned to.

For example:

        const float pi = 3.14159265;
      


The CONTINUE Statement

The CONTINUE statement can be used to skip ahead to the next iteration of a DO...WHILE, FOR, or WHILE loop.

        // Invert every nonzero entry.

        ival = 0;

        for ( i = 0; i < n; i++ )
        {
          if ( x[i] == 0.0 )
          {
            continue;
           }
          x[i] = 1.0 / x[i];
        }
      

Compare the CONTINUE statement with the BREAK statement, which actually terminates execution of the loop.


The Date

Alas, there is no friendly "date" routine to return today's date. Instead, you have to go through various nonsense in order to retrieve the system time in seconds, convert it to a time structure, and convert that to a date string. Here is an example:

        # include <time.h>
        # include <sys/types.h>

        char       date_string[30];
        char      *ipoint;
        time_t     seconds;
        struct tm *time_struct;

        time ( &seconds );
        time_struct = localtime ( &seconds );
        ipoint = asctime ( time_struct );
        strcpy ( date_string, ipoint );
      


Data_Declarations

A declaration statement has the form:

        TYPE NAME;
      
where TYPE is a data type and NAME is a variable name. It's sometimes convenient to initialize the data item at the same time:
        TYPE NAME = VALUE;
      
where VALUE is a literal value of the appropriate type.

The simplest declarations use predefined scalar types:

        int x;
        float bubba;
        int charlie = 2;
        char pound = '#';
      

An array is declared by including the array limits in square brackets:

        char textline[80];
        int checkers[8,8];
      

A type statement may be prefaced by EXTERN or STATIC:

        extern int y;
        static float yurboat;
        extern static miranda;
      

The declaration

        int *x;
      
means that X is a pointer to an integer. That is, X is the address of an integer.

A variable may be initialized at its declaration:

        char esc = '\\';
        int i = 0;
        int limit = MAXLINE+1;
        float eps = 1.0e-5;
      


Data_Types

The basic data types of C are

char
a single byte, one character;
int
an integer
float
a single precision floating point value;
double
a double precision floating point value;

The qualifiers SHORT and LONG can be applied to INT's. Unfortunately, in a bow to the lazy, SHORT INT and SHORT, LONG INT and LONG are synonyms.

        short int e;
        long int f;
      

SIGNED and UNSIGNED may be applied to CHAR. SIGNED is the default.

        signed char g;
        unsigned char h;
      
Note that a character variable has a dual interpretation; sometimes we think of it as a character, such as 'a', 'b' or 'c', and sometimes we think of it as a (signed or unsigned) "small integer", such as 44.

UNSIGNED numbers are always positive or zero, and obey the laws of arithmetic modulo 2^n. SIGNED is the default.

        unsigned int i;
        unsigned short int j;
        unsigned long int k;
      

LONG DOUBLE specifies extended-precision floating point.

        long double l;
      

The headers <limits.h> and <float.h> contain symbolic constants for these sizes.


Defects

C does not include a MIN or MAX function. Of course, you could write your own, but then you'd have to write one for integers, one for SHORT integers, one for floats, one for doubles and so on. To get generic MAX and MIN routines that work for all data types, (though only for two arguments), try using the macro substitution feature of the preprocessor.

        #define MAX(a,b) ( (a)>(b) ? (a) : (b) )
        #define MIN(a,b) ( (a)>(b) ? (b) : (a) )
      
(Sorry, here's a case where the question mark operator is necessary.) This should allow you to say things like:
        z = MAX ( z, y );
        i = MIN ( j, k );
      


The DO WHILE Statement

The DO/WHILE loop differs from the FOR and WHILE loops because it evaluates its condition at the bottom of the loop, after it has executed the statements in the body of the DO loop.

        do
        {
          statements
        }
        while (expression);
      
If EXPRESSION is true, the DO loop is repeated.


The ELSE Statement

An ELSE statement is used to handle the "leftover" cases from a simple "IF" statement:

        if ( n == 1 )
        {
          n = n+1;
        }
        else
        {
          n = n-1;
        }
      

More complicated cases can be handled by "ELSE IF".


The ELSE IF Statement

        if ( n == 1)
        {
          n = n+1;
        }
        else if ( 0 <= n && n < 1 )
        {
          n = n+1;
        }
        else if ( n < 0 )
        {
          n = -n;
        }
        else
        {
          n = 2*n;
        }
      

Warning: It is a mistake to write "ELSEIF" as one word. C won't like it.


The ENUM Datatype

An ENUM statement is used in connection with enumerated data types.

The simplest statement essentially sets up a correspondence between the integers and a set of enumerated values:

        enum { HEARTS, CLUBS, DIAMONDS, SPADES};
      
can be regarded as equivalent to the preprocessor definitions
        #define HEARTS = 0
        #define CLUBS = 1
        #define DIAMONDS = 2
        #define SPADES = 3
      

A variable "SUIT", whose value can only be HEARTS, CLUBS, DIAMONDS or SPADES (or 0, 1, 2, and 3) could be defined by:

        enum suit { HEARTS, CLUBS, DIAMONDS, SPADES};
      
The symbolic names used in different enumerations must be distinct, which makes sense if you think about how you would do this with preprocessor commands.

For instance, to define a data type called "TF" whose possible values are truth and falsehold, we would write

        enum tf { truth, falsehood };
      

If we want a variable to be of type TF, then we next have to use a TYPEDEF statement:

        typedef  enum tf  tf_enum;
      
and now we can declare a variable to be of type tf_enum, and even initialize it:
        tf_enum fred = falsehood;
      

It is then legal to say things like:

        fred = truth;
      

Note that C does not remember the words "truth" and "falsehood", but essentially replaces "truth" by "0" and "falsehood" by "1" (and subsequent items in an enumeration by 2, 3, and so on). This means that an enumeration data item is essentially a nonnegative integer.

Also, if you use several enumeration data types, you are not allowed to use the same name in two different lists.


The EXIT Statement

The EXIT statement is actually a system call, not a part of C, but we'll ignore that. The formal declaration of EXIT is

void exit ( int status );
EXIT terminates program execution and passes a return value to the parent process. By convention, a return value of 0 means success; nonzero values signal an abnormal situation. A typical use might be:
        exit ( 0 );
      
If the executing C program is called "fred", then a parent C shell script that invoked fred and is waiting for its return status might read:
        fred
        if ( $STATUS != 0 )
        {
          exit
        }
      

EXIT automatically calls FCLOSE to flush out any buffered output, and to close all open output files.

If you just want to cease executing a routine, and return control to the calling routine, then you want the RETURN statement, not EXIT.


The EXTERN Storage Property

EXTERN is a storage class specifier (along with AUTO, REGISTER, STATIC and TYPEDEF).

An EXTERN statement has the form

        extern TYPE NAME;
      
where TYPE is a data type and NAME is a variable name.

As with a normal declaration statement, the EXTERN statement sets aside storage for a variable of the given name and type. However, it also makes the name of the variable, and the values it represents, known and accessible to functions in the given source file.

(And when can you make it accessible to functions in other source files??)

In certain circumstances, the EXTERN declaration can be omitted.


The FOR Statement

The FOR statement can repeat a set of statements, which are commonly called a FOR loop. Schematically a FOR loop looks like:

        for ( init; test; increment )
        {
          statements
        }
      

The statement INIT is carried out once, just before the loop is begun. Then TEST is computed, and if true, the loop is executed. Then INCREMENT is carried out. Now the loop is repeated as often as TEST is true. In other words, the above loop is equivalent to:

        init

        label:

        if ( test )
        {
          statements
          increment
          go to label
        }
      

A simple version of a "DO" loop would be:

        for ( i = 1; i <= n; i = i + 1)
        {
          a[i] = a[i] + 1;
        }
      

Inside a FOR loop, the CONTINUE statement can be used to skip the remaining text of the loop, and to start the next iteration. A BREAK statement can be used to exit the loop entirely.

In rare cases, a FOR statement might have no subsidiary statements at all, in which case a semicolon is required to terminate it:

        for ( x = 0; x < 1000; x=x+1);
      

A common version of the FOR statement is the "do forever", in which the TEST section, and possibly the INIT and INCREMENT sections, are null. The loop will repeat indefinitely. Exits from the loop must be carried out using the BREAK statement, EXIT or RETURN explicitly:

        n = 1329;
        for ( ; ; )
        {
          if ( n == 1 )
          {
            break;
          }
          else if ( n % 2 == 0 )
          {
            n = n / 2;
          } 
          else
          {
            n = 3 * n + 1;
          }
        }
      

FOR statements can be nested. The simplest case has the form:

        for (...)
        {
          for (...)
          {
            statements
          }
        } 
      
If a BREAK statement is used in a set of nested loops, only the loop immediately containing the BREAK statement is exited.


Function Names As Arguments

It is possible for a procedure to expect as one of its arguments the name of another procedure.

        double myfunc ( double x );
        double integrate ( double (*func) ( double x ), double a, double b );
        int main ( )
        {
          quad = integrate ( myfunc, a, b )
        }
   
        double integrate ( double (*func) ( double x ), double a, double b )
        {
           return func ( ( a + b ) / 2 ) * ( b - a );
        }
      


The GOTO Statement

The GOTO statement transfers execution to the statement that occurs after a given label. GOTO can easily jump out of several nested loops, which the BREAK statement and the CONTINUE statement cannot do. The label has the same restrictions as a C variable name.

        for (...)
        {
          for (...)
          {
            ...
            if (disaster) 
            {
              goto ERROR;
            }
          }
        }

        ERROR:
          statements to handle error...
      


Identifiers

An identifier is a name to be used to identify a variable, procedure, function, datatype, or statement label. The identifier can be any string of alphanumeric characters, including the underscore. However:


The IF Statement

The simplest IF statement:

        if ( n == 1 )
        {
          n = n+1;
        }
      

When you use an IF statement, you have to write a conditional. When describing an equality conditional, you must use the double equal sign. If you accidentally use the single equal sign, your code will compile, but what you want almost surely won't be what happens!

        if ( n = 1 ) //  WARNING, INAPPROPRIATE CODING HERE!
        {
        ...
        }
      
In other words, this statement will compile, but won't do what you want!

Multiple statements may be grouped.

        if ( n == 1 )
        {
          n = n+1;
          m = 0;
        }
      

If an alternative action is to be performed when the condition is not true, then the ELSE statement may be included:

        if ( x < 0 )
        {
          y = -x;
          x = x+1;
        }
        else
        {
          y = x;
          x = x-1;
        }
      

A sequence of conditions may be checked by using the ELSE IF statement.

        if ( x < 1 )
        {
          ones = ones + 1;
        }
        else if ( x < 10 )
        {
          tens = tens + 1;
        }
        else
        {
          big = big + 1;
        }
      

By convention, many C functions and programs return a zero or nonzero status value. In some cases, 0 is "good" because it means that "0 errors occurred". Otherwise, 1 might mean an error of type 1 and so on. In other cases, the status value is returning a count of how many values were read, or other quantities that are "good" when nonzero. In any case, as long as the interpretation of the return value is understood, it is typical to replace lines like:

        return_status = my_sub ( arguments );
        if ( return_status != 0 )
        {
          cout << "Bad things happened!\n";
          exit;
        }
      
by
        if ( my_sub ( arguments ) )
        {
          cout << "Bad things happened!\n";
          exit;
        }
      


Implementation on UNIX

To compile, link and run a single source file:

        cc -o myprog.c
        cc myprog.o
        a.out
      

INCLUDE files are in the directory:

        /usr/include
      
You can use the "-I" switch on the cc command to add other include directories.

To DEFINE quantities, use the -D switch:

        cc -Dname myprog.c
        cc -Dname=def myprog.c
        cc -D(name1=def1,name2=def2) myprog.c
      

To access the preprocessor directly:

        cpp myprog.c > output
      

Predefined preprocessor symbols:

        #IFDEF unix
      


Input/Output

Simple output can be done using the output operator "<<", which prints data on a given output unit. (Your program must include the "stream.h" include file.) The standard output unit is called cout, and you could be forgiven for calling the typical output statement a "cout" statement. If several items are to be printed, they may be concatenated using repeated "<<" operators. To make a new line, the "\n" character is printed.

Thus, common output statements include:

        cout << "This is just a label.\n" );
        cout << "The value of x is " << x << ".\n";
        cout << "My name is " << name << "\n";
      

To print an array, you can simply use a for loop:

        cout << "Array x = ";
        for ( i = 0; i < n; i++ )
        {
          cout << x[i] << "  ";
        }
        cout << "\n";
      

To print to a file, you will need to include the "fstream.h" include file, and then declare a file stream pointer of type ofstream, and then (use the appropriate open command. Having done all this, the same concatenation operator can be used for output. When you are finished writing to the file, a close command is needed.

        ofstream csave;
        csave.open ( "my_output.txt" );
        if ( !csave )
        {
          cout << "Could not open the output file!\n";
          exit(1);
        }
        csave << "I wrote this file myself.\n"
        csave << "I am just " << n << " years old.\n";
 
        csave.close ( );
      

C++ makes reasonable choices when printing the data. Thus, if a real number is being printed, but it has no fractional part, the real number will be printed out looking just like an integer, that is, "14" rather than, say, "14.000000". However, sometimes you want to take control of some of the choices made when printing output. The simplest choice is the field width, or the number of spaces that the item will take up. You can specify your desired field width by invoking the setw() "I/O manipulator" as part of your output statement:

        cout << setw(6) << i << "  "
             << setw(10) << x << "  "
             << setw(10) << y << "\n";
      
Note that each invocation of setw only affects one output item, and then the default is back.

Another option is to change the numeric base used to print a quantity. This is done with the I/O manipulator setbase():

        cout << "Here is my weight in decimal " << setbase(10) << weight << ", "
             << "and in octal " << setbase(8) << weight << ", "
             << "and in hex " << setbase(16) << weight << "\n";
      
You can also use the specialized manipulators dec, oct and hex in the obvious way.

Usually C++ buffers its output. This can be a problem if your program crashes, because the last several lines of output may not make it to the output file, making it tricky to narrow down the location of the crash. You can force an output statement to transfer its data immediately by using the flush I/O manipulator:

        cout << "I have a feeling there's going to be a crash\n" << flush;
      


Library Routines

ATOF
#include <stdlib>
double atof ( const char *s )

ATOF converts the string S to a double value.

ATOI
#include <stdlib>
int atoi(const char *s)

ATOI converts the string S to an int value.

BZERO
void bzero ( void *b, int length );

BZERO places LENGTH zeroes in the string B.

DELETE
#include <stdio.h>
int delete(const char *filename)

DELETE deletes the file named FILENAME.

EXIT
#include <stdlib>
void exit ( int status )

EXIT causes normal program termination. STATUS=0 is taken as successful termination.

FCLOSE
#include <stdio.h>
int fclose ( FILE *stream )

FCLOSE flushes any unwritten data for stream, discards any unread buffered input, frees buffers, and closes the stream. It returns EOF if any errors occurred, and 0 otherwise.

FEOF
#include <stdio.h>
int feof ( FILE *stream )

FEOF returns TRUE if the end of file has been reached, otherwise 0.

FFLUSH
#include <stdio.h>
int fflush ( FILE *stream )

FFLUSH causes any buffered but unwritten data to be written on an output stream. It has no effect on an input stream. It returns EOF for a write error, and 0 otherwise.

FGETC
#include <stdio.h>
int fgetc ( FILE *stream )

FGETC reads a character from a stream. For historical reasons, it is defined as an INT, but the high order byte is always zero. FGETC returns EOF when the end of file is reached. (FGETC will not read a newline character).

FGETS
#include <stdio.h>
int fgets ( char *s, int length, FILE *stream )

FGETS reads a string from a stream, until either a newline character or length-1 characters have been read. Unlike FGETC, if a newline character is read, it will be part of the string returned.

FOPEN
#include <stdio.h>
FILE *fopen ( const char *filename, const char *mode )

FOPEN opens the named file and returns a pointer to a stream, or NULL if it fails. Legal values for MODE include Update mode permits reading and writing the same file; FFLUSH or a file-positioning function must be called between a read and a write. If the mode includes a "b" after the initial letter, as in "rb" or "w+b", that indicates a binary file.

FPRINTF
#include <stdio.h>
int fprintf ( FILE *stream, const char *format, ...)

FPRINTF converts and writes output to STREAM under the control of FORMAT. The return value is the number of characters written, or negative if an error occurred. FPRINTF is just like PRINTF, except for the extra first argument.

FPUTC
#include <stdio.h>
int fputc ( int ch, FILE *stream )

FPUTC writes a character to STREAM. For historical reasons, CH is defined as an INT, but only the low order byte is used. The return value is the character written, if successful, or EOF otherwise.

FPUTS
#include <stdio.h>
int fputs ( char *s, FILE *stream )

FPUTS writes a string to STREAM.

FREAD
#include <stdio.h>
int fread ( void *ptr, int size, int nitems, FILE *stream );

FREAD reads up to NITEMS items of data from the input file STREAM, where each item of data is of length SIZE bytes. The file pointer associated with STREAM is updated. FREAD returns the actual number of items read. The argument SIZE is typically of the form sizeof(item) where item is a data item of the appropriate type.

FSCANF
#include <stdio.h>
int fscanf ( FILE *stream, const char *format, ... )

FSCANF reads from STREAM under control of FORMAT, and assigns converted values through subsequent arguments, each of which must be a pointer. It returns when FORMAT is exhausted. It returns EOF if end of file or an error occurs before any conversion; otherwise it returns the number of input items converted and assigned.

FSEEK
#include <stdio.h>
int fseek ( FILE *stream, long offset, int origin )

FSEEK sets the file position for STREAM. A subsequent read or write will access data at the new position. For a binary file, the position is set to OFFSET characters from ORIGIN, which may be SEEK_SET (the beginning), SEEK_CUR (current position) or SEEK_END (end of file). For a text file, OFFSET must be zero, or a value returned by FTELL. FSEEK returns nonzero on error.

FTELL
#include <stdio.h>
long ftell ( FILE *stream )

FTELL returns the current file position for STREAM, or -1L on error.

ISALNUM
#include <ctype.h>
int isalnum ( c )

ISALNUM returns nonzero if C is an alphanumeric character, and 0 if C is not.

ISALPHA
#include <ctype.h>
int isalpha ( c )

ISALPHA returns nonzero if C is an alphabetic character, and 0 if C is not.

ISCNTRL
#include <ctype.h>
int iscntrl ( c )

ISCNTRL returns nonzero if C is a control character, and 0 if C is not.

ISDIGIT
#include <ctype.h>
int isdigit ( c )

ISDIGIT returns nonzero if C is a digit, and 0 if C is not.

ISGRAPH
#include <ctype.h>
int isgraph ( c )

ISGRAPH returns nonzero if C is a printing character (except a space), and 0 if C is not.

ISLOWER
#include <ctype.h>
int islower ( c )

ISLOWER returns nonzero if C is a lowercase letter, and 0 if C is not.

ISPRINT
#include <ctype.h>
int isprint ( c )

ISPRINT returns nonzero if C is a a printing character, and 0 if C is not.

ISPUNCT
#include <ctype.h>
int ispunct ( c )

ISPUNCT returns nonzero if C is a printing character, except for a space, alphabetic character or digit, and 0 if C is not.

ISSPACE
#include <ctype.h>
int isspace ( c )

ISSPACE returns nonzero if C is a "space" character (space, formfeed, newline, carriage return, tab, vertical tab), and 0 if C is not.

ISUPPER
#include <ctype.h>
int isupper ( c )

ISUPPER returns nonzero if C is an upper case letter, and 0 if C is not.

ISXDIGIT
#include <ctype.h>
int isxdigit ( c )

ISXDIGIT returns nonzero if C is a hexadecimal digit, and 0 if C is not.

PRINTF
#include <stdio.h>
int printf(const char *format, ...)

PRINTF is equivalent to "fprintf(stdout, ...)".

RAND
#include <stdlib.h>
int rand(void);

RAND returns a pseudo-random integer betwee 0 and RAND_MAX.

REMOVE
#include <stdio.h>
int remove(const char *filename)

REMOVE removes the named file, so that a subsequent attempt to open it will fail. It returns nonzero if the attempt fails.

RENAME
#include <stdio.h>
int rename(const char *oldname, const char *newname)

RENAME changes the name of a file; it returns nonzero if the attempt fails.

SCANF
#include <stdio.h>
int scanf(const char *format, ...)

SCANF is equivalent to "fscanf(stdin, ...)".

SPRINTF
#include <stdio.h>
int sprintf(char *s, const char *format, ...)

SPRINTF is the same as PRINTF except that output is written into the string S, terminated with a '\0'.

SRAND
#include <stdlib.h>
void srand ( unsigned int seed )

SRAND uses the input value of SEED as the seed for the random number generator RAND.

SSCANF
#include <stdio.h>
int sscanf ( char *s, const char *format, ... )

SSCANF is equivalent to SCANF except that input characters are taken from the string S.

STRTOD
#include <stdlib>
double strtod ( const char *s, char **endp )

STRTOD converts the prefix of S to double, ignoring leading white space; it stores the pointer to any unconverted suffix in *ENDP, unless ENDP is NULL.

STRTOL
#include <stdlib>
long strtol ( const char *s, char **endp, int base )

STRTOL converts the prefix of S to long, ignoring leading white space; it stores a point to any unconverted suffix in *ENDP, unless ENDP is NULL. If BASE is between 2 and 36, conversion is done assuming the input is written in that base.


Math Library Routines

Accessed by #include <math>. Also, at load time, the math library must be included with the -lm switch.
FormComment
double acos ( double x ) 
double asin ( double x ) 
double atan ( double x ) 
double atan2 ( double y, double x ) =atan(y/x)
double ceil ( double x ) smallest int as big as x
double cos ( double x ) 
double cosh ( double x ) 
double exp ( double x ) 
double fabs ( double x ) The abs function is for integers only!
double floor ( double x ) biggest int no greater than x
double fmod ( double x, double y ) remainder of x/y, with sign of x
double frexp ( double x, int *exp ) returns normalized fraction in [0.5,1); exp is power of 2
double ldexp ( double x, int n ) returns x * 2^n
double log ( double x ) 
double log10 ( double x ) 
double modf ( double x, double *ip ) returns integral part; fraction returned in ip
double pow ( double x, double y )returns x^y
double sin ( double x ) 
double sinh ( double x ) 
double sqrt ( double x ) 
double tan ( double x ) 
double tanh ( double x ) 

String Library Routines

STRCAT
#include <string.h>
char *strcat ( char *str1, const char *str2 )

STRCAT concatenates the characters in string STR2 onto the end of string STR1, and returns a pointer to STR1.

STRCHR
#include <string.h>
char *strchr ( const char *str1, char c )

STRCHR returns a pointer to the first occurrence of C in the string STR1, or NULL if C is not present.

STRCMP
#include <string.h>
int strcmp ( char *s1, char *s2 )

STRCMP compares strings S1 and S2, returning:

STRCPY
#include <string.h>
char *strcpy ( char *str1, const char *str2 )

STRCPY copies the characters in string STR2 into STR1, and returns a pointer to STR1.

STRLEN
#include <string.h>
size_t strlen ( const char *str1 )

STRLEN returns the length of the string STR1, that is, the location of the first '\0' character.

STRNCAT
#include <string.h>
char *strncat ( char *str1, const char *str2, int n )

STRNCAT concatenates at most N characters from string STR2 onto the end of string STR1, and returns a pointer to STR1.

STRNCMP
#include <string.h>
int strncmp ( char *str1, char *str2, int n )

STRNCMP compares at most the first N characters of two strings. It returns < 0 if STR1 < STR1, 0 if STR1 = STR2, > 0 if STR1 > STR2.

STRNCPY
#include <string.h>
char *strncpy ( char *str1, const char *str2, size_t n )

STRNCPY copies at most N characters from string STR2 into STR1, and returns a pointer to *STR1. STR1 is padded with '\0' if there are fewer than N characters in STR2.

STRRCHR
#include <string.h>
char *strrchr ( const char *str1, char c )

STRRCHR returns a pointer to the last occurrence of C in the string STR1, or NULL if C is not present.

STRSTR
#include <string.h>
char *strstr ( const char *str1, const char *str2 )

STRCHR returns a pointer to the first occurrence of string STR2 in the string STR1, or NULL if STR2 is not present.


Literal Values

Literal Array Values

An array literal can be written as a sequence of scalar literals, separated by commas, and surrounded by curly brackets:

        int x[3];

        x = { 1, 2, 3};
      
or, in a declaration:
        int x[3] = { 1, 2, 3};
      

Literal Matrix Values

A matrix or two dimensional array is a vector of rows. A literal value is therefore a vector whose elements are vectors, namely the row values. In a declaration, this would look like:

        int a[2][3] = { { 11, 12, 13}, {21, 22, 23} };
      
while in an executable statement we might have:
        a = { { 11, 12, 13}, {21, 22, 23} };
      

Literal Boolean Values

A Boolean literal is either true or false, spelled just like that:

        bool guilty;
        bool innocent

        guilty = true;
        innocent = false;
      

Literal Character Values

A character literal is an unsigned integer, which can be written as one character within single quotes, such as

        char fred;
        fred = 'x';
      
or, equivalently, its numeric ASCII value, between 0 and 255.
        char fred;
        fred = 120;
      

Certain non-printable characters have special representations, also called "escape sequences":

In addition, an arbitrary byte can be specified by

        '\***'
      
where *** represents one to three octal digits, or by
       '\x**'
      
where ** represents one or more hexadecimal digits.

Literal Float Values

A floating point constant consists of "i.fExs", an integer part, a decimal point, a fractional part, the letter "E" or "e", an exponent, and a suffix of "f", "F", "l" or "L". The integer or the fractional part may be omitted, but not both. The decimal point or the "E" and exponent may be omitted, but not both. The suffix is optional. "F" makes it a float, and "L" a long double. Otherwise, it is a double. Examples:

        -11.1e-11f
        1.
        .1
        11e-10
      

Literal Integer Values

For a refreshing change, the literal value of an integer is basically just what you think it would be. To set a variable to 17, you just have to write

        i = 17;
      

Naturally, this was much too uncomplicated, so there quickly developed some elaborations of this format. The first had to do with the base in which the constant was expressed. It is possible to express a constant in octal or hexadecimal. A leading 0 on an integer constant AUTOMATICALLY means octal. (Isn't that asking for trouble?) A leading 0x automatically means hexadecimal.

For example, in C,

        i = 21;
        i = 021;
      
are different. The second statement sets I to 17! Ah, we're back in the looney tune world. Meanwhile, a hexadecimal literal would be used in a statement like:
        i = 0x21;
      
in which case I is set to what ten-fingered mortals call 33.

The second blot upon simplicity occurs because of the unsigned integer data type. If the integer data type can be unsigned, then it's necessary to be able to specify a constant of that form. To indicate an unsigned integer constant, a capital U is appended to the end of the value. Thus,

        i = 255U;
        i = 3037000493U;
      

The third thing to keep in mind is that you may want to specify that an integer constant is of type LONG. To do this, you simply append an L to the value. (And if it's an UNSIGNED LONG, you append UL:

        i = 123456789L;
        i = 3037000493UL;
      

Literal String Values

A string literal is denoted by double quotes:

        "This is a string."
      
Implicitly, a string has a trailing null character, so the string "boing" requires 6 entries of storage.
        char string[6] = "boing";
      

For a declaration like the above, it is also legal to say:

        char string[] = "boing";
      
in which case C takes care of deciding how big STRING should be.

Literal Structure Values

The entries of a structure can be thought of as successive elements in an array. Thus, if we define

        struct
        {
          char   key;
          int    number;
          float  rate;
        } fred;
      
then we can assign fred a literal value at declaration time:
        struct {
          char   key;
          int    number;
          float  rate;
        } fred = { 'a', 1, 17.3} ;
      
or we can assign a literal value during execution:
        fred = { 'a', 1, 17.3 };
      

Literal Union Values

A union may only be initialized with a literal value of the type of its first member.

        union
        {
          int   ival;
          float fval
          char *sval;
        } fred = 0;
      
A union literal is the literal value appropriate to a given member of the union:
        fred.fval = 17.34;
      


MALLOC

During the execution of a C program, it is possible to request the allocation of a previously unused block of memory, to be used for a new variable. The routine malloc, part of stdlib.h, is used to do this. MALLOC is told the size, in bytes, of the memory to be allocated, and returns a pointer to the beginning of the memory block.

The sizeof function can be useful in determining the size of a single item of the given type.

The free function can be used to free up or deallocate the memory that was assigned by a malloc call.

The relevant lines of code needed to use malloc might look like this:

        #include 

        float *a;

        a = malloc ( n * sizeof ( float ) );
        ...various commands that apply to a...
        free ( a );
      


Operators

Arithmetic Operators

The usual operators +, -, *, / are available.

The exponentiation operator?

% is a mod function: x % y produces the remainder when x is divided by y. The sign of the result for negative operands is machine dependent.

<< and >> are left and right shift operators. x << 1 essentially moves the bits in X one position left, discards the highest bit and adds a rightmost zero. However, if X is arithmetic, the sign bit is probably preserved, and the next to the highest bit is actually the one discarded. Thus x<<1 should be the same as multiplying X by 2. The right shift operator, working on an arithmetic variable, drops one or more right bits, shifts the remaining bits, and pads with zeroes, or perhaps with the sign bit. Shift operators should never be applied to arithmetic data, but of course this is another C fad.

Assignment Operators

The simplest assignment operator is

        =
      

If op is a binary operator, then in most cases, the expression

        expr1 op= expr2
      
is equivalent to
        expr1 = (expr1) op (expr2)
      

Thus, we have the arithmetic assignment operators: %= x %= y; x = x % y;
OperatorUsageMeaning
+=x += y;x = x + y;
-=x -= y;x = x - y;
*=x *= y;x = x * y;
/=x /= y;x = x / y;

The bitwise operation assignments:
OperatorUsageMeaning
&=x &= y;x = x & y;
|=x |= y;x = x | y;
^=x ^= y;x = x ^ y;
<<=x <<= y;x = x << y;
>>=x >>= y;x = x >> y;

An assignment operator has the type of its left operand, and the value is the value of the left operand after assignment.

Bit Operators

The bitwise operators:
OperatorMeaning
!xNOT x;
x & ybitwise AND of x and y;
x | ybitwise OR of x and y;
x ^ ybitwise XOR of x and y;
x << yleft shift of x by y positions;
x >> yright shift of x by y positions;
~xone's complement of x.

Cast Operators

For every type, there is a cast operator, which converts objects of other types to that type, where possible. A cast operator looks like the name of the desired type, in parentheses:

        (char)
        (double)
        (float)
        (int)
        (long double)
        (long int)
        (short int)
        (signed char)
        (unsigned char)
        (unsigned int)
        (unsigned long int)
        (unsigned short int)
      

It might make sense, for instance, to cast the result of a real division to an integer result:

        i = (int) ( r1 / r2 );
      
The second set of parentheses are used to guarantee that the cast operator (int) operates on the final quotient, and not just on R1.

Logical Operators

The logical operators are

        &&  ||
      
Logical expressions connected by these operators are evaluated left to right. && has higher precedence than ||, and both are lower than relational and equality operators.

Negation Operator

The negation operator converts a nonzero operand into 0 (false), and a 0 operand (false) into 1 (true).

Relational Operator

The relational operators are
OperatorMeaning
>greater than
>=greater than or equal to
<less than
<=less than or equal to
=="if ( a == b )" means "if A equals B"
!="if (a != b)" means "if A is not equal to B"
The first four operators have precedence over the last two. Relational operators have lower precedence than arithmetic operators.


Pointers

If X is a variable, then the expression &X represents the address of the variable X. The address of a variable is called a POINTER.

Given the pointer, it is possible to "dereference" the pointer, and get back the original object, using the notation "*pointer". Thus *(&X) is again the object X.

If X is a pointer to a structure, and M is a member of the structure, then (*X).M is the way to reference the member. An equivalent way of writing this is X->M.


The Preprocessor

#define name

DEFINE may be used to set certain flags, that can be read by the "IFDEF" or "IFNDEF" commands. You may also define certain flags at compile time, using a system-dependent command like

        cc -Dname prog.c
        cc -Dname=def
      
or
        $ CC/DEFINE=(name) prog.c
        $ CC/DEFINE=(name=def) prog.c
      

The C compiler itself will define certain names. For instance, on most UNIX systems, "unix" is defined.

#define name replacement

It is possible to use a symbolic name throughout a C file, and define the value of that symbolic name at one place in the file, using the DEFINE command to the preprocessor. The situation might look like this:

        #define EXIT_FAILURE 0
        #define EXIT_SUCCESS 1

        #define TABSIZE 100

        int table[TABSIZE]
        ...

        exit(EXIT_SUCCESS)
      

At compilation time, the preprocessor will replace all occurrences of the string "EXIT_FAILURE" by 0, and "EXIT_SUCCESS" by 1, and similarly TABSIZE will be replaced by 100.

#undef name

The UNDEF command "undefines" a given name, which may have been defined earlier. It is permissible to undefine a name which has not, in fact, been defined.

#include filename

The INCLUDE command causes the file FILENAME to be inserted into the current file at compilation time. If filename is quoted (with double quotes) then it is searched for in the current directory. If it is not found there, or if the named is enclosed in angle brackets, searching follows an implementation-dependent rule.

        #include "myheader.h"
        #include <stdio.h>
      

#if expression

The #IF statement can be used to make certain preprocessor actions conditional. If the expression is nonzero, then subsequent lines of text are to be included in the compiled file, up to an #ENDIF, #ELIF or #ELSE.

#else

The #ELSE statement can follow an #IF statement. If the #IF expression is false, then the lines of text following #ELSE are to be included in the compiled file, up to the #ENDIF.

#elif

The #ELIF statement can follow an #IF statement. If the #IF expression is false, and the #ELIF expression is true, then the lines of text following #ELIF are to be included in the compiled file, up to an #ENDIF, #ELIF or #ELSE.

#endif

The #ENDIF statement signals the end of the scope of an #IF statement.

defined(name)

The expression "DEFINED(name)" is 1 if NAME has been defined, and 0 otherwise. It can be negated with the "!" prefix:

        #if !defined(HDR)
        #define HDR
        blah blah
        #endif
      

The tested expression may be an equality:

        #if SYSTEM == ATT
          #define HDR "att.h"
        #elif SYSTEM == BSD
          #define HDR "bsd.h"
        #else
          #define HDR "generic.h"
        #endif

        #include HDR
      

#ifdef and #ifndef

The commands

        #ifdef name
        #ifndef name
      
are shorthand for
        #if defined(name)
        #if !defined(name)
      

Certain names are predefined on certain systems. For instance, on most UNIX systems

        #ifdef unix
      
is true.

#Error message

The #ERROR directive forces the C compiler to stop compiling, and to print out the message. The message is not enclosed in double quotation marks.

Built in Macros

The ANSI C preprocessor defines four macro names:

      __DATE__  pointer to a string, the date the file was compiled;
      __FILE__  pointer to a string, the source file that was compiled;
      __LINE__  an integer, the current line number being compiled;
      __TIME__  pointer to a string, the time that the file was compiled.
      

For example, here is a program that uses these macros:

        main() {
          cout << "\n");
          cout << "Line number =   " <<__LINE__ << "\n";
          cout << "Source file =   " << __FILE__ << "\n";
          cout << "Date compiled = " <<__DATE__ << "\n";
          cout << "Time compiled = " <<__TIME__ << "\n";
        }

        Line number =   3
        Source file =   SS_MEP:[MP31879.SRC.CALENDAR]MACRO.C;6
        Date compiled = Feb 12 1997
        Time compiled = 11:15:20
      

The Programming Error Hall of Fame

In the natural evolutionary history of programming bugs, the main defense becomes relying on the programmer's faulty mental image of the text. Anything that is legal enough for the compiler to pass, and that looks like the correct code, will bedevil the programmer who continually compiles the mental program (hey, no errors!) and cannot see that the physical program is different.

I recently spent 4 days (I did have other things to do as well) trying to figure out what was wrong with a piece of code that allocated a local array, filled it with prime numbers, and passed that array to another lower level routine. I was using static int *halton_base for the declaration, and a halton_base = new int[m] command to create the array, and I had to watch out that I hadn't already allocated the array on a previous call. Those were the things I was unsure of, and when the lower level routine complained about bad values in halton_base I was sure I had somehow made one-too-many levels of indirection, or had a wild memory error somewhere else that was coming out here. I stared and stared at the following loop and in my mind, it compiled perfectly, so I knew the error was elsewhere:


        for ( i = 0; i < m; i++ );
        {
          halton_base[i] = prime(i+1);
        }
      
The odd thing was, that if I followed the loop by

        halton_base[0] = prime(1);
        halton_base[1] = prime(2);
      
it ran correctly (for the case m=2, of course.) It was only when I inserted a print statement inside the loop, and it only printed once, as though i were equal to 2, that I realized my deadly error - that invisible little semicolon at the end of the for statement!


The REGISTER Storage Property

The REGISTER declaration advises the compiler that the variable in question will be heavily used. The REGISTER declaration can only be applied to automatic variables and the formal parameters of a function.

        register int i;
      


Reserved Words

Some identifiers are reserved by C, and may not be used to name variables, functions, or other quantities:

        asm
        auto
        bool
        break
        case
        catch
        char
        class    
        const
        const_cast   
        continue  
        default
        delete 
        do
        double
        dynamic_cast
        else
        enum
        explicit
        extern
        false
        float
        for
        friend
        goto
        if
        inline
        int
        long
        mutable
        namespace
        new
        operator
        private
        protected
        public
        register
        reinterpret_cast
        return
        short
        signed
        sizeof
        static
        static_cast
        struct
        switch
        template
        this
        throw
        true
        try
        typedef
        typeid
        typename
        union
        unsigned
        using
        virtual
        void
        volatile
        wchar_t
        while
      


The RETURN Statement

The RETURN statement terminates the execution of a procedure, and returns control to the calling procedure. If the procedure is a (non-void) function, then the RETURN statement is also used to specify the value to be returned to the caller.

Thus, for a procedure or void function, the RETURN statement is simply:

        return;
      
while for non-void functions, the value of the function is specified as the argument of the RETURN statement:
        return 1;
        return (x+y);
        return &(fred);
      

A procedure or void function does not need to have any RETURN statements. In that case, execution will return to the calling procedure when the last statement in the function has been reached.

The main program itself is typically a function that returns an INT to the calling process. This is also done using the RETURN statement.


The SIZEOF Function

SIZEOF(X) returns the size of X, or the amount of storage required for X, in bytes. This function was very useful in the days when you needed malloc to allocate memory.


The STATIC Storage Property

A STATIC statement has the form

        static TYPE NAME;
      
where TYPE is a data type and NAME is a variable name.

Most variables are associated with a single function. They come into existence when the function is called, and disappear when the function returns control to the caller. The variable has a value only during the lifetime of the function, and that value can only be changed by the function itself, or by functions called by the function, to which the function chooses to pass the variable's address.

Variables associated with a particular function, having no permanent storage or value in between calls of that function, are usually known as AUTOMATIC variables. This is the default behavior for C variables.

On the other hand, a variable associated with a particular function may be given permanent storage, and allowed to retain its value between calls of that function. Such a variable is known as a STATIC variable.

Static storage is specified by prefixing the normal variable declaration with the word STATIC.

The STATIC declaration, applied to an external variable or function, limits the scope of that object to the rest of the source file being compiled. External static declaration thus provides a way for routines in one file to share variables, but for those variable names to be hidden from routines in other files.

Internal variables may also be declared STATIC. In that case, the only effect is to allow such variables to remain in existence between function calls, retaining their values.


Strings

There is no explicit string datatype in C, and yet, there are string literals, and there are scads of library functions that operate on strings. In C, a string is really a sequence of characters terminated by a NULL character, that is, '\0'. Thus, strings are "self-counting"; a string "knows" how long it is.

There are two slightly different ways of handling strings:

Since an array is "really" a pointer, there are many ways in which these two approaches are the same. Actually, an array is a collection of pointers to successive values, and the user is responsible for specifying in advance the maximum number of values to be used. Thus a pointer is more flexible; it's the address of the first element of "someone else's array".

Thus, the differences between pointer and array approaches to strings boil down to declarations, to the different techniques traditionally used to access successive elements of arrays and of pointees (objects that are pointed to), and the differences in handling objects directly by array entry reference, or indirectly, by pointer reference.

Once you pass a string array or string pointer to a subroutine, they look the same, because the subroutine gets the same information: a pointer to the first element. Thus, library routines that handle strings don't care whether they're getting a string array or a string pointer.

There is a library of string routines available by including <.string.h>.. These include

To print a string, whether it is a string pointer or array, just use printf with an s format. You can restrict the maximum number of characters printed:

        char *string1 = "A rather longish string";
        char string2[] = "Yet another string";
        printf ( "The whole thing is %s.\n", string1 );
        printf ( "The first 10 characters are %10.10s\n", string2 );
      

String Pointers

A string pointer is declared as a pointer to a character:

        char *string;
      
Thus, a string pointer is usually the address of the first character of a string. For brevity, we will prefer to say it is the address of the string itself.

A string literal is delimited by double quotes. An assignment involving a string pointer might look like any of the following:

        char *string = "Initial value";
        string = "I am a string";
        *string = 'A';
        strcpy ( string, "New value" );
      
In these examples,

To traverse or examine a string for which we have a pointer, we need to use the usual pointer arithmetic. Thus, if password is a string, we could print it out, one character at a time, as follows:

        char *password;
        char *letter;

        for ( letter = password; *letter != 0; letter++ )
        {
          printf ( "%c", *letter );
        }
      

String Arrays

A string array is an array of characters that stores a string. Since a string is terminated by a NULL or '\0' character, the dimension of the array must allow for this extra entry. If, in a declaration statement, a string array is given an initial value, the compiler allows you to omit the dimension, which it will determine implicitly from the length of the initialization string. Here are three equivalent initializations:

        char name[5] = {'f', 'r', 'e', 'd', '\0'};
        char name[5] = "fred";
        char name[]  = "fred";
      

A string literal is delimited by double quotes. An assignment involving a string array might look like any of the following:

        char string1[15] = "Initial value";
        char string2[] = "Another value";
        string1[1] = 'A';
        strcpy ( string1, "New value" );
      
It is not possible to use a simple assignment statement such as
        string = "Not legal!";
      

Individual entries of the string are accessible by the usual array notation. Thus, to change "fred" to "fret", we can say:

        name[3] = 't';
      
and we could look for 'e' in the name by a simple loop:
        for ( i == 0; fred[i] != '/0'; i++ )
        {
          if ( fred[i] == 'e' )
          {
            fred[i] = 'u';
          }
        }
      


The STRUCT Datatype

The form of a STRUCT statement is

        struct TAG
        {
          TYPE1 NAME1;
          TYPE2 NAME2;
          ...
          TYPEn NAMEn
        } NAME;
      

At least one of TAG and NAME must be specified. Specifying NAME sets aside a variable named NAME of the given type. Specifying TAG gives the particular STRUCT a name which can be used in a TYPEDEF statement to allow more objects of the same form to be easily declared:

        struct position
        {
          float xpos;
          float ypos;
        };

        typedef  struct position  point;
      

A structure can be initialized in its declaration by specifying its values in order. For instance, using our previous declaration:

        point fred = { 0.12, 3.14 };
      

A structure can be assigned either componentwise:

        point fred;
        point george;

        george.xpos = 17.1;
        george.ypos = 18.3;
      
or as a whole, using a structure literal:
        george = { 17.1, 18.3};
      
or as a whole, by being set equal to another structure of the same type.
        point fred;
        point george;

        george = { 17.1, 18.3};
        fred = george;
      

One nice application of STRUCT is to set up a representation of complex numbers.

Just the other day, I had a problem where I needed an object to be a pointer to a structure, and I was trying to put stuff into the structure. The compiler complained about the form

        *name.member = 12;
      
and I realized I had to write
        (*name).member = 12;
      
because otherwise the expression was ambiguous. Was it the object pointed to by (name.member) or the member of the object (*name)?

If X is a pointer to a structure, and M is a member of the structure, then (*X).M is the way to reference the member. An equivalent way of writing this is X->M.


The SWITCH Statement

A SWITCH statement examines the value of an expression, and carries out various groups of statements depending on the value of the expression. The form is

        switch ( EXPRESSION )
        {
          case VALUE1:
          {
            statements
            break;
          }
          case VALUE2:
          {
            statements
            break;
          }
          ...
          default:
          {
            statements
          }
        }
      

All case expressions must be different. The case labeled DEFAULT is executed if none of the other cases are satisfied. The DEFAULT case is optional.

A BREAK statement is usually used to terminate the set of statements associated to a particular case; otherwise, contrary to expectations, execution will "fall through" to the next case, and the next one.


The TYPEDEF Statement

If you wish that C had a complex number data type, (which it doesn't), the TYPEDEF command allows you to set it up, in terms of quantities already defined. You still have to take care of defining the arithmetic operations on these quantities, but at least you have a clean way of declaring them. Here's one way to do it:

        typedef complex double[2];
      
This says that any type declaration of a "complex" variable is to be understood as a declaration of a double[2]. In this case, the real and imaginary parts are entries 0 and 1 of the array. Another solution, also using TYPEDEF, uses the STRUCT datatype to accomplish the goal.

Similarly, if you have many variables which are examples of the same kind of STRUCT or UNION, you can issue a command like:

        struct fred
        {
          int a;
          float b;
        } george;
 
        typedef struct fred fred_struct;
      
or, equivalently,
        struct fred
        {
          int a;
          float b;
        };

        typedef struct fred fred_struct;
        fred_struct george;
      

Again, a TYPEDEF statement is useful in setting up an enumeration data type:

        enum month {Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec};
        typedef  enum month  month_enum;
        month_enum my_birthday_month;
      


The UNION Datatype

A union is a variable that can have one of several types. In the simplest case, the variable names a single value, and we simply wish to be able to consider values stored under that name as an integer, float, or character.

For instance, we might declare the variable X as:

        union
        {
          int intval;
          float flval;
          char chrval;
        } x;
      

Now we have to keep track of what we actually store in X. This is done using a syntax that is reminiscent of a STRUCT datatype, as though X were actually storing all three quantities at the same time. To store an integer value, we would write:

        x.intval = 1;
      

The C UNION is similar to a FORTRAN EQUIVALENCE.


The VOID Datatype

A function which returns no value should be declared void. A function or procedure which has no arguments should be declared with a dummy argument list of void.


The VOLATILE Property

VOLATILE is a type-qualifier, along with CONST. The purpose of VOLATILE is to warn the compiler that the given object should not be optimized.


The WHILE Statement

The while statement can be used to iterate a set of statements as long as some condition is true. The user is responsible for making sure that the loop will actually terminate some time.

        while ( n < 100 ) {
          n = n+7;
        }
      

A BREAK statement can be used to exit a while block:

        while ( n < 100 )
        {
          if ( n == 12 )
          {
            break;
          }
          n = n + 7;
        }
      

A CONTINUE statement simply jumps to the end of the while block, and picks up the next iteration:

        while ( n < 100 )
        {
          if ( n == 12 )
          {
            continue;
          }
          n = n + 7;
        }
      

A while statement can be used to do indefinite repetition, by inserting an always true statement in the condition:

        while ( true )
        {
          n = n + 1;
          if ( a[n] == 0 )
          {
            break;
          }
        }
      

You can return to the HTML web page.


Last revised on 15 April 2007.