Sunday, December 6, 2009

Iteration Statements

C provides three looping, or iteration, statements: for, while, and do-while. The for loop has the general form:



for(initialization;condition;increment)



and is useful for counters, such as in this example that displays the entire ASCII character set:

#include main()

{

int x;



for(x = 32; x < 128; x++)

printf ("%d\t%c\t",x,x);

}



An infinite for loop is also valid:



for(;;)

{

statements

}



Also, C allows empty statements. The following for loop removes leading spaces from a string:



for(; *str == ' '; str++)



Notice the lack of an initializer, and the empty statement following the loop. The while loop is somewhat simpler than the for loop; it follows the general form:

The statement following the condition or statements enclosed in curly braces will be executed until the condition is FALSE. If the condition is FALSE before the loop commences, the loop statements will not be executed. The do-while loop, on the other hand, is always executed at least once. It takes the general form:



do {

statements



}

while(condition); Jump Statements

The return statement is used to return from a function to the calling function. Depending upon the declared return data type of the function, it may or may not return a value:



int MULT(int x, int y)

{

return(x * y);

}

or

void FUNCA()

{

printf ("\nHello World"); return;

}

The break statement is used to break out of a loop or from a switch statement. In a loop, it may be used to terminate the loop prematurely, as shown here:



#include



main()

{

int x;



for(x = 0; x < 256; x++) {

if (x == 100)

break; printf ("%d\t",x);

}

}



In contrast to break is continue, which forces the next iteration of the loop to occur, effectively forcing program control back to the loop statement. C provides a function for terminating the

program prematurely with exit( ). Exit( ) may be used with a return value to pass back to the calling program:



exit(return_value);



Continue



The continue keyword forces control to jump to the test statement of the innermost loop (while, do... while( )). This can be useful for terminating a loop gracefully, as in this program that reads strings from a file until there are no more:



#include



void main()

{

FILE *fp;

char *p;

char buff[100];



fp = fopen("data.txt","r");

if (fp == NULL)

{

fprintf(stderr,"Unable to open file data.txt");

exit(0);

}

do {

p = fgets(buff,100,fp);

if (p == NULL)

/* Force exit from loop */ continue; puts(p);

}

while(p);

}



Keep in mind that, with a for( ) loop, the program will continue to pass control back to the third parameter.

Input and Output Input

Input to a C program may occur from the console, the standard input device (unless otherwise redirected), from a file or data port. The general input command for reading data from the standard input stream stdin is scanf( ). Scanf( ) scans a series of input fields, one character at a time. Each field is then formatted according to the appropriate format specifier passed to the scanf( ) function, as a parameter. This field is then stored at the ADDRESS passed to scanf( ), following the format specifier's list. For example, the following program will read a single integer from the stream stdin:



main()

