C Programming


Table of Contents

Preface
1. Chapter 1
Introduction
Advantages and Disadvantages of C
Dialects of C
2. Chapter 2
Your First C Program
A program that does nothing!
Compiling the program
Running the program
Adding Comments
3. The Basics of C
Layout of programs
main
header files and #include directives
functions
Variables
Introduction
Operators in C
Arithmetic operators in C
Relational and Logical Operators
Bitwise Operators
Other Operators
4. C Control Structures
Loops
Introduction
The While Statement
The Do-While Statement
The For Statement
5. Pointers
Introduction
The Type of a Pointer
Given the address how do we use it?
A correct swap_floats function
The void * type
Pointer Arithmetic and Arrays
6. Arrays
Introduction
Examples
Intialising Arrays
Multi-dimensional Arrays
7. Dynamic Memory Allocation
Introduction
malloc
Freeing memory
Dynamically Allocating Multidimensional Arrays
Method 1: Use a 1-D array with index arithmetic
Method 2: Use arrays of pointers
8. Strings in C
Introduction
Useful string functions
9. Functions
Introduction
Writing functions in programs
The return value of a function
Functions Arguments
Scoping
10. Structures
The Basics
Pointers to Structures
Using Structures with Fucntions
11. Input and Output
Introduction
Console I/O
Formatted output: the printf function
Reading input—the scanf command
File Input and Output
Formatted File I/O
12. Using the C Preprocessor
Introduction
#include
#define
13. The C Standard Library
Introduction
Leaving C programs in a hurry!
14. Using the C Math library
Using the Math library
Using the math library—an example
What math functions are there?
Math constants too
15. Chapter
What could we still learn in C?
16. Chapter
What Computer Science could we still learn?
17. Exercises
Various Exercises

Converted to DocBook/XML and updated by Chris Green, 2006.

In addition, there are some special operators that represent common programming idioms. In the following table, i and j are integers.

++Incrementi++ and ++i are equivalent to i = i + 1.
--Decrementi-- and --i are equivalent to i = i - 1.
+=Addition with assignmenti+=j is equivalent to i = i + j
-=Subtraction with assignmenti-=j is equivalent to i = i - j
*=Multiplication with assignmenti*=j is equivalent to i = i * j
/=Division with assignmenti/=j is equivalent to i = i / j
%Modulusi%j gives the reminader when i is divided by j.

Notice the increment and decrement operators have two forms: a postfix form, i++, and a prefix form, ++i. The two forms accomplish the same task, but they differ in when they accomplish it.

Fortunately, C provides a mechanism for requesting an amount of space from the operating system that does not require you to know the size ahead of time. The main routine for this is malloc (memory allocation); it is part of the Standard C Library (so you need to include the header file "stdlib.h" in your code to use it).

malloc is meant to be as general as possible—imagine how difficult it would be to have a separate allocation for function for each type! As such, malloc takes an unsigned integer (giving the number of bytes to allocate) as an argument, and has return type void *.

	
	<pointer variable> = (<type name> *) malloc (<memory size>)
	
	
<pointer variable> denotes the name of the variable which will hold the address of the start of the newly allocated memory block. It must be of type "pointer to <type name>".

