Sunday, March 18, 2007

Starting C Language

What is C?

C is a programming language invented in the early 1970s by Dennis Ritchie as a language for writing operating systems.

Here is a short overview and history of C.

The purpose of C is to precisely define a series of operations that a computer can perform to accomplish a task. Most of these operations involve manipulating numbers and text, but anything that the computer can physically do can be programmed in C.

Computers have no intelligence- they have to be told exactly what to do and this is defined by the programming language you use. Once programmed they can repeat the steps as many times as you wish at very high speed. Modern PCs are so fast they can count to a billion in a second or two.
...................................................................................................................................

Lets write a simple program.

In your editor type the following C code and save it as first.c.

#include
void main()
{
printf("\nHello World\n");
}

Compiling the program.

After you save the program you have to compile the program. This creates an executable file first.exe, which is then executed simply by typing its name.

The result is that the characters " Hello World'' are printed out, preceded by an empty line.

Explanation of the above program

Our first.c code calls printf, an output function from the I/O (input/output) library (defined in the file stdio.h). The original C language did not have any built-in I/O statements whatsoever. Nor did it have much arithmetic functionality. The original language was really not intended for ''scientific'' or ''technical'' computation.. These functions are now performed by standard libraries, which are now part of ANSI C. The K & R textbook lists the content of these and other standard libraries in an appendix.

The printf line prints the message ``Hello World'' on ``stdout'' (the output stream corresponding to the X-terminal window in which you run the code); ``\n'' prints a ``new line'' character, which brings the cursor onto the next line. By construction, printf never inserts this character on its own.

Constants & Variable Types

Variables

A variable must be declared before it can be used. Variables can be declared at the start of any block of code, but most are found at the start of each function. Most local variables are created when the function is called, and are destroyed on return from that function.

A declaration begins with the data type, followed by the name of one or more variables.
For example,
int temp1, temp2, temp[20];

The above can be also be declared as follows.

int temp1;
int temp2;
int temp[20];

Variables can also be initialised when they are declared

int temp1=50;

int temp2=25;

Data Types

C provides a wide range of types.
The basic data types are

int -> An integer.
float -> A floating point number.
char -> A single bit of memory, enough to hold a character.

There are some more data types

Short -> An integer, possibly of reduced range.

long -> An integer, possibly of increased range.

unsigned -> An integer with no negative range.

unsigned long -> Like unsigned, possibly of increased range.

double -> A double precision floating point number.

Global Variables

Local variables are declared within the body of a function, and can only be used within that function.

Alternatively, a variable can be declared globally so it is available to all functions. Modern programming practice recommends against the excessive use of global variables. They can lead to poor program structure, and tend to clog up the available name space.

A global variable declaration looks normal, but is located outside any of the program's functions. This is usually done at the beginning of the program file, but after preprocessor directives. The variable is not declared again in the body of the functions which access it.

Static Variables

Another class of local variable is the static type.

A static can only be accessed from the function in which it was declared, like a local variable.The static variable is not destroyed on exit from the function, instead its value is preserved, and becomes available again when the function is next called. Static variables are declared as local variables, but the declaration is preceeded by the word static.

Static Variable is declared as follows

static int counter;

Static variables can be initialised as normal, the initialisation is performed once only, when the program starts up.

Constants

C constant is usually just the written version of a number.

For example 1, 0, 5.73, 12.5e9. We can specify our constants in octal or hexadecimal, or force them to be treated as long integers.

* Octal constants are written with a leading zero - 015.

* Hexadecimal constants are written with a leading 0x - 0x1ae.

* Long constants are written with a trailing L - 890L.

Character constants are usually just the character enclosed in single quotes; 'a', 'b', 'c'. Some characters can't be represented in this way, so we use a 2 character sequence.

'\n' -> Newline

'\t' -> Tab

'\\' -> Backslash

'\'' -> Single Quote

'\0' -> NULL (used automatically to terminate a string)

String Constants are enclosed within double quotes.

................................................................................................................


Arrays

An array is a collection of variables of the same type.Individual array elements are identified by an integer index. Index begins at zero and is always written inside square brackets.

