#include 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 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 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 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 , 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 #include #include #include #include #include /* 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 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 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.