| |
Main Menu |
VII. Run Time Parameters - Passing Values to 'main'
Programmers often make a distinction between compile time and
run time. Compile time is the period when a
program is being compiled, while run time is the period when a
program is running. The distinction is important, for example,
in terms of the different types of errors that can occur. By
this time you have seen many compile time error messages - error
messages generated by syntactic errors in your code. In contrast,
run time errors involve such things as trying to divide by 0.
Such an error would not be caught at compile time because, at that
point, the system does not know the values of the variables.
Dynamic vs. static memory allocation is also a compile time vs.
a run time issue. As you know, when a program is finished compiling,
all the instructions necessary to allocate memory for the program's
variables and constants have been written. At this point, one could
calculate all the static memory required of a program before running
it and see if a given computer was capable of providing the required
memory. Dynamic memory, on the other hand, is requested during
run time and, in may cases, there is no way to know ahead of time
how much memory will be required - although one can make estimates.
A. Run Time Parameters
Up to now we have written 'main' in all our programs so as not
receive any parameters. It is possible, however, in certain run
time environments to pass information to 'main' For example, the
operating systems 'DOS' and 'Unix' allow one to
call a program with parameters. This is so obvious that we tend
to ignore it.
Anyone who knows 'DOS' knows that one can write the following
command at the prompt:
which, of course, means to copy 'file1.txt' into 'file2.txt'.
What some may not realize is that 'copy' is a program and 'file1.txt'
and 'file2.txt' are values being passed to this program. In C++,
if we wish to use such run time parameters in a program,
we need to declare 'main' with two parameters. The first parameter
tells the system how many parameters there are and the second
parameter is an array of strings where each array element is one
of the parameters. When we run the program (by typing the program's
name at the operating system prompt, the parameters are passed
to 'main'.
For example, suppose we want to write our own version of 'copy'.
We would create a file called 'copy'
and place our 'main' in that file. The declaration for that 'main'
would look as follows:
The two parameter names 'argc' and 'argv' can be different but they are the standard ones used in C++. The first one holds the number of arguments in the run time call to the program. Interestingly enough, the name of the program is considered a parameter. Thus, if the following is typed at the operating system prompt:
the parameter 'argc' will hold the value 2.
B. Multi-Dimensional Arrays
The second argument represents the array of strings. We really
have not explored such arrays up to this point. All we have seen
are the so-called one-dimension arrays, for which one often
pictures the values of the array as one long line. For example,
a character array might look like:
FIGURE 11.4 DRAW an array with one name
However, an array of strings is two dimensional in the
sense that a good picture for it would be:
Figure 11.5 DRAW a 'table' of names.
Of course, one can have two-dimensional arrays of integers or other types for that matter. For example, if you wanted to record the temperature for each day of each month you could use an array with 31 columns and 12 rows - see Figure 11.6.
The declaration for this would be:
As an another example, suppose we wanted an array of 20 strings, where each string represented a name. If the maximum size of a name was 40, this array could be declared as:
To output the temperature on day 3 of the 10th month, one might
write the code:
To output the 15th character of the fourth name one might write
the code:
Two points about this:
When one wants to access a whole row of a two dimensional array
at the same time, one only uses the first array index. This would
not make sense with our temperature array but since strings can
be handled as a unit, it makes sense, for example, to write:
to output the 4th name in the array called 'names'. (Of course, the names better have been stored with null terminating characters or we will have problems with this output.)
C. The Meaning of 'Char**'
The code, 'char**', is a complex way of representing an array of
strings. We know that a string is an array and that arrays can
also be seen as pointers. Thus, we have previously used code such as
to represent a string - called name. Since a string is
an array, an array of strings is an array of arrays. Using the
language of pointers, an array of arrays of some type can be spoken
of as a pointer to a pointer to some type. In our case, a pointer
to a pointer to a char or 'char**'.
Although this pointer based representation is somewhat ugly, it
is the only way to represent the data coming into the program.
Note that we have no idea how large the strings will be or how
many there will be. In such a situation, the pointer-based approach
must be used to allow the system to allocate the required amount
of memory.
Here is the code for the whole 'copy' program:
// Demo of using arguments in main - a program to copy one file to another
// file: ch11copy.cpp
#include <iostream.h>
#include <fstream.h>
#include <stdlib.h>
typedef char string[80];
void main (int argc, char** argv)
// It is OK to use parameter names other than argc and argv but these are the standard
{ string inFileName;
string outFileName;
if (argc == 1)
{
cout << "Missing: Names of file to be copied and destination file\n";
exit(1);
}
if (argc == 2)
{ cout << "Missing: Name of destination file\n";
exit(1);
}
if (argc > 3)
{ cout << "Too many arguments\n";
exit(1);
}
ifstream inFile (argv[1]);
if (!inFile)
{ cout << "Cannot open input file\n";
exit(1);
}
ofstream outFile (argv[2]);
if (!outFile)
{ cout << "Cannot open output file\n";
exit(1);
}
// If both files have been successfully opened:
char ch;
while (outFile && inFile)
{ inFile.get(ch);
outFile << ch;
}
}
Notice how the two parameters are used. The program uses 'argc'
to make sure that the correct number of parameters have been entered.
It then uses the string in array element 'argv[1]' as the name
of the input file and the array element 'argv[2]' as the name
of the output file. In other words, the strings in the parameter
'argv' are the parameters passed to the program at run time. (By
the way, 'argv[0]' holds the string 'copy' - the name of the program.)
The rest of the code takes advantage of ideas we have already
discussed. However, the test in the 'while' loop might be a bit
puzzling. We have two files that are being worked with each time
through the loop and we need to make sure both files are OK. In
the case of the output file the loop will terminate only if a
problem occurs such as if there is no more room on the disk. The
input test is a bit more complex. The stream 'inFile' will have
an 'error' condition either if some problem occurs or if the end-of-file
marker is encountered. While 'end-of-file' is not really an error,
it does set to TRUE the value of the 'fail' flag and thus we can
use 'inFile' to check for the end of the file. In this way, the
while loop will terminate when there are no more characters to
read in the file to be copied.
| |
Main Menu |