Arrays are declared as follows,

int temp[20];

Arrays can have more dimensions, in which case they might be declared as

int temp_2d[20][5];

int temp_3d[20][5][3];

When passed as an argument to a function, the receiving function need not know the size of the array. So for example if we have a function which sorts a list (represented by an array) then the function will be able to sort lists of different sizes. The drawback is that the function is unable to determine what size the list is, so this information will have to be passed as an additional argument.As an example, here is a simple function to add up all of the integers in a single dimensioned array.

int add_array(int array[], int size)
{ int i;
int total = 0;
for(i = 0; i < size; i++)
total += array[i];
return(total);
}

Expressions & Operators

Operators

An operator is a function which is applied to values to give a result.

Arithmetic operators are the most common. Other operators are used for comparison of values, combination of logical states, and manipulation of individual binary digits.Operators and values are combined to form expressions. The values produced by these expressions can be stored in variables, or used as a part of even larger expressions.

Assignment Statement

The easiest example of an expression is in the assignment statement.An expression is evaluated, and the result is saved in a variable.

y = (m * x) + c
This assignment will save the value of the expression in variable y.

Arithmetic operators

The most common arithmetic operators are,
+ Addition
- Subtraction
* Multiplication
/ Division
% Modulo Reduction (Its simply the remainder from integer division)

*, / and % will be performed before + or - in any expression.
Brackets can be used to force a different order of evaluation to this.
Where division is performed between two integers, the result will be an integer, with remainder discarded.
Modulo reduction is only meaningful between integers.
If a program is ever required to divide a number by zero, this will cause an error, usually causing the program to crash.

C has some operators which allows abbreviation of certain types of arthmetic assignment statements.

i++; or ++i; This is equivalent to i=i+1;
i--; or --i; This is equivalent to i=i-1;


These operations are usually very efficient. They can be combined with another expression.x = a * b++; This expression is commputed as follows,

x = a * b;b = b + 1;
Where as,x = --i * (a + b);
This is computed as follows,i = i - 1;
is computed first and with that value of i the expressionx = i * (a + b); is computed.

ComparisonC has a lot of comparison operators, Comparison operators are used to compare two values.

The Comparison operators are,

== Equal to
> Greater than
< Less than
>= Greater than or equal to
<= Less than or equal to
!= Not equal to


Note that == is used to compare two variables and = is used asassignments operator.Examples of comparison operators,

x==y
i>10
a + b != c


Logical ConnectorsThe Logical operators are,

&& AND
OR
! NOT


They are frequently used to combine relational operators,
Example:
x <>= 10

In C these logical connectives employ a technique known as lazy evaluation. They evaluate their left hand operand, and then only evaluate the right hand one if this is required. Clearly false && anything is always false, true anything is always true. In such cases the second test is not evaluated.

Not operates on a single logical value, its effect is to reverse its state.
Here is an example of its use.

if ( ! acceptable )
printf("Not Acceptable !!\n");

-----------------------------------------------------------------------------------------------


Type conversion

Type casting is the process of converting a data of one type into another for calculations. For example a float is converted into int for calculating.

char types will be treated as int when that is directly considered for calculation and the value will the ASCII value of the char.
  1. 1. When the variable is too small to hold the value. In this case it will be corrupted, So the variable is type casted to the right type.
  2. 2. The variable is an integer type and is being assigned a real value, the value is rounded down.

Values passed as function arguments must be of the correct type. The function has no way of determining the type passed to it, so automatic conversion cannot take place. This can lead to corrupt results. The solution is to use a method called casting which temporarily disguises a value as a different type.

Consider the following example.The function sqrt finds the square root of a double.

int i = 256;
int sqrroot;


sqrroot = sqrt( (double) i);

The cast is made by putting the bracketed name of the required type just before the value. (double) in this example. The result of sqrt( (double) i); is also a double, but this is automatically converted to an int on assignment to root.

Control Statements


Conditional Statements

The if else Statement

This is used to decide between two or more courses of action.Lets see an example,The following test decides whether the cost is above 500

