26. The C Language

Last revision:  April 15 1998

This section introduces a subset of the C language and explores its similarities and differences with Mathematica.

A Quick Tour of the C Language

The C language is a computer programming language widely used by professional programmers, and for that reason we believe that students should have some exposure to the language.  While structuring an introductory computing class around C is possible, it poses pedagogical burdens which we did not wish to assume, one of the most important being that it initially takes students longer to write programs in C than in Mathematica, and another being that Mathematica contains many more built-in functions which C, in its original state, does not support.  However, C shares so many similarities to certain aspects of Mathematica that we can introduce C by highlighting these similarities.  This section is by no means intended as a tutorial which will result in the reader's immediately being able to write perfect C code; instead, it is meant to serve as a reader's guide to literature in C.  The student who wishes to program professionally should take at least a second course in programming structured around C or some other common language.

Data Types, Declarations, and Definitions

C is a functional language, just like Mathematica, which means that it is primarily structured around variables and functions which act upon them.  Unlike Mathematica, however, C requires that every variable and function have a data type, and these types cannot change over the course of the program.  For instance, if "i" is an integer, it must always denote an integer, and if an approximate number is assigned to it, "i" will hold only the integer portion.  What we mean when we say "the data type of a function" is really the data type of the return value, and if a function does not return a value, its data type (or "return type", as we will use later) must defined as void.

What data types does C support, other than "void" for functions with no return value?  Here is a partial list:  "char" (values must be single characters); "int" (values must be machine-precision integers, typically ranging in value from -32767 to +32767); "long" (values are integers which take up several bytes, so typically ranging from -2147483647 to 2147483647); "float" (values are machine-size approximate numbers, typically ranging from -10^37 to 10^37, with up to 6 significant digits); "double" (values are approximate numbers allowed more bytes than floats, so with a wider range of values and with more significant digits); and arrays of any of these types.

Note that C does not support lists or strings as separate data types.  Instead, C programmers must keep arrays of integers or characters and must worry about the lengths of these arrays themselves.  While Mathematica allows lists of arbitrary length and numbers of arbitrary size, C instead restricts the values to machine-precision numbers.  In C, to use huge integers, it is necessary to store them in pieces and manipulate their separate parts (digits, say) separately.  C is thus a more primitive language than Mathematica.   However, one can write packages so that C can use arbitrary-length lists and numbers, if one desires, and C's chief advantages over Mathematica include its versatility and its compiled, rather than interpreted, implementation.

Declaring Variables and Functions; Arrays

Again, every variable and function in C must have its type declared.  The syntax for declaring variables and functions is to write, on a separate line, the data type name, followed by a space, followed by the names of the variables and functions which have that type.  Function names must be suffixed with "()", which indicates that the name refers to a function, not a variable, and if the function takes any arguments, they must be included between the parentheses, separated by commas, each preceded by its data type.  In order to declare a variable to be an array of elements of a certain type, the variable name must be suffixed with "[number]", where "number" is an integer, in which case an array of size "number" will be declared.  Finally, all C lines must end with a semicolon.  Here are some examples:

In this example we have three integer variables defined, "a", "b", and "c"; two functions which return float values, "sin" and "myFunc", the first of which takes no arguments and the second of which takes one argument of type float; and finally an array of 30 characters, "str".

Arrays in C deserve special mention, because in C the first array element is referenced with "[0]", and the last element (in an array with "n" elements) "[n-1]".  Hence, the values in a three-element array "a" are "a[0]", "a[1]", and "a[2]".  Also, arrays with more than one dimension are declared simply by suffixing more "[number]" components:  "int a[10][10]" declares a 10 by 10 array of integers.

Several syntactical differences with Mathematica are immediately apparent.  First, Mathematica suffixes functions with square brackets which enclose the arguments, if any; C instead uses parentheses.  Also, Mathematica uses double brackets to denote an array, but C uses single square brackets.  Finally, Mathematica does not permit the declaration of an array without constructing it, as with a "Table[]".

Defining Variables and Functions; Return Statements; Local, Global

In C, just as in Mathematica, variables are defined or modified with assignment statements, as in "i=3;".  Functions are defined similarly to Mathematica, as follows.  The return type of the function is specified first, followed by the name of the function, followed by a parenthesized, comma-separated sequence of argument names, each preceded by the appropriate data type, followed by a curly-brace-enclosed set of statements inside the function, called the "function block".  The statements inside a C "function block" are similar to Mathematica statements, as we will see, later; however, we point out now that the return value must be contained in a statement "return x", where "x" is the value the function wishes to return---the return value is not simply the value of the last statement executed, as it is in Mathematica.