{

int x;

Notice the address operator and the prefix to the variable name x in the scanf( ) parameter list. The reason for this is because scanf( ) stores values at ADDRESSES, rather than assigning values to variables directly. The format string is a character string that may contain three types of data: whitespace characters (space, tab, and newline), nonwhitespace characters (all ASCII characters except the percent symbol--%), and format specifiers. Format specifiers have the general form:



%[*][width][h|l|L]type_character



Here's an example using scanf( ):



#include



main()

{

char name[30]; int age;



printf ("\nEnter your name and age ");

scanf("%30s%d",name,&age);

printf ("\n%s %d",name,age);

}



Notice the include line—#include : this tells the compiler to also read the file stdio.h, which contains the function prototypes for scanf( ) and printf ( ). If you type in and run this sample program, you will see that only one name can be entered.



An alternative input function is gets( ), which reads a string of characters from the stream stdin until a newline character is detected. The newline character is replaced by a null (0 byte) in the target string. This function has the advantage of allowing whitespace to be read in. The following program is a modification to the earlier one, using gets( ) instead of scanf( ):



#include #include #include



main()

{

char data[80]; char *p; char name[30]; int age;



printf ("\nEnter your name and age "); /* Read in a string of data */ gets(data);



/* P is a pointer to the last character in the input string */ p = &data[strlen(data) - 1];



/* Remove any trailing spaces by replacing them with null bytes *

243

while(*p == ' '){

*p = 0;



}

/* Locate last space in the string */ p = strrchr(data,' ');

/* Read age from string and convert to an integer */ age = atoi(p);

/* Terminate data string at start of age field */

*p = 0;



/* Copy data string to name variable */ strcpy(name,data);



/* Display results */

printf ("\nName is %s age is %d",name,age);

}



Output



The most common output function is printf ( ). Printf( ) is very similar to scanf( ) except that it writes formatted data out to the standard output stream stdout. Printf( ) takes a list of output data fields, applies format specifiers to each, and outputs the result. The format specifiers are the same as for scanf( ), except that flags may be added. These flags include:



- Left-justifies the output padding to the right with spaces. + Causes numbers to be prefixed by their sign.



The width specifier is also slightly different for printf( ): its most useful form is the precision specifier:

width.precision

So, to print a floating-point number to three decimal places, you would use: printf ("%.3f",x);

The following are special character constants that may appear in the printf( ) parameter list:



\n


Newline

\r


Carriage return

\t


Tab

\b


Sound the computer's bell

\f


Formfeed

\v


Vertical tab

\\


Backslash character

\'


Single quote

\"


Double quote

\?


Question mark

\O


Octal string

\x Hexadecimal string



The following program shows how a decimal integer may be displayed as a decimal, hexadecimal, or octal integer. The 04 following the percent symbol (%) in the printf ( ) format tells the compiler to pad the displayed figure to a width of at least four digits:

/* A simple decimal to hexadecimal and octal conversion program */

#include

main()

{

int x;



do {

printf ("\nEnter a number, or 0 to end "); scanf("%d",&x);

printf ("%04d %04X %04o",x,x,x);

}





while(x != 0);

}

Functions associated with printf ( ) include fprintf( ), with prototype: fprintf(FILE *fp,char *format[,argument,... ]); This variation on printf ( ) simply sends the formatted output to the specified file stream. Another associated function is sprintf( ); it has the following prototype:

sprintf(char *s,char *format[,argument,... ]);

An alternative to printf ( ) for outputting a simple string to the stream stdout is puts( ). This function sends a string to the stream stdout, followed by a newline character. It is faster than printf( ), but far less flexible.



Direct Console I/O



Data may be sent to and read from the console (keyboard and screen), using the direct console I/O functions. These functions are prefixed by the letter c; thus, the direct console I/O equivalent of printf ( ) is cprintf( ), and the equivalent of puts( ) is cputs( ). Direct console I/O functions differ from standard I/O functions in that:

They do not make use of the predefined streams, and hence may not be redirected.

They are not portable across operating systems (for example, you can't use direct console I/O functions in a Windows program).

They are faster than their standard I/O equivalents.

They may not work with all video modes (especially VESA display modes).

Pointers



A pointer is a variable that holds the memory address of an item of data. A pointer is declared like an ordinary variable, but its name is prefixed by an asterisk (*), as illustrated here:



char *p;



This example declares the variable p to be a pointer to a character variable.



Pointers are very powerful, and similarly dangerous, because a pointer can be inadvertently set to point to the code segment of a program, and then some value can be assigned to the address of the pointer. The following program illustrates a simple pointer application:



#include



main()

{

int a; int *x;



/* x is a pointer to an integer data type */



a = 100; x = &a;



printf ("\nVariable 'a' holds the value %d at memory address %p", a,x);

}



Pointers may be incremented and decremented and have other mathematics applied to them as well. Pointers are commonly used in dynamic memory allocation. When a program is running, it is often necessary to temporarily allocate a block of data in memory. C provides the function malloc( ) for this purpose; it follows the general form:



any pointer type = malloc(number_of_bytes);



Here, malloc( ) actually returns a void pointer type, which means it can be any type—integer, character, floating point, and so on. This example allocates a table in memory for 1,000 integers:



#include #include



main()

{

int *x; int n;



/* x is a pointer to an integer data type */



/* Create a 1000 element table, sizeof() returns the compiler */ /* specific number of bytes used to store an integer */



x = malloc(1000 * sizeof(int));

/* Check to see if the memory allocation succeeded */ if (x == NULL) {



printf(" \nUnable to allocate a 1000 element integer table");

exit(0);

}

/* Assign values to each table element */

for(n = 0; n < 1000; n++)

{

*x = n; x++;

}

/* Return x to the start of the table */ x -= 1000;



/* Display the values in the table */

for(n = 0; n < 1000; n++){

printf(" \nElement %d holds a value of %d",n,*x); x++;

}

/* Deallocate the block of memory now it's no longer required */ free(x);

}



Pointers are also used with character arrays, called strings. Since all C program strings are terminated by a zero byte, we can count the letters in a string using a pointer:



#include #include



main()

{

char *p;

char text[100];

int len;



/* Initialize variable 'text' with some writing */ strcpy(text,"This is a string of data");

/* Set variable p to the start of variable text */ p = text;

/* Initialize variable len to zero */

len = 0;



/* Count the characters in variable text */ while(*p) {

len++; p++;

/* Display the result */

printf("\nThe string of data has %d characters in it",len);

}



To address 1MB of memory, a 20-bit number is composed of an offset and a 64KB segment. The IBM PC uses special registers called segment registers to record the segments of addresses. This introduces the C language to three new keywords: near, far, and huge.

Near pointers are 16 bits wide and access only data within the current segment.

Far pointers are composed of an offset and a segment address, allowing them to access data anywhere in memory.

Huge pointers are a variation of the far pointer and can be successfully incremented and decremented through the entire 1 MB range (since the compiler generates code to amend the

offset).



It will come as no surprise that code using near pointers executes faster than code using far pointers, which in turn is faster than code using huge pointers. To give a literal address to a far pointer, C compilers provide a macro, MK-FP( ), which has the prototype:



void far *MK_FP(unsigned segment, unsigned offset);



Structures



C provides the means to group variables under one name, thereby providing a convenient means of keeping related information together and forming a structured approach to data. The general form for a structure definition is:



typedef struct

{

variable_type variable_name; variable_type variable_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, composed 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 individual fields of the structure variable are accessed via the following general format: structure_variable.field_name;

There is no limit to the number of fields that 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;



This example declares a structure, dp, that is composed of a character array field, an integer field, and a character pointer field. Structure variables may be passed as a parameter by passing the address of the variable as the parameter with the ampersand (&) operator. The following is an example program that makes use of a structure to provide basic access to the data in a simple name and address file:

#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 ADD_REC(void); void CLS(void);

void DISPDATA(void); void FATAL(char *); void GETDATA(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;

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 the following:



MODE DESCRIPTION

r Open for reading.

w Create for writing, destroying any existing file.
a Open for append; create a new file if it doesn't

exist.

Open for append; create a new file if it doesn't exist.



Optionally, either b or t may be appended for binary or text mode. If neither is appended, the file stream will be opened in the mode described by the global variable, _fmode. Data read or written

254 from file streams opened in text mode endures conversion; that is, the characters CR and LF are converted to CR LF pairs on writing, and the CR LF pair is converted to a single LF on reading. File streams opened in binary mode do not undergo conversion.



If fopen( ) fails to open the file, it returns a value of NULL (defined in stdio.h) to the file pointer. Thus, the following program will create a new file called data.txt, and open it for reading and writing:

#include void main()

{

FILE *fp;



fp = fopen("data.txt","w+");



}



To close a stream, C provides the function fclose( ), which accepts the stream's file pointer as a parameter:



fclose(fp);



If an error occurs in closing the file stream, fclose( ) returns nonzero. There are four basic functions for receiving and sending data to and from streams: fgetc( ), fputc( ), fgets( ) and fputs( ). The fgetc( ) function simply reads a single character from the specified input stream:



char fgetc(FILE *fp);



Its opposite is fputc( ), which simply writes a single character to the specified input stream:

char fputc(char c, FILE *fp);

The fgets( ) function reads a string from the input stream:

char *fgets(char s, int numbytes, FILE *fp);

It stops reading when either numbytes—1 bytes—have been read, or a newline character is read in. A null-terminating byte is appended to the read string, s. If an error occurs, fgets( ) returns NULL.



The fputs( ) function writes a null-terminated string to a stream:



int fputs(char *s, FILE *fp);



Except for fgets( ), which returns a NULL pointer if an error occurs, all the other functions described return EOF (defined in stdio.h), if an error occurs during the operation. The following program creates a copy of the file data.dat as data.old and illustrates the use of fopen( ), fgetc( ), fputc( ), and fclose( ):

#include int main()

{

FILE *in; FILE *out;

in = fopen("data.dat","r"); if (in == NULL)

fp = fopen("data.txt","w+");



}



To close a stream, C provides the function fclose( ), which accepts the stream's file pointer as a parameter:



fclose(fp);



If an error occurs in closing the file stream, fclose( ) returns nonzero. There are four basic functions for receiving and sending data to and from streams: fgetc( ), fputc( ), fgets( ) and fputs( ). The fgetc( ) function simply reads a single character from the specified input stream:



char fgetc(FILE *fp);



Its opposite is fputc( ), which simply writes a single character to the specified input stream:

char fputc(char c, FILE *fp); The fgets( ) function reads a string from the input stream:

char *fgets(char s, int numbytes, FILE *fp);



It stops reading when either numbytes—1 bytes—have been read, or a newline character is read in. A null-terminating byte is appended to the read string, s. If an error occurs, fgets( ) returns NULL.



The fputs( ) function writes a null-terminated string to a stream:



int fputs(char *s, FILE *fp);



Except for fgets( ), which returns a NULL pointer if an error occurs, all the other functions described return EOF (defined in stdio.h), if an error occurs during the operation. The following program creates a copy of the file data.dat as data.old and illustrates the use of fopen( ), fgetc( ), fputc( ), and fclose( ):

#include int main()

{

FILE *in;

FILE *out;

in = fopen("data.dat","r");



if (in == NULL)



int fseek(FILE *fp, long numbytes, int fromwhere);

256

Here, fseek( ) repositions a file pointer associated with a stream previously opened by a call to fopen( ). The file pointer is positioned numbytes from the location fromwhere, which may be the file beginning, the current file pointer position, or the end of the file, symbolized by the constants SEEKSET, SEEKCUR, and SEEKEND, respectively. If a call to fseek( ) succeeds, a value of 0 is returned. The ftell( ) function is associated with fseek( ), which reports the current file pointer position of a stream, and has the following functional prototype:



long int ftell(FILE *fp);



The ftell( ) function returns either the position of the file pointer, measured in bytes from the start of the file, or -1 upon an error occurring.



Handles



File handles are opened with the open( ) function, which has the prototype:



int open(char *filename,int access[,unsigned mode]);



If open( ) is successful, the number of the file handle is returned; otherwise, open( ) returns -1. The access integer is comprised from bitwise OR-ing together of the symbolic constants declared in fcntl.h. These vary from compiler to compiler and may be:



O_APPEND If set, the file pointer will be set to the end of the file prior to each write.

O_CREAT If the file does not exist, it is created. O_TRUNC Truncates the existing file to a length of 0 bytes.

O_EXCL Used with O_CREAT.

O_BINARY Opens the file in binary mode. O_TEXT Opens file in text mode.



Once a file handle has been assigned with open( ), the file may be accessed with read( ) and write( ). Read() has the function prototype:



int read(int handle, void *buf, unsigned num_bytes );



It attempts to read num_bytes, and returns the number of bytes actually read from the file handle, handle, and stores these bytes in the memory block pointed to by buf. Write( ) is very similar to read( ), and has the same function prototype, and return values, but writes num_bytes from the memory block pointed to by buf. Files opened with open( ) are closed using close( ), which uses the function prototype:



int close(int handle);



The close( ) function returns 0 on successes, and -1 if an error occurs during an attempt.



Random access is provided by lseek( ), which is very similar to fseek( ), except that it accepts an integer file handle as the first parameter, rather than a stream FILE pointer. This example uses file handles to read data from stdin (usually the keyboard), and copies the text to a new file called data.txt:

#include #include #include



int main()

{

int handle;

char text[100];



handle = open("data.txt",O_RDWR|O_CREAT|O_TRUNC,S_IWRITE);

do {

gets(text);

write(handle,&text,strlen(text));

}

while(*text); close(handle);

}



Advanced File I/O



The ANSI standard on C defines file I/O by way of file streams, and defines various functions for file access. The fopen( ) function has the prototype:



FILE *fopen(const char *name,const char *mode);



Here, fopen( ) attempts to open a stream to a file name in a specified mode. If successful, a FILE type pointer is returned to the file stream. If the call fails, NULL is returned. The mode string can be one of the following:



DESCRIPTION

MODE

R Open for reading only.

W Create for writing; overwrite any existing file with the same name.

A Open for append (writing at end of file) or create the file if it does not exist.

r+ Open an existing file for reading and writing.

w+ Create a new file for reading and writing.

a+ Open for append with read and write access.



The fclose( ) function is used to close a file stream previously opened by a call to fopen( ) and has the prototype:



int fclose (FILE *fp);



When a call to fclose( ) is successful, all buffers to the stream are flushed, and a value of 0 is returned. If the call fails, fclose( ) returns EOF.

Many host computers, use buffered file access; that is, when writing to a file stream, the data is stored in memory and only written to the stream when it exceeds a predefined number of bytes. A power failure that occurs before the data has been written to the stream will result in data loss, so the function fflush( ) can be called to force all pending data to be written; fflush( ) has the prototype:



int fflush(FILE *fp);



When a call to fflush( ) is successful, the buffers connected with the stream are flushed, and a value of 0 is returned. On failure, fflush( ) returns EOF. The location of the file pointer connected with a stream can be determined with the function ftell( ), which has the prototype:



long int ftell(FILE *fp);



Here, ftell( ) returns the offset of the file pointer in bytes from the start of the file, or - 1L if the call fails. Similarly, you can move the file pointer to a new position with fseek( ), which has the prototype:



int fseek(FILE *fp, long offset, int from_what_place);



The fseek( ) function attempts to move the file pointer, fp, offset bytes from the position ''from_what_place," which is predefined as one of the following:

SEEKSET The beginning of the file SEEK_CUR The current position of the file pointer

SEEK_END End of file



The offset may be a positive value, to move the file pointer on through the file, or negative, to move backward. To move a file pointer quickly back to the start of a file, and to clear any references to errors that have occurred, C provides the function rewind( ), which has the prototype:



void rewind(FILE *fp);



Here, rewind(fp) is similar to fseek(fp,0L,SEEK_SET) in that they both set the file pointer to the start of the file, but where fseek( ) clears the EOF error marker, rewind( ) clears all error indicators. Errors occurring with file functions can be checked with the function ferror( ):



int ferror(FILE *fp);



The ferror( ) function returns a nonzero value if an error has occurred on the specified stream. After checking ferror( ) and reporting any errors, you should clear the error indicators; and this can be done by a call to clearerr( ), which has the prototype:



void clearerr(FILE *fp);



The condition of reaching end of file (EOF) can be tested for with the predefined macro feof( ), which has the prototype:



int feof(FILE *fp);



The feof( ) macro returns a nonzero value if an end-of-file error indicator was detected on the specified file stream, and zero, if the end of file has not yet been reached.

Reading data from a file stream can be achieved using several functions. A single character can be read with fgetc( ), which has the prototype:



int fgetc(FILE *fp);



Here, fgetc( ) returns either the character read and converted to an integer or EOF if an error occurred. Reading a string of data is achieved with fgets( ), which attempts to read a string terminated by a newline character; it has the prototype:



char *fgets(char s, int n, FILE *fp);



A successful call to fgets( ) results in a string being stored in s that is either terminated by a newline character or that is n-1 characters long. The newline character is retained by fgets( ), and a null byte is appended to the string. If the call fails, a NULL pointer is returned. Strings may be written to a stream using fputs( ), which has the prototype:



int fputs(const char *s,FILE *fp);



The fputs( ) function writes all the characters, except the null-terminating byte, in the string s to the stream fp. On success, fputs( ) returns the last character written; on failure, it returns EOF. To write a single character to a stream, use fputc( ), which has the prototype:

int fputc(int c,FILE *fp);

If this procedure is successful, fputc( ) returns the character written; otherwise, it returns EOF.

To read a large block of data or a record from a stream, you can use fread(), which has the prototype:

size_t fread(void *ptr,size_t size, size_t n, FILE *fp);



The fread( ) function attempts to read n items, each of length size from the file stream fp, into the block of memory pointed to by ptr. To check the success or failure status of fread( ), use ferror( ).



The sister function to fread( ) is fwrite( ); it has the prototype:



size_t fwrite(const void *ptr,size_t size, size_t n,FILE *fp);



This function writes n items, each of length size, from the memory area pointed to by ptr to the specified stream fp.



Formatted input from a stream is achieved with fscanf(); it has prototype:



int fscanf(FILE *fp, const char *format[,address ... ]);



The fscanf( ) function returns the number of fields successfully stored, and EOF on end of file. This short example shows how fscanf( ) is quite useful for reading numbers from a stream:



#include



void main()

{

FILE *fp;

int a;

int b; int c; int d; int e;

char text[100];



fp = fopen("data.txt","w+");



if(!fp) {

perror("Unable to create file");

exit(0);

}

fprintf(fp,"1 2 3 4 5 \"A line of numbers\"");

fflush(fp);

if (ferror(fp)) {

fputs("Error flushing stream",stderr); exit(1);

}



rewind(fp);

if (ferror(fp))

{

fputs("Error rewind stream",stderr); exit(1);

}



fscanf(fp,"%d %d %d %d %d %s",&a,&b,&c,&d,&e,text);

if (ferror(fp)) {

fputs("Error reading from stream",stderr); exit(1);

}



printf ("\nfscanf() returned %d %d %d %d %d %s",a,b,c,d,e,text);

}



As you can see from the example, fprintf( ) can be used to write formatted data to a stream. If you wish to store the position of a file pointer on a stream, and then later restore it to the same position, you can use the functions fgetpos( ) and fsetpos( ): fgetpos( ) reads the current location of the file pointer, and has the prototype:



int fgetpos(FILE *fp, fpos_t *pos);



The fsetpos( ) function repositions the file pointer, and has the prototype:



int fsetpos(FILE *fp, const fpos_t *fpos);



Here, fpos_t is defined in stdio.h. These functions are more convenient than doing an ftell( ) followed by an fseek( ).

An open stream can have a new file associated with it, in place of the existing file, by using the function freopen( ), which has the prototype:



FILE *freopen(const char *name,const char *mode,FILE *fp);



The freopen( ) function closes the existing stream, then attempts to reopen it with the specified filename. This is useful for redirecting the predefined streams stdin, stdout, and stderr to a file or device. For example, if you wish to redirect all output intended to stdout (usually the host computer's display device) to a printer, you might use:



freopen("LPT1","w",stdout);



Predefined I/O Streams



There are three predefined I/O streams: stdin, stdout, and stderr. The streams stdin and stdout default to the keyboard and display, respectively, but can be redirected on some hardware platforms, such as the PC and under UNIX. The stream stderr defaults to the display, and is not usually redirected by the operator. It can be used for the display of error messages even when program output has been redirected:



fputs("Error message",stderr);



The functions printf ( ) and puts( ) forward data to the stream stdout and can therefore be redirected by the operator of the program; scanf( ) and gets() accept input from the stream stdin.



As an example of file I/O with the PC, consider the following short program that does a hex dump of a specified file to the predefined stream, stdout, which may be redirected to a file using:



dump filename.ext > target.ext

#include #include #include #include



main(int argc, char *argv[])

{

unsigned counter; unsigned char v1[20];

int f1;

int x; int n;



if (argc != 2)

{

fputs("\nERROR: Syntax is dump f1 \n",stderr); return(1);

}

f1 = open(argv[1],O_RDONLY); if (f1 == -1)

{

fprintf(stderr,"\nERROR: Unable to open %s\n",argv[1]); return(1);



fprintf(stdout,"\nDUMP OF FILE %s\n\n",strupr(argv[1])); counter = 0;



while(1)

{

/* Set buffer to zero bytes */ memset(v1,0,20);



/* Read buffer from file */ x = _read(f1,&v1,16);



/* x will be 0 on EOF or -1 on error */ if (x < 1) break;



/* Print file offset to stdout */ fprintf(stdout,"%06d(%05x) ",counter,counter);



counter += 16;



/* print hex values of buffer to stdout */ for(n = 0; n < 16; n++) fprintf(stdout,"%02x ",v1[n]);



/* Print ascii values of buffer to stdout */

for(n = 0; n < 16; n++)

{

if ((v1[n] > 31) && (v1[n] < 128))

fprintf(stdout,"%c",v1[n]);

else

fputs(".",stdout);

}

/* Finish the line with a new line */ fputs("\n",stdout);

}



/* successful termination */ return(0);

}



Strings



The C language has one of the most powerful string-handling capabilities of any general-purpose computer language. A string is a single dimension array of characters terminated by a zero byte. Strings may be initialized in two ways, either in the source code where they may be assigned a constant value, as in: char *p = "System 5";

char name[] = "Test Program" ;

}



or at runtime by the function strcpy( ), which has the function prototype:



char *strcpy(char *destination, char *source);



The strcpy( ) function copies the source string into the destination location, as in the following example:

#include int main()

{

char name[50];

strcpy(name,"Servile Software"); printf("\nName equals %s",name);

}

C also allows direct access to each individual byte of the string:

#include int main()

{

char name[50];

strcpy(name,"Servile Software");

printf("\nName equals %s",name);

/* Replace first byte with lower case 's' */ name[0] = 's';



printf("\nName equals %s",name);

}



Some C compilers include functions to convert strings to upper- and lowercase, but these functions are not defined in the ANSI standard. However, the ANSI standard does define the functions toupper( ) and tolower( ) that return an integer parameter converted to upper- and lowercase, respectively. By using these functions, you can create our own ANSI-compatible versions:



#include



void strupr(char *source)

{

char *p; p = source;

*p = toupper(*p);

p++;

}

}



void strlwr(char *source)

{

char *p;



p = source;

while(*p)

{

*p = tolower(*p);

p++;

}

}



int main()

{

char name[50];

strcpy(name,"Servile Software"); printf("\nName equals %s",name); strupr(name);

printf("\nName equals %s",name); strlwr(name);

printf("\nName equals %s",name);

}



C does not impose a maximum string length, unlike other computer languages. However, some CPUs impose restrictions on the maximum size of a memory block. An example program to reverse all the characters in a string is:



#include #include



char *strrev(char *s)

{

/* Reverses the order of all characters in a string except the nu ll */

/* terminating byte */



char *start; char *end; char tmp;



/* Set pointer 'end' to last character in string */

/* Preserve pointer to start of string */ start = s;



/* Swop characters */ while(end >= s)

{

tmp = *end; *end = *s; *s = tmp; end-- ; s++;

}

return(start);

}



main()

{

char text[100]; char *p;



strcpy(text,"This is a string of data");

p = strrev(text); printf("\n%s",p);





strtok( )



The function strtok( ) is a very powerful standard C feature for extracting substrings from within a single string. It is used when the substrings are separated by known delimiters, such as the commas in the following example:



#include #include



main()

{

char data[50]; char *p;



strcpy(data,"RED,ORANGE,YELLOW,GREEN,BLUE,INDIGO,VIOLET");



p = strtok(data,",");

while(p) {

puts(p);

p = strtok(NULL,",");

};

}



A variation of this program can be written with a for( ) loop:

#include #include



main()

{

char data[50]; char *p;



strcpy(data,"RED,ORANGE,YELLOW,GREEN,BLUE,INDIGO,VIOLET");



for(strtok(data,","); p; p = strtok(NULL,","))

{

puts(p);

};

}



Initially, you call strtok( ) with the name of the string variable to be parsed, and a second string that contains the known delimiters. Strtok( ) then returns a pointer to the start of the first substring and replaces the first token with a zero delimiter. Subsequent calls to strtok( ) can be made in a loop, passing NULL as the string to be parsed; strtok( ) will return the subsequent substrings. Since strtok( ) can accept numerous delimiter characters in the second parameter string, you can use it as the basis of a simple word-counting program:



#include #include #include



void main(int argc, char *argv[])

{

FILE *fp;

char buffer[256];

char *p;

long count;

if (argc != 2) {

fputs("\nERROR: Usage is wordcnt \n",stderr);

exit(0);

}



/* Open file for reading */ fp = fopen(argv[1],"r");



/* Check the open was okay */

if (!fp)

{

fputs("\nERROR: Cannot open source file\n",stderr);

exit(0);

}



/* Initialize word count */ count = 0;



do

/* Read a line of data from the file */ fgets(buffer,255,fp);



/* check for an error in the read or EOF */ if (ferror(fp) || feof(fp)) continue;

the characters */ ) - and [space] */



/* count words in received line */ /* Words are defined as separated by /* \t(tab) \n(newline) , ; : . ! ? ( p = strtok(buffer,"\t\n,;:.!?()- ");

while(p) {

count++;

p = strtok(NULL,"\t\n,;:.!?()- ");

}

}

while(!ferror(fp) && !feof(fp));



/* Finished reading. Was it due to an error? */ if (ferror(fp))



{

fputs("\nERROR: Reading source file\n",stderr); fclose(fp);

exit(0);

}



/* Reading finished due to EOF, quite valid so print count */ printf("\nFile %s contains %ld words\n",argv[1],count); fclose(fp);

}



Converting Numbers To and From Strings



All C compilers provide a facility for converting numbers to strings such as sprintf( ). However, sprintf( ) is a multipurpose function, meaning that it is large and slow. The function ITOS( ) can be used instead, as it accepts two parameters, the first being a signed integer and the second being a pointer to a character string. It then copies the integer into the memory pointed to by the character pointer. As with sprintf( ), ITOS( ) does not check that the target string is long enough to accept the result of the conversion. An example function for copying a signed integer into a string would be:



void ITOS(long x, char *ptr)

{

/* Convert a signed decimal integer to a string */



long pt[9] = { 100000000, 10000000, 1000000, 100000, 10000, 1000

, 100, 10, 1 } ;

int n;



/* Check sign */

if (x < 0)

{

*ptr++ =

/* Convert x to absolute */ x = 0 - x;





for(n = 0; n < 9; n++) {

if (x > pt[n])

{

*ptr++ = '0' + x / pt[n]; x %= pt[n];

}

}

return;

}



To convert a string into a floating-point number, C provides two functions: atof( ) and strtod( ); atof( ) has the prototype:

double atof(const char *s); and strtod( ) has the prototype:

double strtod(const char *s,char **endptr);

Both functions scan the string and convert it as far as they can, until they come across a character they don't understand. The difference between the two functions is that if strtod( ) is passed a character pointer for parameter endptr, it sets that pointer to the first character in the string that terminated the conversion. Because of better error reporting, by way of endptr, strtod( ) is often preferred over atof( ).



To convert a string into an integer, you can use atoi( ); it has the prototype:



int atoi(const char *s);



Note that atoi( ) does not check for an overflow, and the results are undefined. The atol( )function is similar but returns a long. Alternatively, you can use strtol( ) and stroul( ) instead for better error checking.

No comments:

Post a Comment