if (result >= 45)
printf("Pass\n");
else
printf("Fail\n");


Else is optional,

if (temperature < 0)
print("Frozen\n");


The condition is always kept inside the brackets.
If the condition returns true then the statements following it is executed, if it returns false the the statements after else is executedAfter this, the rest of the program continues as normal.

We can have multiple if statements,

The most general way of doing this is by using the else if variant on the if statement. This works by cascading several comparisons. As soon as one of these gives a true result, the following statement or block is executed, and no further comparisons are performed.

if (result >= 75)
printf("Passed: Grade A\n");
else if (result >= 60)
printf("Passed: Grade B\n");
else if (result >= 45)
printf("Passed: Grade C\n");
else
printf("Failed\n");


In this example, all comparisons test a single variable called result. In other cases, each test may involve a different variable or some combination of tests. The same pattern can be used with more or fewer else if's, and the final lone else may be left out.

The switch Statement
This is another form of the multi way decision.

* Only one variable is tested, all branches must depend on the value of that variable. The variable must be an integral type. (int, long, short or char).* Each possible value of the variable can control a single branch. A final, catch all, default branch may optionally be used to trap all unspecified cases.

Lets see an example,

int number;
{
switch(number)

{
case 0 :
printf("None\n");
break;
case 1 :
printf("One\n");
break;
case 2 :
printf("Two\n");
break;
case 3 :
case 4 :
printf("Several\n");
break;
default :
printf("Many\n");
break;
}
}


Each case is listed with a corresponding action.

The break statement prevents any further statements from being executed by leaving the switch. Since case 3 have no following break, they continue on allowing the same action for several values of number.

------------------------------------------------------------------------------------------------------------------------

Loops

There are three types of loops while, do while and for.* The while loop keeps repeating an action until an associated test returns false. This is useful where the programmer does not know in advance how many times the loop will be traversed.

* The do while loops is similar, but the test occurs after the loop body is executed. This ensures that the loop body is run at least once.
* The for loop is frequently used, usually where the loop will be traversed a fixed number of times. It is very flexible, and novice programmers should take care not to abuse the power it offers.

The while Loop

The while loop repeats a statement until the test at the top proves false.Lets see a function that returns the length of a string(using while loop)

int string_length(char string[])
{
int i = 0;
while (string[i] != '\0')
i++;
return(i);
}


The string is passed to the function as an argument. The size of the array is not specified, the function will work for a string of any size.

The while loop is used to look at the characters in the string one at a time until the null character is found. Then the loop is exited and the index of the null is returned. While the character isn't null, the index is incremented and the test is repeated.

The do while Loop

This is very similar to the while loop except that the test occurs at the end of the loop body. This guarantees that the loop is executed at least once before continuing. Such a setup is frequently used where data is to be read. The test then verifies the data, and loops back to read again if it was unacceptable.

do
{ printf("Enter 1 for yes, 0 for no :");
scanf("%d", &input_value);
} while (input_value != 1 && input_value != 0)
The for Loop


The for loop works well where the number of iterations of the loop is known before the loop is entered. The head of the loop consists of three parts separated by semicolons.

* The first is run before the loop is entered. This is usually the initialisation of the loop variable.* The second is a test, the loop is exited when this returns false.
* The third is a statement to be run every time the loop body is completed. This is usually an increment of the loop counter.
The example is a function which calculates the average of the numbers stored in an array. The function takes the array and the number of elements as arguments.
float average(float array[], int count)
{
float total = 0.0;
int i;
for(i = 0; i < count; i++)
total += array[i];
return(total / count);
}

The for loop ensures that the correct number of array elements are added up before calculating the average.The three statements at the head of a for loop usually do just one thing each, however any of them can be left blank. A blank first or last statement will mean no initialisation or running increment. A blank comparison statement will always be treated as true. This will cause the loop to run indefinitely unless interrupted by some other means. This might be a return or a break statement.It is also possible to squeeze several statements into the first or third position, separating them with commas. This allows a loop with more than one controlling variable. The example below illustrates the definition of such a loop, with variables hi and lo starting at 100 and 0 respectively and converging.