Here is an example of definitions of a variable "i" and a function "myFunc", which multiplies its first argument, an integer, by 2 and adds the second argument, returning the result.  (The variable definition here has nothing to do with the function definition but is provided only as an example.)

Notice that a separate variable was defined inside the function block.  This variable, "c", will be a local variable in C because it is declared inside the function block.  Global variables, on the other hand, are those variables declared outside any function block.

One difference with Mathematica is that, in C, when a return statement is encountered, the function immediately stops and returns the value; no succeeding statements will be executed.  For instance, in the function

a "1" will always be returned, and the statement "return a" would never be executed.

Control Structures

C's control structures are also very similar to Mathematica's.  We'll sample the most important ones here.

If

C's "If" statement has the following syntax:  "if (boolean-expression) statement;", and the statement is executed only if the boolean condition is true.  When several statements are to be executed, they should be listed, separated by semicolons as usual, with the entire group surrounded by curly braces to form a block.  For example,

causes "b=2" to be executed only if "a" is 1, after which "b" is set equal to 3, and then, if "a" is 2, then "b" is set to 3 and "c" to 4.

If we wish to have other statements executed if the boolean value is false, the syntax is "if (boolean-expression) statement; else statement;", where again a statement can be replaced by a curly-brace-enclosed block.  Hence we could have the following recursive function to compute factorials (notice that the function is declared before it it defined!):

Switch, Break

C's switch statement has the following standard syntax:  "switch (variable) { case value: statement; case value: statement; ....}", where as many "case value: statement;" structures many be repeated as necessary.  The value of the variable will be checked, the program will jump to the statement following "case value" with the same value, and will then continue down through the rest of the statements until a "break;" statement is encountered, in which case the switch statement will terminate.  As usual, one may substitute a block of statements enclosed by curly braces for a "statement" in the syntax expression.  Here's one example.

Note that without the "break" statements, each statement after one jumped to by the case would have been executed, so that "commentOnGrade('A')" would have caused all five print statements to be executed.  Notice also that single characters are surrounded by single quotes---in essence, they are separate from strings---and the "printf" command behaves much like Mathematica's "Print[]" command.  Finally, notice that there is no "return" statement, since the function has a "void" return value.

For

C's For statement is very similar to Mathematica's; the only difference is that the first three statements in Mathematica's For command are separated by commas and enclosed by parentheses, and the fourth statement simply follows the parentheses, as in "for (initialization-statement; boolean-expression; increment-statement) execution-statement;".  Here is an example.

As might be expected, this section of code adds up the numbers from 1 to 9.

While

C's While statement is also very similar to Mathematica's, having syntax "while (boolean-expression) statement;".  An example is then

Do...While

C's Do statement, however, differs from Mathematica's, because C's Do statement is simply a While statement with the boolean test at the end instead of the beginning.  In other words, the execution is the same as a While loop except that the loop is executed once before the boolean test is checked.  C's Do statement, therefore, does not permit the use of iterators.  Here is an example which counts the number of zeros in an array which is a global variable.

main()

When one runs a C program, the first function executed is always the "main()" function.  Hence, every C program must have a "main" function defined somewhere, which marks the entry point into the program.  "main()" typically has void return type.

File Access Basics; Reading, Writing; Standard Input, Output

C uses streams for file management, just like Mathematica.  For reasons we won't go into here, a stream variable must be preceded by an asterisk in its declaration:

Apart from that peculiar detail, however, the open, close, and string-printing functions are quite similar:

In order to read or write variables, however, C must be told what sort of format the values will be in.  For reading from a file, "fscanf" (which stands for "file-scan-formatted") must first be told the stream variable ("fp" above), followed by a string with symbols indicating what is to be read in---"%d" means an integer, "%f" an approximate number, "%c" a character, and so forth---followed by the variable names to store the values in, each preceded by an ampersand ("&").  For writing to a file, "fprintf" (which stands for "file-print-formatted") must be told the stream variable ("fp") above, followed by a string with symbols indicating what it to be printed---characters interspersed with "%d" for integral output, "%f" for approximate numeric output, "%c" for single-character output, and so on---followed by the variable names whose values need to be written out.  These strings are the "formatting" of the input and the output referred to in the names of the functions.  Here are a couple of examples:

