Spring 2002 COMP 122 Peter Smith
Handout #8
C – Part 2
In this handout we look at some features of C that you will probably not need this semester but will be handy in future classes.
Redirection of input and output
When running programs in a Unix environment, a simple way to read data from a file into a program and to save results and/or error messages to a file without making any changes to the program source code is to use the Unix shell redirection.
A C program has three built-in file streams: stdin, stderr and stdout. When you use scanf you are reading from stdin. When you use printf you are writing to stdout.
The following table shows how you can redirect the three streams when calling a.out.
Command
/Stdin
/Stdout
/Stderr
a.out / Keyboard / Screen / Screena.out < Datafile / Datafile / Screen / Screen
a.out > Results / Keyboard / Results / Screen
a.out < Datafile > Results / Datafile / Results / Screen
a.out & Allout / Keyboard / Allout / Allout
a.out < Datafile & Allout / Datafile / Allout / Allout
(a.out > Results) & Errors / Keyboard / Results / Errors
(a.out < Datafile > Results) & Errors / Datafile / Results / Errors
Later we look at attaching files to programs in general.
Command line arguments
In general, the main program function can have the following heading
main(int argc, char *argv[])
When the program runs, the operating system puts into argc (argument count) the number of tokens on the command line. This count includes the program name itself. Pointers to the texts of the tokens is put into the elements of argv (argument vector). Here is a simple program; it just echoes the components of the command line back to the standard output.
Example 13
#include <stdio.h>
int main (int argc, char* argv[])
{
int i;
printf("argc is: %d\n", argc);
for (i=0; i<argc; i++) printf("argv [%d] is %s\n",i,argv[i]);
}
Here is the compilation and a run of the program (gcc is the Gnu C compiler).
hp9k2 22: gcc ctesta.c
hp9k2 23: a.out one two three
argc is: 4
argv [0] is a.out
argv [1] is one
argv [2] is two
argv [3] is three
More pre-processor options
We can define symbols as in
#define MAXMEM 4096
#define PI 3.14159
to be used in various ways. For example we could declare int memory[MAXMEM]. Also we can define symbols to control debugging as in the following example.
Example 14
#define DEBUG 1
main()
{
int a[] = {2, 4, 3, -1, 7 };
int i,sum=0;
for (i=0; i<5; i++)
{
sum += a[i];
#ifdef DEBUG
printf ("sum is now %d\n",sum);
#endif
}
printf("sum is %d\n", sum);
}
The line between #ifdef and #endif is only included in the output from the preprocessor (and hence input to the compiler) if the symbol DEBUG has a non-zero value. There is also #ifndef.
hp9k2 22: gcc ctestb.c
hp9k2 23: a.out
sum is now 2
sum is now 6
sum is now 9
sum is now 8
sum is now 15
sum is 15
To remove the debugging information from the input to the compiler we need only change the value of the symbol to 0 (or to omit its definition altogether) thus
#define DEBUG 0
main()
{
int a[] = {2, 4, 3, -1, 7 };
int i,sum=0;
for (i=0; i<5; i++)
{
sum += a[i];
#ifdef DEBUG
printf ("sum is now %d\n",sum);
#endif
}
printf("sum is %d\n", sum);
}
Now when the program is compiled the debug statements are omitted
hp9k2 26: gcc ctestb.c
hp9k2 27: a.out
sum is 15
Macros
The preprocessor recognizes macro definitions. Consider the following program
Example 15
#include <stdio.h>
#define SQ(x) ((x) * (x))
main()
{
int i=4, j=9;
printf("%d %d %d\n",SQ(i),SQ(j),SQ(i+j));
}
When run, it outputs
16 81 169
Text files
We can attach text files to programs and read them in a simple way as in the following example
Example 16
#include <stdio.h>
#include <string.h>
main()
{
FILE *ifp, *ofp;
char temp[100];
int longest;
ifp = fopen("words","r"); /* open file for reading */
ofp = fopen("results99", "w"); /* open file for writing */
while (fscanf(ifp,"%s",temp) != EOF)
{
if (strlen(temp)>longest)
{
longest = strlen(temp);
fprintf(ofp,"%s\n",temp);
}
}
fclose(ifp);
fclose(ofp);
}
Notes on the program
- ifp and ofp are file variables to which we can attach files.
*The fopen function call specifies the file name and the mode in which it is attached to the program
- fscanf and fprintf are the more general versions of scanf and printf. The additional first parameter is the file variable.
- We read from the file of words until we reach the end of file (EOF)
- Every time we encounter a word longer than the longest one we have seen so far we output it to the results file.
Here is a run of the program.
hp9k2 22: gcc ctestc.c
hp9k2 23: a.out
hp9k2 24: cat results99
10th
Aarhus
abalone
abbreviate
aboveground
abovementioned
acknowledgeable
anthropomorphism
arteriolosclerosis
electroencephalogram
electroencephalograph
electroencephalography
More usefully we can let the user supply the names of files at run-time as in the following program that counts the number of lines and characters in a file.
Example 17
#include <stdio.h>
#include <string.h>
main(int argc, char *argv[])
{
if (argc<2)
printf ("Need a file name!\n");
else
{
FILE *fp;
int charcount=0, linecount=0;
char temp[100];
fp = fopen(argv[1],"r"); /* open file for reading */
while (fscanf(fp,"%s",temp) != EOF)
{
linecount++;
charcount+=strlen(temp);
}
fclose(fp);
printf("file %s has %d lines and %d characters\n",
argv[1],linecount,charcount);
}
}
hp9k2 22: gcc ctestd.c
hp9k2 23: a.out ctestd.c
file ctestd.c has 53 lines and 351 characters
hp9k2 24: a.out words
file words has 25143 lines and 181519 characters
Direct access files
When we open a file in the manner of the previous program they can only be read (or written) in a serial manner. In some applications we may wish to read a file randomly in the same way that we access an array
for example.
We have a conceptual file pointer and a function (fseek) that lets us move it to an arbitrary location within a file and another function (ftell) that reports on its current position.
The following program (from Kelley and Pohl) prompts the user for the name of a file then outputs the contents of the file backwards.
#include <stdio.h>
#define MAXSTRING 100
main()
{
char filename[MAXSTRING];
int c;
FILE *ifp;
printf("Input a file name: ");
scanf("%s", filename);
ifp = fopen(filename,"rb"); /* binary mode */
fseek(ifp,0,2); /* move to end of file */
fseek(ifp,-1,1); /* back up 1 character */
while (ftell(ifp) >=0)
{
c = getc(ifp); /* get one character */
putchar(c); /* output it */
if (ftell(ifp)==1) break;
fseek(ifp, -2, 1); /* backup two characters */
}
printf("\n");
}
Here is the program tested on itself
hp9k2 22: gcc ctestf.c
hp9k2 23: a.out
Input a file name: ctestf.c
}
;)"n\"(ftnirp
}
/* sretcarahc owt pukcab */ ;)1 ,2- ,pfi(keesf
;kaerb )1==)pfi(lletf( fi
/* ti tuptuo */ ;)c(rahctup
/* retcarahc eno teg */ ;)pfi(cteg = c
{
)0=> )pfi(lletf( elihw
/* retcarahc 1 pu kcab */ ;)1,1-,pfi(keesf
/* elif fo dne ot evom */ ;)2,0,pfi(keesf
/* edom yranib */ ;)"br",emanelif(nepof = pfi
;)emanelif ,"s%"(fnacs
;)" :eman elif a tupnI"(ftnirp
;pfi* ELIF
;c tni
;]GNIRTSXAM[emanelif rahc
{
)(niam
001 GNIRTSXAM enifed#
>h.oidts< edulcni#
1