for (hi = 100, lo = 0; hi >= lo; hi--, lo++)

The for loop is extremely flexible and allows many types of program behaviour to be specified simply and quickly.

-----------------------------------------------------------------------------------------------------------------------


Break & Continue Statements

The break Statement

We have already met break in the discussion of the switch statement. It is used to exit from a loop or a switch, control passing to the first statement beyond the loop or a switch.

With loops, break can be used to force an early exit from the loop, or to implement a loop with a test to exit in the middle of the loop body. A break within a loop should always be protected within an if statement which provides the test to control the exit condition.

The continue Statement

This is similar to break but is encountered less frequently. It only works within loops where its effect is to force an immediate jump to the loop control statement.

* In a while loop, jump to the test statement.
* In a do while loop, jump to the test statement.
* In a for loop, jump to the test, and perform the iteration. Like a break, continue should be protected by an if statement. You are unlikely to use it very often.

Functions

Functions in C

Almost all programming languages have some equivalent of the function. You may have met them under the alternative names subroutine or procedure.

Some languages distinguish between functions which return variables and those which don't. C assumes that every function will return a value. If the programmer wants a return value, this is achieved using the return statement. If no return value is required, none should be used when calling the function.

Here is a function which raises a double to the power of an unsigned, and returns the result.

double power(double val, unsigned pow)
{

double ret_val = 1.0;
unsigned i;
for(i = 0; i < pow; i++)
ret_val *= val;
return(ret_val);
}


The function follows a simple algorithm, multiplying the value by itself pow times. A for loop is used to control the number of multiplications, and variable ret_val stores the value to be returned. Careful programming has ensured that the boundary condition is correct too.

Let us examine the details of this function.

double power(double val, unsigned pow)

This line begins the function definition. It tells us the type of the return value, the name of the function, and a list of arguments used by the function. The arguments and their types are enclosed in brackets, each pair separated by commas.

The body of the function is bounded by a set of curly brackets. Any variables declared here will be treated as local unless specifically declared as static or extern types.

return(ret_val);

On reaching a return statement, control of the program returns to the calling function. The bracketed value is the value which is returned from the function. If the final closing curly bracket is reached before any return value, then the function will return automatically, any return value will then be meaningless.

The example function can be called by a line in another function which looks like this
result = power(val, pow);

This calls the function power assigning the return value to variable result.

Here is an example of a function which does not return a value.

void error_line(int line)
{
fprintf(stderr, "Error in input data: line %d\n", line);
}


The definition uses type void which is optional. It shows that no return value is used. Otherwise the function is much the same as the previous example, except that there is no return statement. Some void type functions might use return, but only to force an early exit from the function, and not to return any value. This is rather like using break to jump out of a loop.

This function also demonstrates a new feature.

fprintf(stderr, "Error in input data: line %d\n", line);

This is a variant on the printf statement, fprintf sends its output into a file. In this case, the file is stderr. stderr is a special UNIX file which serves as the channel for error messages. It is usually connected to the console of the computer system, so this is a good way to display error messages from your programs. Messages sent to stderr will appear on screen even if the normal output of the program has been redirected to a file or a printer.

The function would be called as followserror_line(line_number);

-----------------------------------------------------------------------------------


Scope of Function Variables

  • Variables declared within the calling function can't be accessed unless they are passed to the called function as arguments. The only other contact a function might have with the outside functions is through global variables.
  • Local variables are declared within a function. They are created anew each time the function is called, and destroyed on return from the function. Values passed to the function as arguments can also be treated like local variables.Static
  • variables are slightly different, they don't die on return from the function. Instead their last value is retained, and it becomes available when the function is called again.
  • Global variables don't die on return from a function. Their value is retained, and is available to any other function which accesses them.

Pointers in C

Pointers

Consider that we have an int called y. Its address could be represented by the symbol &i. If the pointer is to be stored as a variable, it should be stored like this.

int *pi = &y;

int * is the notation for a pointer to an int. & is the operator which returns the address of its argument. When it is used, as in &i we say it is referencing i.