The code above prints a single line with the numeric value of "i" after the string "The answer is ".

Here, three characters are read from the file and placed in variables "c1", "c2", and "c3", respectively.

Finally, two files are always associated to a C program, "stdin" and "stdout".  The former, "stdin", is the "standard input" file, which typically means input from the keyboard.  The latter, "stdout", is the "standard output" file, typically meaning the console screen.  These two stream names "stdin" and "stdout" can be used with "fscanf" and "fprintf", or they can be omitted if one uses the functions "printf" and "scanf"---"printf" writes to the "standard output" file, while "scanf" reads from the "standard input" file.

Comments, Operators, Libraries

In C comments are enclosed not by "(*" and "*)" as in Mathematica, but by "/*" and "*/".

Many of the operators of C and Mathematica are identical.  Here are the most important:  "!" (not), "+", "-", "*", "/", "=", "==", "!=", "<", ">", "<=", ">=", "++", "--", "+=", "-=", "*=", "/=", "||", "&&", """ (quotation mark to mark beginning or ending of a string), and parentheses to show precedence in an expression.

Finally, just as Mathematica has separate packages which can be loaded to permit greater functionality, C programs can include functions from other libraries which add to the functions the program can use.  These are usually indicated at the top of the program with "#include <filename>" or "#include "filename" " statements.

Reading Examples

In this section we will present entire C programs and comment on their functionality.

1: primeTester

C does not have a prime-tester function like Mathematica's "PrimeQ".  Here we present a program which would allow a user to enter a machine-size integer and have its primality determined.  We use the fact that if an integer has no divisor smaller than its square root, it must be prime.  Note that when one divides two integers in C, the result is the integer quotient---the remainder is thrown away.

Here no "#include" statements were necessary, since we did not use any non-standard C functions. The program writes out the query line, reads in a number from the keyboard, and sets "b" to be its square root (rounded to the next lowest integer, since the result is placed in "b", an integer).  Then, one at a time, it decides if any of the numbers between 2 and "b" evenly divide "a", by taking the integer quotient, multiplying back by "c", and seeing if the result equals "a", which would only occur if "c" evenly divided "a".  If one does evenly divide, then a flag "d" is set to "1", indicating that the number is not prime.  A message indicating the primality is finally written out to the screen.

2: factorialComputer

Here we present a program which writes out "n" factorial, as "n" runs from 1 to 7, to a file "myFile", one number to a line.

Here we needed to include the standard input/output function library, "stdio.h".  Note that we could not have increased the number of factorials to 8 without changing the data types of the factorial function and its arguments, since 8! cannot be stored in a machine-size integer.

3: grades

We end with a version of the "Grades`" package defined in a previous section, converted to be a C program.  In order to implement the range of choices permitted by the package, we introduce a menu function which writes a list of options to the screen, one of which the user must indicate by entering appropriate data from the keyboard.

Let's look through this C program piece by piece.  The matter at the top reads in the appropriate input/output libraries of commands and declares several functions and data.

The function "gradeInit()" sets "numGrades" to 0 and zeros out the array "grades".

The function "addGrade()" takes an argument, and, assuming there is room in the array "grades" to add an additional grade to it, does so, incrementing "numGrades" in the process.

"deleteGrade()" must do more work.  First it looks through the array, attempting to find a match with the given argument.  If it does not find one, it complains to the operator; otherwise, it eliminates the grade by copying everything in the array left one position, beginning with the grade to be deleted.  Hence, if the original array contained '1,2,3,4,5', and '3' was deleted, then the '4' would be copies to the place of '3', the '5' to the place of '4', resulting in '1,2,4,5'.

"listGrades()" and "findAverage()" are fairly self-explanatory.  "findAverage" must check "numGrades" to insure that it does not divide by zero, and it returns the average as its return value.

Finally, we have below the portion of the program which implements the outer loop.  "main()" declares some variables, initializes the grades, and has an outer "do...while" loop based on the user's command, a character in the variable "c".  The first "do" loop inside loops until a valid character is entered from the keyboard.  Note that in C the input stream contains the return button character, which we must take out of the stream in order to process correctly the remaining characters.  (Otherwise the return character would be read in as the next command, an illegal one, and we would then get the menu printed again.)  If the command requires a grade, one is read in, and finally a switch statement controls the path of execution.