Note also that we cast the result of malloc to the desired type. This is not strictly necessary, but it is very good programming practice. (In addition, some compilers might warn you about a type mismatch if you don't cast.) (The C FAQ gives an argument in support of not casting, but their argument mostly applies to much older compilers.)

The <memory size> is the number of bytes to allocate. Typically (for an array, say) this is the number of entries times the size of the type. The sizeof is very useful here for ensuring portability across platforms.

In the example below, we allocate an array of 10 doubles.

	
	   double *x;
	   x = (double *) malloc(sizeof(double)*10);
	
	

Occasionally, the operating system will not have enough free memory to fulfill your request. In such cases, malloc will return the special value NULL. It is a very good idea to always check the return value of malloc before using it. Attempting to dereference the NULL value results in a segmentation fault or access violation.

Dynamically allocating space for a multidimensional array can be tricky. There are basically two ways to do it.

In this option we allocate an array of pointers to pointers to pointers etc., then allocate each lower dimensional array. This method is more complicated to set up than Method 1, but is often easier to work with in your code, as you can use repeated subscripts [ ][ ][ ]...[ ] for indexing.

Let's redo the above example using this method.

		  /* put this at the top of the code */
		  #define NROWS 10
		  #define NCOLS 10

		  double **x;
		  int i, j;
		  int memerr = 0; /* sentinel for memory errors */

		  x = (double **) malloc(sizeof(double *)*NCOLS);
		  if ( x == NULL ) {
			 printf("Allocation error.\n");
			 exit(EXIT_FAILURE);
		  }
		  for ( i = 0; i < NCOLS; i++ ) {
			x[i] = (double *) malloc(sizeof(double)*NROWS);
			if ( x[i] == NULL ) {
				printf("Allocation error.\n")
				memerr = 1;
				break;
			}
		  }
		  if ( memerr == 1 ) {
		  	/* 
				free subarrays starting from where the allocation
				error occurred.
			*/
		  	for ( j = i ; j >= 0 ; j-- ) {
				free(x[i]);
			}
			free(x);
			exit(EXIT_FAILURE);
		  }

		  /* set x[2][2] = 0.0 */
		  x[2][2] = 0.0;
		  /* set x[0][2] = 0.0 */
		  x[0][2] = 0.0;

		  /* 

		  	Be a good geek!
			Don't create a memory leak!
			
		  */
		  for ( i = 0 ; i < NCOLS; i++ ) {
		  	free(x[i]);
		  }
		  free(x);
		  
		  
Notice the methodology used: we declared a pointer to a pointer, then allocated an array of pointers. After checking that the first allocation was successful, we allocate each column of the array. (We chose any array of columns, but an array of rows would also work.) If allocation fails, we set a flag variable memerr, then clean up before exiting. If all goes well, we can access the elements of x using double brackets. This works because x[i][j] is equivalent to (x[i])[j]. Finally, we clean up as usual, making sure to clean up each subelement before we clean up the main element.

Syntax:

		
			#include <stdio.h>

		    printf( <format string>, <arguments> )
		
		
<format string> is a string which may contain placeholders for any variables we want to display. These variable names follow in the argument list, separated by commas.

Let us begin with the integer placeholder holder %d. Example:

			#include <stdio.h>
			int main ()
			{
				printf("%d times %d = %d\n",2,4,2*4);
				return 0; 
			}
		
		
The output for this program is:
			2 times 4 = 8
		

Other common placeholders:

%dsigned decimal integers
%uunsigned decimal integers
%ereal numbers in scientific notation
%freal numbers in floating point form
%g%e or %f, whichever is shorter
%ounsigned octal
%xunsigned hexadecimal
%ccharacter
%sstring
%ppointer
%%percent sign
The precise format of the output can be further adjusted using modifiers. The "l" prefix specifies that the argument is a long (int, float): %ld, %lf. For long doubles use "L" instead of "l".

Field width and precision specifiers control the number of characters printed. A field width specifier should be an integer placed between the percent sign and the placeholder. This integer gives the minimum width of the output value; values shorter than this will be padded (by default with spaces; use a zero to pad with zeros). For example, %05d specifies that the integer value should be five characters wide, and integers with fewer than five digits should be printed with enough zeros added to the front to fill them out to five characters. Thus 1 would print as 00001 and 54321 prints as 54321. Values with more than five digits are left untouched by this specification.

A precision specification controls how many digits after the decimal point are printed. This specifier should be an integer, and should occur after a decimal point and before the placeholder, e.g., %.3f will print a float with three digits after the decimal point. When exponential notation is used the precision specifier controls the number of significant digits.

Precison and field width specifiers may be combined: %10.4f will print a float value in a field at least ten characters wide with four decimal places.

Precision and field width specifiers can be applied to strings; in this case the precision specifier gives the maximum width of the field. A string longer than this maximum will be truncated.

Finally, the minus sign - can be used to specify left justification instead of the default right justification. Example:

		
			#include <stdio.h>

			void main(void) {
				printf("Right justified: %8d\n", 500);
				printf("Left justified: %-8d\n", 500);
				return;
			}
		
		

The first step to accessing a file is to declare a variable of type FILE *.

				
					FILE *<file variable>
				
				
(Traditionally the file variable is called something like "fp", for "file pointer".)

The #define preprocessor directive is used to define compiler constants or macros. These constants can be values or functions.

An example: the constant M_PI in math.h is defined as

	
	#define M_PI 3.1415926535897932385E0
	
	
The compiler will replace all occurrences of the string M_PI in your code with '3.1415926535897932385E0'. (It won't check that this value makes sense, though, so be careful!) For example, suppose we had the following code.
	double x,y;
	y = sin(2.0 * M_PI);
	
In the resulting compiled code this is translated to the following.
	double x,y;
	y = sin(2.0 * 3.1415926535897932385E0);
	
	
This type of behavior would be useful if we later decided we wanted to use a less accurate approximation to pi, for instance: we'd only need to make the change in one place, rather than everywhere in the code.

#define can also be used to define things that are "function-like".

	#define <function name> <parameter list> <replacement>
	
<parameter list> is either empty or takes the form
	( <variable name 1>, <variable name 2>,... )
	
For example, we can define a function that adds two numbers as follows.
	#define ADD(x,y) x+y
	
When invoked in a program the inputs x and y will be substituted by the compiler into the expression x+y. The resulting expression will then be computed at run-time.

One caveat about macros: it's best to parenthesize arguments when you are unsure of what might be substituted in their place. For instance, suppose we had a macro for squaring a number.

	  #define SQR(x) x*x
	
This will work fine for SQR(4), but SQR(4+2) expands to 4+2*4+2, which will evaluate to 4+8+2=14, not 36. It would be safer to write the macro as
	  #define SQR(x) ((x)*(x))
	
The parentheses help ensure proper evaluation of the expression.

This construct is convenient, but you should not think of it as a formal function, as it does no type checking: it will happily try to add a character and a float, for instance. Nevertheless, macros such as the one above are often useful, particular for very simple functions.

A useful subset:

Note how we could have used the function atan2 in our example!

To find other functions try the apropos shell command. To get information on Bessel functions:

madrid1_2% apropos bessel
j0                   (3)  - Bessel functions
j0f [j0]             (3)  - Bessel functions
j0l [j0]             (3)  - Bessel functions
j1 [j0]              (3)  - Bessel functions
j1f [j0]             (3)  - Bessel functions
j1l [j0]             (3)  - Bessel functions
jn [j0]              (3)  - Bessel functions
jnf [j0]             (3)  - Bessel functions
jnl [j0]             (3)  - Bessel functions
y0 [j0]              (3)  - Bessel functions
y0f [j0]             (3)  - Bessel functions
y0l [j0]             (3)  - Bessel functions
y1 [j0]              (3)  - Bessel functions
y1f [j0]             (3)  - Bessel functions
y1l [j0]             (3)  - Bessel functions
yn [j0]              (3)  - Bessel functions
ynf [j0]             (3)  - Bessel functions
ynl [j0]             (3)  - Bessel functions

  1. Write a program which takes in one integer, n say, and displays all the prime numbers up to and including n.

  2. Write a program to display your name and address formatted into separate lines.

  3. Write a program which reads first an integer n, and then n floating point numbers. Calculate the mean and variance of these numbers.

  4. Write a program which reads in doubles and writes the square of the number in each case. Set up the program so that it terminates when one enters a value less than -999.

  5. Write a program which reads the coefficients (as double precision numbers) of the quadratic ax^2+bx+c, followed by a list of x's at which the quadratic is to be evaluated. Terminate the program when two successive values of x are the same.

  6. This exercise revolves around writing functions for arrays of doubles. Write the following functions:

  7. Write functions to add, subtract, multiply and divide complex numbers. Also write a function to calculate the modulus of a complex number.