The opposite operator, which gives the value at the end of the pointer is *. An example of use, known as de-referencing pi, would be

y = *py;

Take care not to confuse the many uses of the * sign; Multiplication, pointer declaration and pointer de-referencing.

Let us see an example. The following function jobs takes two arguments, x is an int while y is a pointer to int. It changes both values.

jobs(int x, int *y)
{ printf("Starting job : x = %d, y = %d\n", x, *y);
x ++;
(*y)++;
printf("Finishing job : x = %d, y = %d\n", x, *y);
}


since y is a pointer, we must de-reference it before incrementing its value.A very simple program to call this function might be as follows.

main()
{

int i = 0;
int j = 0;
printf("Starting main : i = %d, j = %d\n", x, y);
printf("Calling jobs now\n");.
jobs(i, &j);
printf("Returned from jobs\n");
printf("Finishing main : i = %d, j = %d\n", x, y);
}


Note here how a pointer to int is created using the & operator within the call jobs(i, &amp;j);.The result of running the program will look like this.

Starting main : i = 0 ,j = 0
Calling jobs now
Starting jobs: x = 0, y = 0
Finishing jobs: x = 1, y = 1
Returned from jobs
Finishing main : i = 0, j = 1


After the return from the function jobs the value of i is unchanged while j, which was passed as a pointer, has changed.

To summarise, if you wish to use arguments to modify the value of variables from a function, these arguments must be passed as pointers, and de-referenced within the function.

Where the value of an argument isn't modified, the value can be passed without any worries about pointers.
.....................................................................



Arrays and Pointers

An array is actually a pointer to the 0th element of the array. Dereferencing the array name will give the 0th element. This gives us a range of equivalent notations for array access. In the following examples, arr is an array.

Array Access Pointer Equivalent
arr[0] *arr
arr[n] *(arr + n)

Differences between arrays and pointers.

The array is treated as a constant in the function where it is declared. This means that we can modify the values in the array, but not the array itself, so statements like arr ++ are illegal, but arr[n] ++ is legal.

Since an array is like a pointer, we can pass an array to a function, and modify elements of that array without having to worry about referencing and de-referencing. Since the array is implemented as a hidden pointer, all the difficult stuff gets done automatically.

A function which expects to be passed an array can declare that parameter in one of two ways.

int arr[]; or int *arr;

Either of these definitions is independent of the size of the array being passed.

This is met most frequently in the case of character strings, which are implemented as an array of type char.

This could be declared as char string[]; but is most frequently written as char *string;

In the same way, the argument vector argv is an array of strings which can be supplied to function main. It can be declared as one of the following.

char **argv; or char *argv[];

-----------------------------------------------------------------------------------------------------------------------


Recursive Functions

A recursive function is one which calls itself.

Recursive functions are useful in evaluating certain types of mathematical function. You may also encounter certain dynamic data structures such as linked lists or binary trees. Recursion is a very useful way of creating and accessing these structures.

Here is a recursive version of the Fibonacci function.

int fib(int num)
{
switch(num)
{
case 0:
return(0);
break;
case 1:
return(1);
break;
default: /*Recursive calls */
return(fib(num - 1) + fib(num - 2));
break;
}
}


In the above function fib is called inside the function itself with a condition.Care must be taken to define functions which will not call themselves indefinitely, otherwise your program will never finish.

Recursive functions are sometimes the simplest answer to a calculation. However there is always an alternative non-recursive solution available too. This will normally involve the use of a loop, and may lack the elegance of the recursive solution

Input/Output in C

Character Input / Output

This is the lowest level of input and output. It provides very precise control, but is usually too fiddly to be useful. Most computers perform buffering of input and output.
This means that they'll not start reading any input until the return key is pressed, and they'll not print characters on the terminal until there is a whole line to be printed.

getchar

getchar returns the next character of keyboard input as an int.

If there is an error then EOF (end of file) is returned instead. It is therefore usual to compare this value against EOF before using it. If the return value is stored in a char, it will never be equal to EOF, so error conditions will not be handled correctly.

