So many instructions were removed in going from BCPL to B, that Dennis Ritchie of Bell Labs put some back in (in 1972), and called the language C.
The famous book The C Programming Language was written by Kernighan and Ritchie in 1978, and was the definitive reference book on C for almost a decade.
The original C was still too limiting, and not standardized, and so in 1983 an ANSI committee was established to formalise the language definition.
It has taken until now (ten years later) for the ANSI standard to become well accepted and almost universally supported by compilers.
main () { }
#include <stdio.h> main () { printf ("hello world\n"); }Notes:
Note also the strange character sequence `\n' in the string constant "hello world\n". `\n' is called a newline character, and is regarded as a single character in C (i.e., it occupies one byte of storage).
The value of a character constant is the numeric value of the character in the computer's character set (e.g., 'A' has the value 65). In 99.99% of cases this is the ASCII character set, but this is not defined by the standard!
But how do you represent a character such as a single quote itself? The answer is to use an escape sequence.
For reference, here is a program to print out all the special escape sequences:
/* A program to print out all the special C escape sequences */ /* Michael Ashley / UNSW / 04-May-1994 */ #include <stdio.h> /* for printf definition */ main () { printf ("audible alert (bell) BEL \\a %d\n" , '\a'); printf ("backspace BS \\b %d\n" , '\b'); printf ("horizontal tab HT \\t %d\n" , '\t'); printf ("newline LF \\n %d\n" , '\n'); printf ("vertical tab VT \\v %d\n" , '\v'); printf ("formfeed FF \\f %d\n" , '\f'); printf ("carriage return CR \\r %d\n" , '\r'); printf ("double quote \" \\\" %d\n", '\"'); printf ("single quote \' \\\' %d\n", '\''); printf ("question mark ? \\? %d\n" , '\?'); printf ("backslash \\ \\\\ %d\n", '\\'); }And here is the output it produces, when compiled with gcc on newt:
audible alert (bell) BEL \a 7 backspace BS \b 8 horizontal tab HT \t 9 newline LF \n 10 vertical tab VT \v 11 formfeed FF \f 12 carriage return CR \r 13 double quote " \" 34 single quote ' \' 39 question mark ? \? 63 backslash \ \\ 92Note: this program actually produces the wrong output when used with cc on newt!
In addition, you can specify any 8-bit ASCII character using either \ooo or \xhh where `ooo' is an octal number (with from 1 to 3 digits), and 'xhh' is a hexadecimal number (with 1 or 2 digits). For example, \x20 is the ASCII character for SPACE.
The above program also shows how to add comments to your C program.
String constants are a sequence of zero or more characters, enclosed in double quotes. For example, "test", "", "this is an invalid string" are all valid strings (you can't always believe what a string tells you!). String constants are stored in memory as a sequence of numbers (usually from the ASCII character set), and are terminated by a null byte (\0). So, "test" would appear in memory as the numbers 116, 110, 115, 116, 0.
We have already used some examples of strings in our programs, e.g, "hello world\n" was a null-terminated character string that we sent to the printf function above.
/* This is a comment */ /* here is another one that spans two lines */ i = /* a big number */ 123456;Here are some problems that can arise when using comments:
i = 123456; /* a comment starts here i = i + 2; this statement is also part of the comment */ /* this is a comment /* and so is this */ but this will generate an error */The fact that comments don't nest is a real nuisance if you want to comment-out a whole section of code. In this case, it is best to use pre-processor directives to do the job. For example:
i = 123456; #if 0 i = i + 2; j = i * i; #endif
The idea behind make is that you should be able to compile/link a program simply by typing make (followed by a carriage-return). For this to work, you have to tell the computer how to build your program, by storing instructions in a Makefile.
While this step takes some time, you are amply repaid by the time savings later on. make is particularly useful for large programs that consist of numerous source files: make only recompiles the file that need recompiling (it works out which ones to process by looking at the dates of last modification).
Here is an example of a simple Makefile:
test: test.o; cc test.o -o testNotes:
OBJS = main.o sub1.o sub2.o main: $(OBJS); cc $(OBJS) -o mainIt is well worth learning a little bit about make since it can save a lot of time!
cd mkdir hello cd hello cat > hello.c #include <stdio.h> main () { printf ("hello world\n"); } ^D cat > Makefile hello: hello.o; cc hello.o -o hello ^D make ./hello
/* A program to print out various machine-dependent constants */ /* Michael Ashley / UNSW / 04-May-1994 */ #include <stdio.h> /* for printf definition */ #include <limits.h> /* for CHAR_MIN, CHAR_MAX, etc */ #include <float.h> /* for FLT_DIG, DBL_DIG, etc */ main () { printf ("char %d bytes %d to %d \n", sizeof(char ), CHAR_MIN, CHAR_MAX ); printf ("unsigned char %d bytes %d to %d \n", sizeof(unsigned char ), 0 , UCHAR_MAX ); printf ("short %d bytes %hi to %hi \n", sizeof(short ), SHRT_MIN, SHRT_MAX ); printf ("unsigned short %d bytes %hu to %hu \n", sizeof(unsigned short), 0 , USHRT_MAX ); printf ("int %d bytes %i to %i \n", sizeof(int ), INT_MIN , INT_MAX ); printf ("unsigned int %d bytes %u to %u \n", sizeof(unsigned int ), 0 , UINT_MAX ); printf ("long %d bytes %li to %li \n", sizeof(long ), LONG_MIN, LONG_MAX ); printf ("unsigned long %d bytes %lu to %lu \n", sizeof(unsigned long ), 0 , ULONG_MAX ); printf ("float %d bytes %e to %e \n", sizeof(float ), FLT_MIN , FLT_MAX ); printf ("double %d bytes %e to %e \n", sizeof(double ), DBL_MIN , DBL_MAX ); printf ("precision of float %d digits\n", FLT_DIG); printf ("precision of double %d digits\n", DBL_DIG); }
char 1 bytes -128 to 127 unsigned char 1 bytes 0 to 255 short 2 bytes -32768 to 32767 unsigned short 2 bytes 0 to 65535 int 4 bytes -2147483648 to 2147483647 unsigned int 4 bytes 0 to 4294967295 long 4 bytes -2147483648 to 2147483647 unsigned long 4 bytes 0 to 4294967295 float 4 bytes 1.175494e-38 to 3.402823e+38 double 8 bytes 2.225074e-308 to 1.797693e+308 precision of float 6 digits precision of double 15 digits
int 123, -1, 2147483647, 040 (octal), 0xab (hexadecimal) unsigned int 123u, 2147483648, 040U (octal), 0X02 (hexadecimal) long 123L, 0XFFFFl (hexadecimal) unsigned long 123ul, 0777UL (octal) float 1.23F, 3.14e+0f double 1.23, 2.718281828 long double 1.23L, 9.99E-9LNote:
real*8 r r = 1.0 + 0.2 r = r - 1.2 write (*,*) r end
When compiled with 'f77' on newt, this gives the result '4.7683715864721423E-08', not zero as you might expect. The reason for this is that '1.0' and '0.2' are single precision numbers by default, and so the addition is only done to this precision. The number '1.2' is converted into binary with double precision accuracy, and so is a different number from '1.0 + 0.2'.
Interestingly, the above program gives the result '0.0000000000000000E+00' when compiled with 'f772.1' on newt, and '4.768371586472142E-08' when compiled with 'f77' on the CANCES HP cluster.
The correct way to write this program is as follows:
real*8 r r = 1.0D0 + 0.2D0 r = r - 1.2D0 write (*,*) r end
Here is the equivalent program written in C:
#include <stdio.h> main () { double r; r = 1.0 + 0.2; r = r - 1.2; printf ("%22.16e\n", r); }
In this case the result is '0.0000000000000000e+00', but this isn't really a fair comparison with our original FORTRAN program since floating point numbers are assumed to be 'double' in C, not 'real*4' as in FORTRAN. So let's go back to using 'float's instead:
#include <stdio.h> main () { double r; r = 1.0F + 0.2F; r = r - 1.2F; printf ("%22.16e\n", r); }
Now the program generates '-4.4703483581542969e-08' when compiled with 'cc' on newt, and yet it gives '0.0000000000000000e+00' when compiled with 'gcc', interesting...
The lesson to be learnt here is when writing constants, always think carefully about what type you want them to be, and use the suffixes 'U', 'L', and 'F' to be explicit about it. It is not a good idea to rely on the compiler to do what you expect. Don't be surprised if different machines give different answers if you program sloppily.
#include <stdio.h> main () { int i, j; i = 1.99; j = -1.99; printf ("i = %d; j = %d\n", i, j); }
This program produces the result 'i = 1; j = -1'. Note that the floating point numbers have been truncated when converted to integers (FORTRAN does the same thing).
When converting integers to floating-point, be aware that a 'float' has fewer digits of precision than an 'int', even though they both use 4 bytes of storage (on newt). This can result in some strange behaviour, e.g.,
#include <stdio.h> main () { unsigned int i; float f; i = 4294967295; /* the largest unsigned int */ f = i; /* convert it to a float */ printf ("%u %20.13e %20.13e\n", i, f, f - i); }
This program produces the following output when compiled with 'cc':
4294967295 4.2949672960000e+09 1.0000000000000e+00
and this output when compiled with 'gcc':
4294967295 4.2949672960000e+09 0.0000000000000e+00
Curiouser and curiouser... It appears that what is happening is that 'cc' is doing the calculation 'f - i' as a 'double', i.e., 'f' and 'i' are converted to type 'double', and then subtracted. Whereas 'gcc' is converting 'i' to type 'float' (just as was done with 'f = i'), and hence the subtraction results in zero. To test this hypothesis, you can force 'cc' to use a 'float' conversion by putting a type-cast operator before 'i'. Here it is
#include <stdio.h> main () { unsigned int i; float f; i = 4294967295; /* the largest unsigned int */ f = i; /* convert it to a float */ printf ("%u %20.13e %20.13e\n", i, f, f - (float)i); }
This program now gives the same results when used with 'cc' or 'gcc' (i.e., zero). Incidentally, 'gcc's behaviour without the '(float)' agrees with the ANSI standard.
Note the use of the type-cast operator '(float)'. This converts the number or variable or parethesised expression immediately to its right, to the indicated type. It is a good idea to use type-casting to ensure that you leave nothing to chance.
Some of the operators we have already seen (e.g., 'sizeof()'), others are very simple (e.g., +, -), others are really neat (e.g., ~, !), others are useful for adding/subtracting 1 automatically (e.g., ++i, --i, i++, i--), and the rest involve pointers and addressing, which will be covered in detail later.
sizeof(i) the number of bytes of storage allocated to i +123 positive 123 -123 negative 123 ~i one's complement (bitwise complement) !i logical negation (i.e., 1 if i is zero, 0 otherwise) *i returns the value stored at the address pointed to by i &i returns the address in memory of i ++i adds one to i, and returns the new value of i --i subtracts one from i, and returns the new value of i i++ adds one to i, and returns the old value of i i-- subtracts one from i, and returns the old value of i i[j] array indexing i (j) calling the function i with argument j i.j returns member j of structure i i->j returns member j of structure pointed to by i
Here is a list. All the usual operators that you would expect are there, with a whole bunch of interesting new ones.
+ addition - subtraction * multiplication / division % remainder (e.g., 2%3 is 2), also called 'modulo' << left-shift (e.g., i<<j is i shifted to the left by j bits) >> right-shift & bitwise AND | bitwise OR ^ bitwise exclusive-OR && logical AND (returns 1 if both operands are non-zero; else 0) || logical OR (returns 1 if either operand is non-zero; else 0) < less than (e.g., i<j returns 1 iff i is less than j) > greater than <= less than or equal >= greater than or equal == equals != does not equal ? conditional operator, explained later...Note:
= assignment += addition assignment -= subtraction assignment *= multiplication assignment /= division assignment %= remainder/modulus assignment &= bitwise AND assignment |= bitwise OR assignment ^= bitwise exclusive OR assignment <<= left shift assignment >>= right shift assignment
So, for example, 'i += j' is equivalent to 'i = i + j'. The advantage of the assignment operators is that they can reduce the amount of typing that you have to do, and make the meaning clearer. This is particularly noticeable when, instead of a simple variable such as 'i', you have something complicated such as 'position[wavelength + correction_factor * 2]';
The thing to the left of the assignment operator has to be something where a result can be stored, and is known as an 'lvalue' (i.e., something that can be on the left of an '='). Valid 'lvalues' include variables such as 'i', and array expressions. It doesn't make sense, however, to use constants or expressions to the left of an equals sign, so these are not 'lvalues'.
For example,
Example Equivalent to ------- ------------- i = ((j = 2), 3); i = 3; j = 2; myfunct (i, (j = 2, j + 1), 1); j = 2; myfunct (i, 3, 1);
The comma operator has the lowest precedence, so it is always executed last when evaluating an expression. Note that in the example given comma is used in two distinct ways inside an argument list for a function.
Both the above examples are artifical, and not very useful. The comma operator can be useful when used in 'for' and 'while' loops as we will see later.
The precedence of an operator gives the order in which operators are applied in expressions: the highest precedence operator is applied first, followed by the next highest, and so on.
The associativity of an operator gives the order in which expressions involving operators of the same precedence are evaluated.
The following table lists all the operators, in order of precedence, with their associativity:
Operator Associativity -------- ------------- () [] ->> . left-to-right - + ++ -- ! ~ * & sizeof (type) right-to-left * / % left-to-right + - left-to-right << >> left-to-right < <= > >= left-to-right == != left-to-right & left-to-right ^ left-to-right | left-to-right && left-to-right || left-to-right ?: right-to-left = += -= *= /= %= &= ^= |= <<= >>= right-to-left , left-to-right
Note: the + - and * operators appear twice in the above table. The unary forms (on the second line) have higher precedence that the binary forms.
Operators on the same line have the same precedence, and are evaluated in the order given by the associativity.
To specify a different order of evaluation you can use parentheses. In fact, it is often good practice to use parentheses to guard against making mistakes in difficult cases, or to make your meaning clear.
It is possible to write C expressions that give different answers on different machines, since some aspects of expression-evaluation are not defined by the ANSI standard. This is deliberate since it gives the compiler writers the ability to choose different evaluation orders depending on the underlying machine architecture. You, the programmer, should avoid writing expressions with side effects.
Here are some examples:
myfunc (j, ++j); /* the arguments may be the same, or differ by one */ array[j] = j++; /* is j incremented before being used as an index? */ i = f1() + f2(); /* the order of evaluation of the two functions is not defined. If one function affects the results of the other, then side effects will result */
A useful aspect of C is that it guarantees the order of evaluation of expressions containing the logical AND (&&) and OR (||) operators: it is always left-to-right, and stops when the outcome is known. For example, in the expression `1 || f()', the function `f()' will not be called since the truth of the expression is known regardless of the value returned by `f()'.
It is worth keeping this in mind. You can often speed up programs by rearranging logical tests so that the outcome of the test can be predicted as soon as possible.
Another good example is an expression such as `i >= 0 && i <n && array[i] == 0'. The compiler will guarantee that the index into `array' is within legal bounds (assuming the array has `n' elements).
auto double int struct break else long switch case enum register typedef char extern return union const float short unsigned continue for signed void default goto sizeof volatile do if static while
i count NumberOfAardvarks number_of_aardvarks MAX_LENGTH
Prototypes for the math functions are in the system include-file "math.h", so you should put the line
#include <math.h>in any C source file that calls one of them.
Here is a list of the math functions defined by the ANI standard:
sin(x) sine of x cos(x) cosine of x tan(x) tan of x asin(x) arcsine of x, result between -pi/2 and +pi/2 acos(x) arccosine of x, result between 0 and +pi atan(x) arctan of x, result between -pi/2 and +pi/2 atan2(y,x) arctan of (y/x), result between -pi and +pi hsin(x) hyperbolic sine of x hcos(x) hyperbolic cosine of x htan(x) hyperbolic tan of x exp(x) exponential function log(x) natural logarithm log10(x) logarithm to base 10 pow(x,y) x to the power of y (x**y in FORTRAN) sqrt(x) the square root of x (x must not be negative) ceil(x) ceiling; the smallest integer not less than x floor(x) floor; the largest integer not greater than x fabs(x) absolute value of x ldexp(x,n) x times 2**n frexp(x, int *exp) returns x normalized between 0.5 and 1; the exponent of 2 is in *exp modf(x, double *ip) returns the fractional part of x; the integral part goes to *ip fmod(x,y) returns the floating-point remainder of x/y, with the sign of xIn the above table, 'x', and 'y' are of type 'double', and 'n' is an 'int'. All the above functions return 'double' results.
C libraries may also include 'float' versions of the above. For example, 'fsin(x)' on newt takes a float argument and returns a float result. Microsoft C does not provide 'float' versions (presumably because the floating-point accelerator chips do all their work in double precision).
The basic looping construct in C is the `for' loop.
Here is the syntax of the `for' statement:
for (initial_expression; loop_condition; loop_expression) statement;
An example will clear this up:
for (i = 0; i < 100; i++) printf ("%i\n", i);
which simply prints the first 100 integers onto `stdout'. If you want to include more that one statement in the loop, use curly brackets to delimit the body of the loop, e.g.,
for (i = 0; i < 100; i++) { j = i * i; printf ("i = %i; j = %i\n", i, j); }
cc -o prog prog.c -lm
The `-l' switch stands for `library', which means that the specified library of pre-compiled C routines is searched in order to satisfy any external references from yoru program `prog.c'. The library that is searched in this case is `libm.a' and the path that is used for the search is the default library search path, which include `/usr/lib' where 'libm.a' is found.
To use a library in a directory that is not part of the default library search path, you use the '-L' switch. For example, to search the library '/usr/users/smith/libastro.a', you would use
cc -o prog prog.c -L/usr/users/smith -lastro
Note: the order of the switches is important. External references are only searched for in libraries to the right of the reference. So, if you have two libraries that call each other, then you need to do something like the following:
cc -o prog prog.c -L/usr/users/smith -llib1 -llib2 -llib1
Here is a simple example of calling the math library:
#include <stdio.h> #include <math.h> main () { const double pi = 3.1415926535; double e, d = pi/2; e = sin(d); printf ("The sine of %f is %f\n", d, e); }
This program produces the result:
The sine of 1.570796 is 1.000000
However, if you leave off the `#include <math.h>' line, you will get
The sine of 1.570796 is 4.000000
Why, because the default type for an undefined function is `extern int function();'
The last statement in the body of statements in a `for' loop (or, in fact, in any other compound statement) must be terminated with a semicolon.
For example,
for (i = 0; i < 10; i++) { x = i * i; x += 2; /* the semicolon is required here */ } /* do not use a semicolon here */<h3>#include
The example programs I showed last time didn't always have `#include <stdio.h>' at the top. They should have had this line (although they will work without it), since it defines the prototypes of the I/O functions, thereby guarding against errors.
You can create variables that are local to a compound statement by declaring the variables immediately after the leading curly bracket.
Variables in C belong to one of two fundamental storage classes: `static' or `automatic'.
A static variable is stored at a fixed memory location in the computer, and is created and initialised once when the program is first started. Such a variable maintains its value between calls to the block (a function, or compound statement) in which it is defined.
An automatic variable is created, and initialised, each time the block is entered (if you jump in half-way through a block, the creation still works, but the variable is not initialised). The variable is destroyed when the block is exited.
Variables can be explicitly declared as `static' or `auto' by using these keywords before the data-type definition. If you don't use one of these keywords, the default is `static' for variables defined outside any block, and `auto' for those inside a block.
Actually, there is another storage class: `register'. This is like `auto' except that it asks the compiler to try and store the variable in one of the CPU's fast internal registers. In practice, it is usually best not to use the `register' type since compilers are now so smart that they can do a better job of deciding which variables to place in fast storage than you can.
Variables can be qualified as `const' to indicate that they are really constants, that can be initialised, but not altered.
Variables can also be termed `volatile' to indicate that their value may change unexpectedly during the execution of the program (e.g., they may be hardware registers on a PC, able to be altered by external events). By using the `volatile' qualifier, you prevent the compiler from optimising the variable out of loops.
Variables (and functions) can also be classified as `extern', which means that they are defined external to the current block (or even to the current source file). An `extern' variable must be defined once (and only once) without the `extern' qualifier.
As an example of an `extern' function, all the functions in `libm.a' (the math library) are external to the source file that calls them.
#include <stdio.h> int i; /* i is static, and visible to the entire program */ extern j; /* j is static, and visible to the entire program */ static int j; /* k is static, and visible to the routines in this source file */ void func (void) { /* i.e., a function that takes no arguments, and doesn't return a value */ int m = 1; /* m is automatic, local to this function, and initialised each time the function is called */ auto int n = 2; /* n is automatic, local to this function, and initialised each time the function is called */ static int p = 3; /* p is static, local to this function, and initialised once when the program is first started up */ extern int q; /* q is static, and defined in some external module */ for (i = 0; i < 10; i++) { int m = 10; /* m is automatic, local to this block, and initialised each time the block is entered */ printf ("m = %i\n", m); } }
A variable is initialised by equating it to a constant expression on the line in which it is defined. For example
int i = 0;
`static' variables are initialised once (to zero if not explicitly initialised), `automatic' variables are initialised when the block in which they are defined is entered (and to an undefined value if not explicitly initialised).
The `constant expression' can contain combinations of any type of constant, and any operator (except assignment, incrementing, decrementing, function call, or the comma operator), including the ability to use the unary & operator to find the address of static variables.
Here are some valid examples:
#include <stdio.h> #include <math.h> int i = 0; int j = 2 + 2; int k = 2 * (3 << 8) / 3; int m = (int)(&i + 2); int p = sizeof(float) * 2; int q = sizeof(p); float r = (float)(2 * 3); main () { printf ("i = %i\n", i); printf ("j = %i\n", j); printf ("k = %i\n", k); printf ("m = %i\n", m); printf ("p = %i\n", p); printf ("q = %i\n", q); printf ("r = %f\n", r); for (r = 0.0; r < 1.0; r += 0.1) { double s = sin(r); printf ("The sine of %f is %f\n", r, s); } }
Notes:
if (expression) statement else if (expression) statement else if (expression) statement else statement
Where `statement' is a simple C statement ending in a semicolon, or a compound statement ending in a curly bracket.
Some examples will help:
if (i == 6) x = 3; if (i == 6) { x = 3; } if (i) x = 3; if (i) x = 3; else if (i ==1) if (j) y = 2; else /* NOTE: possible ambiguity here, the compiler uses */ y = 3; /* the closest if statement that does not have */ else { /* an else clause */ x = 4; y = 5; }
`break' exits the innermost current loop.
`continue' starts the next iteration of the loop.
for (;;;) { statement; } while (1) { statement; } do { statement; } while (1);
Infinite loops can be useful. They are normally terminated using a conditional test with a `break' or `return' statement.
C does have a `goto' statement, but you don't need it. Using `goto' is almost always a result of bad programming.
The format string given to the `printf' function may contain both ordinary characters (which are simply printed out) and conversion characters (beginning with a percent symbol, %, these define how the value of an internal variable is to be converted into a character string for output).
Here is the syntax of a conversion specification:
%{flags: - + space 0 #}{minimum field width}{.}{precision}{length modifier}{conversion character}
Character Type Result d,i int signed decimal integer o int unsigned octal (no leading zero) x, X int unsigned hex (no leading 0x or 0X) u int unsigned decimal integer c int single character s char * characters from a string f double floating point [-]dddd.pppp e, E double exponential [-]dddd.pppp e[=/-]xx g, G double floating is exponent less than -4, or >= precision else exponential p void * pointer n int * the number of characters written so far by printf is stored into the argument (i.e., not printed) % print %
Here is an example program to show some of these effects:
#include <stdio.h> main () { int i = 123; double f = 3.1415926535; printf ("i = %i\n", i); printf ("i = %o\n", i); printf ("i = %x\n", i); printf ("i = %X\n", i); printf ("i = %+i\n", i); printf ("i = %8i\n", i); printf ("i = %08i\n", i); printf ("i = %+08i\n", i); printf ("f = %f\n", f); printf ("f = %10.3f\n", f); printf ("f = %+10.3f\n", f); printf ("f = %g\n", f); printf ("f = %10.6g\n", f); printf ("f = %10.6e\n", f); }
Notes:
For example:
scanf ("%d", &i); /* reads an integer into `i' */ scanf ("%i", &i); /* reads an integer (or octal, or hex) into `i' */ scanf ("%f %i", &f, &i); /* reads a double followed by an integer */
scanf is actually an integer function, which returns the number of input items assigned (or EOF if the end-of-file is reached or an error occurred).
The ampersand character `&' is a unary operator that returns the address of the thing to its right. Recall that a C function can not alter the value of its arguments (but there is nothing stopping it altering that value that is pointed to by one of its arguments!).
/* interest.c, by Tom Boutell, 6/27/93. Updated 6/29/93 to support user input. */ /* This program calculates the balance of a savings or loan account after a number of years specified by the user, with an interest rate, monthly payment, initial balance and rate of compounding specified by the user. */ /* Get standard input and output functions */ #include <stdio.h> /* Get standard math functions */ #include <math.h> int main() { /* Initial balance (money in account). Since this value can have a fractional part, we declare a float (floating point) variable to store it. */ float initial_balance; /* Rate of interest, per year (also a floating point value) */ float interest; /* Number of times interest is compounded each year (interest periods) (thus 1.0 is annually, 365.0 is daily) */ float frequency; /* Time in years */ float years; /* Total interest periods. This cannot have a fractional part, so we declare an integer (no fractional part) variable to store it. */ int interest_periods; /* Current balance. (We store this in a separate place form the initial balance, so we will still be able to tell how much money we started with when the calculation is finished.) */ float balance; /* Counter of interest periods */ int i; /* Monthly deposit (negative values are permitted) */ float deposit; /* Flag: when this is set true (nonzero), the user is finished */ int done; /* User input: analyze again? */ int again; /* Initially, of course, we are *not* finished. (C does NOT automatically set variables to zero. Making this assumption is a common mistake among new programmers.) */ done = 0; /* Loop until done. */ while (!done) { /* Fetch starting values from user */ printf("Initial balance: "); scanf("%f", &initial_balance); printf("Interest rate (example: .05 for 5 percent): "); scanf("%f", &interest); printf("Number of compoundings per year (12 = monthly, 365 = daily): "); scanf("%f", &frequency); printf("Monthly deposit (enter negative value for loan payment): "); scanf("%f", &deposit); printf("Number of years (examples: 1, 5, .5): "); scanf("%f", &years); /* Actual logic begins here. */ /* Calculate number of interest periods. */ interest_periods = frequency * years; /* Set working balance to begin at initial balance. */ balance = initial_balance; /* Loop through interest periods, increasing balance */ for (i=0; (i < interest_periods); i++) { /* Add deposit. User enters deposit in terms of a monthly deposit, so calculate how much this comes out to over a given interest period. This isn't 100% accurate because the deposit begins earning interest on the fraction of it "deposited" in one interest period, when in fact the whole amount should be added at the beginning of the month. Feel free to try to improve on this. */ balance += deposit * ( (1.0/frequency) * 12.0); /* Each period, multiply balance by 1.0 plus the annual rate of interest divided by the number of times per year (frequency) with which interest is compounded. */ balance = balance * (1.0 + (interest/frequency)); } /* Print out result */ printf("Initial balance: %f Final balance: %f\n", initial_balance, balance); printf("Enter 1 for another analysis, or 0 to quit: "); scanf("%d", &again); if (again) { done = 0; } else { done = 1; } } /* Everything went fine */ return 0; }
cc -g -O0 main.c dbx a.out quit[!] - quit dbx run arg1 arg2 ... { f1 }& f2 - begin execution of the program stop at {line} - suspend execution at the line [n] cont {signal} - continue with signal return - continue until the current procedure returns print {exp} ... - print the value of the expressions printf "string", exp, ... - print expressions using format string(C) where [n] - print currently active procedures (stack trace) status - print trace/stop/record's in effect func {proc} - move to activation level of {proc} {exp}[/ | ?]{count}{format} - display count number of formatted memory items file {file} - change current file to file list {exp1},{exp2} - list source lines from {exp1} to {exp2} list {exp}:{int} - list source lines at {exp} for {int} lines sh {shell command} - perform shell command
Arrays are declared in C as follows (for example):
int counts[100]; float temperature[1024];
In this example, `count' is an array that can hold 100 integers, and `temperature' is an array that can hold 1024 floats.
So far so good. The major departure from FORTRAN is that the first element in a C array is element number 0 (rather than 1 as in FORTRAN). While this may be confusing to die-hard FORTRAN programmers, it is really a more natural choice. A side-effect of this choice is that the last element in an array has an index that is one less that the declared size of the array. This is a source of some confusion, and something to watch out for.
To initialise an array, specify the initial values in a list within curly brackets. For example:
int primes[100] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541}; float temp[1024] = {5.0F, 2.3F}; double trouble[] = {1.0, 2.0, 3.0};
In this example, `primes' is initialised with the values of the first 100 primes (check them!), and the first two elements (temp[0] and temp[1]) of `temp' are initialised to 5.0F and 2.3F respectively. The remaining elements of `temp' are set to 0.0F. Note that we use the trailing `F' on these numbers to indicate that they are floats, not doubles.
The array `trouble' in the above example contains three double numbers. Note that its length is not explicitly declared, C is smart enough to work the length out.
Static arrays that are not explicitly initialised are set to zero. Automatic arrays that are not explicitly initialised, have undefined values.
Multidimensional arrays are declared and referenced in C by using multiple sets of square brackets. For example,
int table[2][3][4];
`table' is a 2x3x4 array, with the rightmost array subscript changing most rapidly as you move through memory (the first element is `table[0][0][0]', the next element is `table[0][0][1]', and the last element is `table[1][2][3]'.
When writing programs that use huge arrays (say, more than a few megabytes), you should be very careful to ensure that array references are as close as possible to being consecutive (otherwise you may get severe swapping problems).
This is best demonstrated with an example:
int mat[3][4] = { { 0, 1, 2}, { 3, 4, 5}, { 6, 7, 8} }
Note that I have only initialised a 3x3 subset of the 3x4 array. The last column of each row will have the default initialisation of zero.
Arrays can be of any type. Character arrays hold a single character in each element. In C, you manipulate character strings as arrays of characters, and operations on the strings (such as concatenation, searching) are done by calling special libary functions (e.g., strcat, strcmp).
Note that when calling a string-manipulation function, the end of the string is taken as the position of the first NUL character (0) in the string.
Character arrays can be initialised in the following ways:
char str[] = {'a', 'b', 'c'}; char prompt[] = "please enter a number";
In the example, `str' has length 3 bytes, and `prompt' has length 22 bytes, which is one more than the number of characters in "please enter a number", the extra character is used to store a NUL character (zero) as an indication of the end of the string. `str' does not having a trailing NUL.
If there is one thing that sets C apart from most other languages, it is the use of pointers. A `pointer' is a variable containing the address of a memory location.
Suppose `p' is a pointer, then `*p' is the thing which `p' points to.
Suppose `i' is being pointed at by `p', then `i' and `*p' are the same thing, and `p', being equal to the address of `i', is equal to `&i' (remember, `&' is the unary address operator).
Pointers are declared by specifying the type of thing they point at. For example,
int *p;
defines `p' as a pointer to an int (so, therefore, `*p' is an int, hence the form of the declaration).
Note carefully, then by declaring `p' as in the above example, the compiler simply allocates space for the pointer (4 bytes in most cases), not for the variable that the pointer points to! This is a very important point, and is often overlooked. Before using `*p' in an expression, you have to ensure that `p' is set to point to a valid int. This `int' must have had space allocated for it, either statically, automatically, or by dynamically allocating memory at run-time (using a `malloc' function).
Here is an example showing some of the uses of pointers:
#include <stdio.h> void main (void) { int m = 0, n = 1, k = 2; int *p; char msg[] = "hello world"; char *cp; p = &m; /* p now points to m */ *p = 1; /* m now equals 1 */ k = *p; /* k now equals 1 */ cp = msg; /* cp points to the first character of msg */ *cp = 'H'; /* change the case of the 'h' in msg */ cp = &msg[6]; /* cp points to the 'w' */ *cp = 'W'; /* change its case */ printf ("m = %d, n = %d, k = %d\nmsg = \"%s\"\n", m, n, k, msg); }
Note the very important point that the name of an array (`msg' in the above example), if used without an index, is considered to be a pointer to the first element of the array. In fact, an array name followed by an index is exactly equivalent to a pointer followed by an offset. For example,
#include <stdio.h> void main (void) { char msg[] = "hello world"; char *cp; cp = msg; cp[0] = 'H'; *(msg+6) = 'W'; printf ("%s\n", msg); printf ("%s\n", &msg[0]); printf ("%s\n", cp); printf ("%s\n", &cp[0]); }
Note, however, that `cp' is a variable, and can be changed, whereas `msg' is a constant, and is not an lvalue.
We have already seen that C functions can not alter their arguments. They can, however, alter the variables that their arguments point to. Hence, by passing a pointer to a variable, one can get the effect of `call by reference'.
Here is an example of a function that swaps the values of its two arguments:
void swap_args (int *pi, int *pj) { int temp; temp = *pi; *pi = *pj; *pj = temp; }
If all the asterisks were left out of this routine, then it would still compile OK, but its only effect would be to swap local copies of `pi' and `pj' around.
The standard C libraries include a bunch of functions for manipulating strings (i.e., arrays of chars).
Note that before using any of these functions, you should include the line "#include <string.h>" in your program. This include-file defines prototypes for the following functions, as well as defining special types such as `size_t', which are operating-system dependent.
char *strcat (s1, s2) Concatenates s2 onto s1. null-terminates s1. Returns s1. char *strchr (s, c) Searches string s for character c. Returns a pointer to the first occurence, or null pointer if not. int strcmp (s1, s2) Compares strings s1 and s2. Returns an integer that is less than 0 is s1 < s2; zero if s1 = s2; and greater than zero is s1 > s2. char *strcpy (s1, s2) Copies s2 to s1, returning s1. size_t strlen (s) Returns the number of characters in s, excluding the terminating null. char *strncat (s1, s2, n) Concatenates s2 onto s1, stopping after `n' characters or the end of s2, whichever occurs first. Returns s1. int strncmp (s1, s2, n) Like strcmp, except at most `n' characters are compared. char *strncpy (s1, s2) Like strcpy, except at most `n' characters are copied. char *strrchr (s, c) Like strchr, except searches from the end of the string. int strstr (s1, s2) Searches for the string s2 in s1. Returns a pointer if found, otherwise the null-pointer. size_r strspn (s1, s2) Returns the number of consecutive characters in s1, starting at the beginning of the string, that are contained within s2. size_r strcspn (s1, s2) Returns the number of consecutive characters in s1, starting at the beginning of the string, that are not contained within s2. char *strpbrk (s1, s2) Returns a pointer to the first character in s1 that is present in s2 (or NULL if none). char *strerror (n) Returns a pointer to a string describing the error code `n'. char *strtok (s1, s2) Searches s1 for tokens delimited by characters from s1. The first time it is called with a non-null s1, it writes a NULL into s1 at the first position of a character from s2, and returns a pointer to the beginning of s1. When strtok is then called with a null s1, it finds the next token in s1, starting at one position beyond the previous null.
The following program illustrates the use of `strtok', the most complex of the string functions.
#include <string.h> #include <stdio.h> int main (void) { char s1[80]; char s2[80]; char *cp; if (gets(s1) == (char *)NULL) { /* gets returns the next line of input from stdio, a null if there isn't one */ printf ("end of file\n"); return 1; } if (gets(s2) == (char *)NULL) { printf ("end of file\n"); return 1; } cp = strtok (s1, s2); do { printf ("<%s>\n", cp); } while ((cp = strtok ((char *)NULL, s2)) != (char *)NULL); return 0; }
<stdio> defines a number of functions that are used for accessing files. Before using a file, you have to declare a pointer to it, so that it can be referred to with the functions. You do this with, for example,
FILE *in_file; FILE *out_file;
where `FILE' is a type defined in <stdio> (it is usually a complicated structure of some sort). To open a file, you do, for example:
in_file = fopen ("input_file.dat", "r"); out_file = fopen ("output_file.dat", "w");
Note the use of "r" to indicate read access, and "w" to indicate write access. The following modes are available:
"r" read "w" write (destroys any existing file with the same name) "rb" read a binary file "wb" write a binary file (overwriting any existing file) "r+" opens an existing file for random read access, and writing to its end "w+" opens a new file for random read access, and writing to its end (destroys any existing file with the same name)
Once the file is opened, you can use the following functions to read/write it:
int getc (FILE *fp) Returns the next character from `fp', or EOF on error or end-of-file. int putc (int c, FILE *fp) Write the character c to the file `fp', returning the character written, or EOF on error. int fscanf (FILE *fp, char *format, ...) Like scanf, except input is taken from the file fp. int fprintf (FILE *fp, char *format, ...) Like printf, except output is written to the file fp. char *fgets (char *line, int n, FILE *fp) Gets the next line of input from the file fp, up to `n-1' characters in length. The newline character is included at the end of the string, and a null is appended. Returns `line' if successful, else NULL if end-of-file or other error. int fputs (char *line, FILE *fp) Outputs the string `line' to the file fp. Returns zero is successful, EOF if not.
When you have finished with a file, you should close it with 'fclose':
int fclose (FILE *fp) Closes the file 'fp', after flushing any buffers. This function is automatically called for any open files by the operating system at the end of a program.
It can also be useful to flush any buffers associated with a file, to guarantee that the characters that you have written have actually been sent to the file:
int fflush (FILE *fp) Flushes any buffers associated with the file 'fp'.
To check the status of a file, the following functions can be called:
int feof (FILE *fp) Returns non-zero when an end-of-file is read. int ferror (FILE *fp) Returns non-zero when an error has occurred, unless cleared by clearerr. void clearerr (FILE *fp) Resets the error and end-of-file statuses. int fileno (FILE *fp) Returns the integer file descriptor associated with the file (useful for low-level I/O).
Note that the above `functions' are actually defined as pre-processor macros in C. This is quite a common thing to do.
Any programming language worth its salt has to allow you to manipulate more complex data types that simply ints, and arrays. You need to have structures of some sort. This is best illustrated by example:
To define a structure, use the following syntax:
struct time { int hour; int minute; float second; };
This defines a new data type, called `time', which contains 3 elements (packed consecutively in memory). To declare a variable of type `time', do the following:
struct time t1[10], t2 = {12, 0, 0.0F};
To refer to an individual element of a structure, you use the following syntax:
t1[0].hour = 12; t1[0].minutes = 0; t1[0].second = t2.second; t1[1] = t2;
Structures can contain any type, including arrays and other structures. A common use is to create a linked list by having a structure contain a pointer to a structure of the same type, for example,
struct person { char *name[80]; char *address[256]; struct person *next_person; };
As an example of how to program in C, let's explore the topic of multiple precision arithmetic. All the hard work will be done by GNU's Multiple Precision Arithmetic Library (GNU MP), written by Torbjorn Granlund. The version I will be using here is 1.3.2, obtained from archie.au on 9 August 1994.
Multiple precision arithmetic allows you to perform calculations to a greater precision than the host computer will normally allow. The penalty you pay is that the operations are slower, and that you have to call subroutines to do all the calculations.
GNU MP can perform operations on integers and rational numbers. It uses preprocessor macros (defined in gmp.h) to define special data types for storing these numbers. MP_INT is an integer, and MP_RAT is a rational number. However, since a multiple precision number may occupy an arbitrarily large amount of memory, it is not sufficient to allocate memory for each number at compile time. GNU MP copes with this problem by dynamically allocating more memory when necessary.
To begin with you need to initialise each variable that you are going to use. When you are finished using a variable, you should free the space it uses by calling a special function.
MP_INT x, y; mpz_init (&x); mpz_init (&y); /* operations on x and y */ mpz_clear (&x); mpz_clear (&y);
Let's now try a full program. For example, calculating the square root of two to about 200 decimal places:
#include <gmp.h> #include <stdio.h> void main (void) { char two[450], guess[225]; int i; MP_INT c, x, temp, diff; two[0] = '2'; for (i = 1; i < sizeof(two)-1; i++) { two[i] = '0'; } two[i] = 0; mpz_init_set_str (&c, two, 10); guess[0] = '1'; for (i = 1; i < sizeof(guess)-1; i++) { guess[i] = '0'; } guess[i] = 0; mpz_init_set_str (&x, guess, 10); mpz_init (&temp); mpz_init (&diff); do { mpz_div (&temp, &c, &x); mpz_sub (&diff, &x, &temp); mpz_abs (&diff, &diff); mpz_add (&x, &temp, &x); mpz_div_ui (&x, &x, 2U); } while (mpz_cmp_ui (&diff, 10U) > 0); printf ("the square root of two is approximately "); mpz_out_str (stdout, 10, &x); }
To compile, link, and run the program, use
cc -o two two.c -I/usr/local/include -L/usr/local/lib -lgmp ./two
#define PI 3.1415926535 #define SQR(a) (sqrarg=(a),sqrarg*sqrarg) #include "filename" /* from the current directory */ #include/* from the system directories (modified by -I) */ #define DEBUG /* defines the symbol DEBUG */ #ifdef DEBUG /* code here is compiled if DEBUG is defined */ #elif defined UNIX /* code here is compiled if DEBUG is not defined, and UNIX is defined */ #else /* code here is compiled if neither DEBUG or UNIX are defined */ #endif #if 0 /* code here is never compiled */ #endif
#include <stdio.h> void main (int argc, char *argv[]) { printf ("this program is called '%s'\n", argv[0]); if (argc == 1) { printf ("it was called without any arguments\n"); } else { int i; printf ("it was called with %d arguments\n", argc - 1); for (i = 1; i < argc; i++) { printf ("argument number %d was <%s>\n", i, argv[i]); } } exit (argc); }echo $status
#include <stdio.h> #include <stdlib.h> /* Generates a two dimensional matrix, and fills it randomly with zeroes and ones. */ void main (void) { int xdim, ydim; int i, j; int *p, *q; printf ("x dimension of matrix? > "); scanf ("%d", &xdim); printf ("y dimension of matrix? > "); scanf ("%d", &ydim); p = (int *) malloc (xdim * ydim * sizeof(int)); if (p == NULL) { printf ("malloc failed!\n"); return; } for (i = 0; i < xdim * ydim; i++) { if (rand() > RAND_MAX/2) { *(p+i) = 1; } else { *(p+i) = 0; } } for (i = 0; i < xdim; i++) { q = p + i * ydim; for (j = 0; j < ydim; j++) { printf ("%d ", *(q++)); } printf ("\n"); } free ((void *)p); }
Write a C program that will accept an arbitrarily long list of real numbers from stdin, one per line, and will output the list sorted into ascending order. You should use "malloc" to obtain space to store the numbers. You do not know in advance how many numbers to expect, so you will have to "malloc" on-the-fly as necessary.
In addition, you will each be assigned one of the following exercises:
this is a test of the program
It should return
tset a si siht margorp eht fo
You are not allowed to use the C library function for reversing strings!
this is a test of the program
It should return
this is a test of the program
this is a test of the program
It should return
1 2 1 2 0 0 1
/* This is an example of a "while" loop */ #include <stdio.h> main() { int count; count = 0; while (count < 6) { printf ("The value of count is %d\n", count); count = count + 1; } }
/* This is an example of a do-while loop */ #include <stdio.h> main() { int i; i = 0; do { printf("The value of i is now %d\n",i); i = i + 1; } while (i < 5); }
/* This is an example of a for loop */ #include <stdio.h> main() { int index; for (index = 0; index < 6; index++) printf ("The value of the index is %d\n",index); }
/* This is an example of the if and the if-else statements */ #include <stdio.h> main() { int data; for (data = 0; data < 10; data++) { if (data == 2) printf("Data is now equal to %d\n",data); if (data < 5) printf("Data is now %d, which is less than 5\n",data); else printf("Data is now %d, which is greater than 4\n",data); } /* end of for loop */ }
#include <stdio.h> main() { int xx; for (xx = 5; xx < 15; xx++){ if (xx == 8) break; printf("In the break loop, xx is now %d\n",xx); } for(xx = 5;xx < 15;xx = xx + 1){ if (xx == 8) continue; printf("In the continue loop, xx is now %d\n",xx); } }
#include <stdio.h> main() { int truck; for (truck = 3;truck < 13;truck = truck + 1) { switch (truck) { case 3 : printf("The value is three\n"); break; case 4 : printf("The value is four\n"); break; case 5 : case 6 : case 7 : case 8 : printf("The value is between 5 and 8\n"); break; case 11 : printf("The value is eleven\n"); break; default : printf("It is one of the undefined values\n"); break; } /* end of switch */ } /* end of for loop */ }
#include <stdio.h> main() { int dog,cat,pig; goto real_start; some_where: printf("This is another line of the mess.\n"); goto stop_it; /* the following section is the only section with a useable goto */ real_start: for(dog = 1;dog < 6;dog = dog + 1) { for(cat = 1;cat < 6;cat = cat + 1) { for(pig = 1;pig < 4;pig = pig + 1) { printf("Dog = %d Cat = %d Pig = %d\n",dog,cat,pig); if ((dog + cat + pig) > 8 ) goto enough; }; }; }; enough: printf("Those are enough animals for now.\n"); /* this is the end of the section with a useable goto statement */ printf("\nThis is the first line out of the spaghetti code.\n"); goto there; where: printf("This is the third line of spaghetti.\n"); goto some_where; there: printf("This is the second line of the spaghetti code.\n"); goto where; stop_it: printf("This is the last line of this mess.\n"); }
/****************************************************************/ /* */ /* This is a temperature conversion program written in */ /* the C programming language. This program generates */ /* and displays a table of farenheit and centigrade */ /* temperatures, and lists the freezing and boiling */ /* of water. */ /* */ /****************************************************************/ #include <stdio.h> main() { int count; /* a loop control variable */ int farenheit; /* the temperature in farenheit degrees */ int centigrade; /* the temperature in centigrade degrees */ printf("Centigrade to Farenheit temperature table\n\n"); for(count = -2;count <= 12;count = count + 1){ centigrade = 10 * count; farenheit = 32 + (centigrade * 9)/5; printf(" C =%4d F =%4d ",centigrade,farenheit); if (centigrade == 0) printf(" Freezing point of water"); if (centigrade == 100) printf(" Boiling point of water"); printf("\n"); } /* end of for loop */ }