#include <stdio.h>
int funca(int x, int y)
{
/* This function receives two parameters by value, x and y */
x = x * 2;
y = y * 2;
printf("\nValue of x in funca() %d value of y in funca() %d",x,y);
return(x);
}
int funcb(int *x, int *y)
{
/* This function receives two parameters by reference, x and y */
*x = *x * 2;
*y = *y * 2;
printf("\nValue of x in funcb() %d value of y in funcb() %d",*x,*y);
return(*x);
}
main()
{
int x;
int y;
int z;
x = 5;
y = 7;
z = funca(x,y);
z = funcb(&x,&y);
printf("\nValue of x %d value of y %d value of z %d",x,y,z);
}
Actually funcb() does not change the values of the parameters it receives.
Rather it changes the contents of the memory addresses pointed to by the
received parameters. Whilst funca() receives the values of variables 'x' and
'y' from function main(), funcb() receives the memory addresses of the
variables 'x' and 'y' from function main().
Passing an array to a function:
The following program passes an array to a function, funca(), which
initialises the array elements;
#include <stdio.h>
void funca(int x[])
{
int n;
for(n = 0; n < 100; n++)
x[n] = n;
}
main()
{
int array[100];
int counter;
funca(array);
for(counter = 0; counter < 100; counter++)
printf("\nValue of element %d is %d",counter,array[counter]);
}
The parameter of funca() 'int x[]' is declared to be an array of any length.
This works because the compiler passes the address of the start of the array
parameter to the function, rather than the value of the individual elements.
This does of course mean that the function can change the value of the array
elements. To prevent a function from changing the values you can specify the
parameter as type 'const';
funca(const int x[])
{
}
This will then generate a compiler error at the line which attempts to write a
value to the array. However, specifying a parameter to be const does not
protect the parameter from indirect assignment as the following program
illustrates;
#include <stdio.h>
int funca(const int x[])
{
int *ptr;
int n;
/* This line gives a 'suspicious pointer conversion warning' */
/* because x is a const pointer, and ptr is not */
ptr = x;
for(n = 0; n < 100; n++){
*ptr = n;
ptr++;
}
}
main()
{
int array[100];
int counter;
funca(array);
for(counter = 0; counter < 100; counter++)
printf("\nValue of element %d is %d",counter,array[counter]);
}
Passing parameters to main():
C allows parameters to be passed from the operating system to the program when
it starts executing through two parameters; argc and argv[], as follows;
#include <stdio.h>
main(int argc, char *argv[])
{
int n;
for(n = 0; n < argc; n++)
printf("\nParameter %d equals %s",n,argv[n]);
}
Parameter argc holds the number of parameters passed to the program, and the
array argv[] holds the addresses of each parameter passed. argv[0] is always
the program name.
Returning from a function:
The command 'return' is used to return immediately from a function. If the
function was declared with a return data type, then return should be used with
a parameter of the same data type.
Function prototypes:
Prototypes for functions allow the C compiler to check that the type of data
being passed to and from functions is correct. This is very important to
prevent data overflowing its allocated storage space into other variables
areas.
A function prototype is placed at the begining of the program, after any
preprocessor commands, such as #include <stdio.h>, and before the decleration
of any functions.
STRUCTURES
C provides the means to group togther variables under one name providing a
convenient means of keeping related information togther and also providing a
structured approach to data.
The general form for a structure definition is;
typedef struct{
variable_type variable_name;
variable_type varaiable_name;
}structure_name;
When accessing data files, with a fixed record structure, the use of a
structure variable becomes essential. The following example shows a record
structure for a very simple name and address file. It declares a data
structure called 'data', and comprised of six fields; 'name', 'address',
'town', 'county' , 'post' and 'telephone'.
typedef struct{
char name[30];
char address[30];
char town[30];
char county[30];
char post[12];
char telephone[15];
}data;
The structure 'data' may then be used in the program as a variable data type
for declaring variables;
data record;
Thus declares a variable called 'record' to be of type 'data'.
The individual fields of the structure variable are accessed by the general
form;
structure_variable.field_name;
Thus, the name field of the structure variable record is accessed with;
record.name
There is no limit to the number of fields which may comprise a structure, nor
do the fields have to be of the same types, for example;
typedef struct{
char name[30];
int age;
char *notes;
}dp;
Declares a structure 'dp' which is comprised of a character array field, an
integer field and a character pointer field.
The following is an example program which makes use of a structure to provide
the basic access to the data in a simple name and address file;
/* A VERY simple address book written in ANSI C */
#include <stdio.h>
#include <stdlib.h>
#include <io.h>
#include <string.h>
#include <fcntl.h>
#include <sys\stat.h>
/* num_lines is the number of screen display lines */
#define num_lines 25
typedef struct{
char name[30];
char address[30];
char town[30];
char county[30];
char post[12];
char telephone[15];
}data;
data record;
int handle;
/* Function prototypes */
void CLS(void);
void FATAL(char *);
void OPENDATA(void);
void GETDATA(void);
void DISPDATA(void);
void ADD_REC(void);
int SEARCH(void);
void MENU(void);
void CLS()
{
int n;
for(n = 0; n < num_lines; n++)
puts("");
}
void FATAL(char *error)
{
printf("\nFATAL ERROR: %s",error);
exit(0);
}
void OPENDATA()
{
/* Check for existence of data file and if not create it */
/* otherwise open it for reading/writing at end of file */
handle = open("address.dat",O_RDWR|O_APPEND,S_IWRITE);
if (handle == -1){
handle = open("address.dat",O_RDWR|O_CREAT,S_IWRITE);
if (handle == -1)
FATAL("Unable to create data file");
}
}
void GETDATA()
{
/* Get address data from operator */
CLS();
printf("Name ");
gets(record.name);
printf("\nAddress ");
gets(record.address);
printf("\nTown ");
gets(record.town);
printf("\nCounty ");
gets(record.county);
printf("\nPost Code ");
gets(record.post);
printf("\nTelephone ");
gets(record.telephone);
}
void DISPDATA()
{
/* Display address data */
char text[5];
CLS();
printf("Name %s",record.name);
printf("\nAddress %s",record.address);
printf("\nTown %s",record.town);
printf("\nCounty %s",record.county);
printf("\nPost Code %s",record.post);
printf("\nTelephone %s\n\n",record.telephone);
puts("Press RETURN to continue");
gets(text);
}
void ADD_REC()
{
/* Insert or append a new record to the data file */
int result;
result = write(handle,&record,sizeof(data));
if (result == -1)
FATAL("Unable to write to data file");
}
int SEARCH()
{
char text[100];
int result;
printf("Enter data to search for ");
gets(text);
if (*text == 0)
return(-1);
/* Locate start of file */
lseek(handle,0,SEEK_SET);
do{
/* Read record into memory */
result = read(handle,&record,sizeof(data));
if (result > 0){
/* Scan record for matching data */
if (strstr(record.name,text) != NULL)
return(1);
if (strstr(record.address,text) != NULL)
return(1);
if (strstr(record.town,text) != NULL)
return(1);
if (strstr(record.county,text) != NULL)
return(1);
if (strstr(record.post,text) != NULL)
return(1);
if (strstr(record.telephone,text) != NULL)
return(1);
}
}while(result > 0);
return(0);
}
void MENU()
{
int option;
char text[10];
do{
CLS();
puts("\n\t\t\tSelect Option");
puts("\n\n\t\t\t1 Add new record");
puts("\n\n\t\t\t2 Search for data");
puts("\n\n\t\t\t3 Exit");
puts("\n\n\n\n\n");
gets(text);
option = atoi(text);
switch(option){
case 1 : GETDATA();
/* Go to end of file to append new record */
lseek(handle,0,SEEK_END);
ADD_REC();
break;
case 2 : if (SEARCH())
DISPDATA();
else{
puts("NOT FOUND!");
puts("Press RETURN to continue");
gets(text);
}
break;
case 3 : break;
}
}while(option != 3);
}
void main()
{
CLS();
OPENDATA();
MENU();
}
Bit fields:
C allows the inclusion of variables with a size of less than eight bits to be
included in structures. These variables are known as bit fields, and may be
any declared size from one bit upwards.
The general form for declaring a bit field is;
type name : number_of_bits;
For example, to declare a set of status flags, each occupying one bit;
typedef struct{
unsigned carry : 1;
unsigned zero : 1;
unsigned over : 1;
unsigned parity : 1;
} df;
df flags;
The variable 'flags' then occupies only four bits in memory, and yet is
comprised of four variables which may be accessed like any other structure
field.
UNIONS
Another facility provided by C for efficient use of available memory is the
union structure. A union structure is a collection of variables which all
share the same memory storage address. As such only one of the variables is
ever accessible at a time.
The general form of a union definition is;
union name{
type variable_name;
type variable_name;
.
.
.
type variable_name;
};
Thus, to declare a union structure for two integer variables;
union data{
int vara;
int varb;
};
and to declare a variable of type 'data';
data my_var;
The variable 'my_var' is then comprised of the two variables 'vara' and 'varb'
which are accessed like with any form of structure, eg;
my_var.vara = 5;
Assigns a value of 5 to the variable 'vara' of union 'my_var'.
ENUMERATIONS
An enumeration assigns ascending integer values to a list of symbols. An
enumeration decleration takes the general form;
enum name { enumeration list } variable_list;
Thus to define a symbol list of colours, you can say;
enum COLOURS {
BLACK,
BLUE,
GREEN,
CYAN,
RED,
MAGENTA,
BROWN,
LIGHTGRAY,
DARKGRAY,
LIGHTBLUE,
LIGHTGREEN,
LIGHTCYAN,
LIGHTRED,
LIGHTMAGENTA,
YELLOW,
WHITE
};
Then, the number zero may be refered to by the symbol BLACK, the number one by
the symbol BLUE, the number two by the symbol GREEN and so on.
The following program illustrates the use of an enumeration list to symbolise
integers;
#include <stdio.h>
enum COLOURS {
BLACK,
BLUE,
GREEN,
CYAN,
RED,
MAGENTA,
BROWN,
LIGHTGRAY,
DARKGRAY,
LIGHTBLUE,
LIGHTGREEN,
LIGHTCYAN,
LIGHTRED,
LIGHTMAGENTA,
YELLOW,
WHITE
};
void main()
{
int x;
x = RED;
printf("\nVariable 'x' holds %d",x);
}
FILE I/O
C provides buffered file streams for file access. Some C platforms, such as
Unix and DOS provide unbuffered file handles as well.
Buffered streams:
Buffered streams are accessed through a vraiable of type 'file pointer'. The
data type FILE is defined in the header file stdio.h. Thus to declare a file
pointer;
#include <stdio.h>
FILE *ptr;
To open a stream C provides the function fopen() which accepts two parameters,
the name of the file to be opened, and the access mode for the file to be
opened with. The access mode may be any one of;
Mode Description
r Open for reading
w Create for writing, destroying any existing file
a Open for append, creating a new file if it doesn't
exist
r+ Open an existing file for reading and writing
w+ Create for reading and writing, destroying any
existing file
a+ Open for append, creating a new file if it doesn't
exist.
If fopen() fails to open the file, it returns a value of NULL (defined in
stdio.h) to the file pointer.