As an example, here is a program to count the number of characters read until an EOF is encountered. EOF can be generated by typing Control - d.

#include
main()
{
int ch, i = 0;
while((ch = getchar()) != EOF) i ++;
printf("%d\n", i);
}

putchar

putchar puts its character argument on the standard output (usually the screen).

The following example program converts any typed input into capital letters.
To do this it applies the function toupper from the character conversion library ctype.h to each character in turn. #include

#include
main()
{
int ch;
while((ch = getchar()) != EOF) putchar(toupper(ch));
}

This above code converts the typed letter into uppercase

------------------------------------------------------------------------------------------------------------------------------

Formatted Input / Output

printf

This offers more structured output than putchar. Its arguments are, in order; a control string, which controls what gets printed, followed by a list of values to be substituted for entries in the control string.

Control String Represents
%d A Decimal Integer
%f A Floating Point Value
%c A Character
%s A String

It is also possible to insert numbers into the control string to control field widths for values to be displayed. For example %6d would print a decimal value in a field 6 spaces wide, %8.2f would print a real value in a field 8 spaces wide with room to show 2 decimal places. Display is left justified by default, but can be right justified by putting a - before the format information, for example %-6d, a decimal integer right justified in a 6 space field.

scanf

scanf allows formatted reading of data from the keyboard.

Like printf it has a control string, followed by the list of items to be read. However scanf wants to know the address of the items to be read, since it is a function which will change that value. Therefore the names of variables are preceeded by the & sign. Character strings are an exception to this. Since a string is already a character pointer, we give the names of string variables unmodified by a leading &.
Example,
scanf("%d",&job_no);
scanf("%c",&job_code);

In the above examples the first one gets an integer value and the second one gets a char value.

-----------------------------------------------------------------------------------------------------------------------------------

Line Input and Output

This kind of input method is used where we are not too interested in the format of our data, or perhaps we cannot predict its format in advance, we can read and write whole lines as character strings.

This approach allows us to read in a line of input, and then use various string handling functions to analyse it at our leisure.

gets

gets reads a whole line of input into a string until a newline or EOF is encountered. It is critical to ensure that the string is large enough to hold any expected input lines.When all input is finished, NULL as defined in stdio.h is returned.

puts

puts writes a string to the output, and follows it with a newline character.
Example:
Program which uses gets and puts to double space typed input.

#include
main()
{ char line[256];
while(gets(line) != NULL)
{ puts(line);
printf("\n");
}
}

Input/Output in C

File Handling in C

File Handling in C - File Pointers

C communicates with files using a new datatype called a file pointer.This type is defined within stdio.h, and written as FILE *.

Let us declare a file pointer called newfile,
FILE *newfile;

Opening a file pointer using fopen

The file must be opened before it can be accessed in a program.
This is done using the fopen function, which returns the required file pointer.

If the file cannot be opened for any reason then the value NULL will be returned.

Here is an example showing how to open an file,

if ((newfile = fopen("newfile", "w")) == NULL)
fprintf(stderr, "Cannot open %s\n", "newfile");

In the above example file named newfile that we declared above is opened using fopen in write mode and that file is checked whether it is NULL, If the file is does not exist then a error is displayed Cannot open newfile

fopen takes two arguments, both are strings, the first is the name of the file to be opened, the second is an access character, which is usually one of:
"r" Open the file for reading
"w" Open the file for writing
"a" Open the file for appending

Closing a file using fclose

The fclose command can be used to disconnect a file pointer from a file. This is usually done so that the pointer can be used to access a different file. Systems have a limit on the number of files which can be open simultaneously, so it is a good idea to close a file when you have finished using it.

The closing statement looks like this,

fclose(output_file);

If files are still open when a program exits, the system will close them for you as the program closes. However it is usually better to close the files properly.
.....................................................................................................................................



Input and Output using file pointers

Having opened a file pointer, you will wish to use it for either input or output.
C supplies a set of functions to allow you to do this. All are very similar to input and output functions that you have already met.

Character Input and Output with Files

This is done using equivalents of getchar and putchar which are called getc and putc. Each takes an extra argument, which identifies the file pointer to be used for input or output.

putchar(c); is equivalent to putc(c,stdout);
getchar(); is equivalent to getc(stdin);

Formatted Input Output with File Pointers

Similarly there are equivalents to the functions printf and scanf which read or write data to files. These are called fprintf and fscanf.

The functions are used in the same way, except that the fprintf and fscanf take the file pointer as an additional first argument.

Whole Line Input and Output using File Pointers

Predictably, equivalents to gets and puts exist called fgets and fputs. The programmer should be careful in using them, since they are incompatible with gets and puts. gets requires the programmer to specify the maximum number of characters to be read. fgets and fputs retain the trailing newline character on the line they read or write, wheras gets and puts discard the newline.

When transferring data from files to standard input / output channels, the simplest way to avoid incompatibility with the newline is to use fgets and fputs for files and standard channels too.

For Example, read a line from the keyboard using
fgets(data_string, 80, stdin);

and write a line to the screen using
fputs(data_string, stdout);
.............................................................................................................................



Special Characters

C makes use of some 'invisible' characters

NULL, The Null Pointer

NULL is a character or pointer value. If a pointer, then the pointer variable does not reference any object (i.e. a pointer to nothing).It is usual for functions which return pointers to return NULL if they failed in some way. NULL is returned by read commands of the gets family when they try to read beyond the end of an input file.

Where it is used as a character, NULL is commonly written as '\0'. It is the string termination character which is automatically appended to any strings in your C program. You usually need not bother about this final \0', since it is handled automatically. However it sometimes makes a useful target to terminate a string search.

EOF, The End of File Marker

EOF is a character which indicates the end of a file. It is returned by read commands of the getc and scanf families when they try to read beyond the end of a file.

Structures in C

A structure is a collection of variables under a single name. These variables can be of different types, and each has a name which is used to select it from the structure. A structure is a convenient way of grouping several pieces of related information together.

A structure can be defined as a new named type, thus extending the number of available types. It can use other structures, arrays or pointers as some of its members, though this can get complicated unless you are careful.

Defining a Structure

A structure type is usually defined near to the start of a file using a typedef statement. typedef defines and names a new type, allowing its use throughout the program. typedefs usually occur just after the #define and #include statements in a file.

Here is an example structure definition.

typedef struct {
char name[64];
char course[128];
int age;
int year;
} student;

This defines a new type student variables of type student can be declared as follows.

student st_rec;

Notice how similar this is to declaring an int or float.
The variable name is st_rec, it has members called name, course, age and year.

Accessing Members of a Structure

Each member of a structure can be used just like a normal variable, but its name will be a bit longer. To return to the examples above, member name of structure st_rec will behave just like a normal array of char, however we refer to it by the name

st_rec.name

Here the dot is an operator which selects a member from a structure.

Where we have a pointer to a structure we could dereference the pointer and then use dot as a member selector. This method is a little clumsy to type. Since selecting a member from a structure pointer happens frequently, it has its own operator -> which acts as follows. Assume that st_ptr is a pointer to a structure of type student We would refer to the name member as

st_ptr -> name

Structures as Function Arguments

A structure can be passed as a function argument just like any other variable. This raises a few practical issues.
Where we wish to modify the value of members of the structure, we must pass a pointer to that structure. This is just like passing a pointer to an int type argument whose value we wish to change.

If we are only interested in one member of a structure, it is probably simpler to just pass that member. This will make for a simpler function, which is easier to re-use. Of course if we wish to change the value of that member, we should pass a pointer to it.

When a structure is passed as an argument, each member of the structure is copied. This can prove expensive where structures are large or functions are called frequently. Passing and working with pointers to large structures may be more efficient in such cases.

Apart from holding data, structures can be used as members of other structures. Arrays of structures are possible, and are a good way of storing lists of data with regular fields, such as databases.

Another possibility is a structure whose fields include pointers to its own type. These can be used to build chains (programmers call these linked lists), trees or other connected structures. These are rather daunting to the new programmer, so we won't deal with them here.