Parameters of the main function
Video:
The argc and argv parameters
The main function is declared in one of the following ways:
1
2
3
4
5
int main() { /* ... */ }
int main( int argc, char *argv[]) { /* ... */ }
int main( int argc, char **argv) { /* same as previous */ }
// only when the host environment supports it (mainly UNIX):
int main( int argc, char *argv[], char *envp[]) { /* ... */ }
If argc is defined then argv[argc] is NULL pointer. If argc is greater than zero, then argv[0] is the name of the program, and argv[1] … argv[argc-1] are the program parameters. The argv[i] parameters are pointers to NULL terminated character arrays.
In usual environments, argc is always greater than zero.
1
2
3
4
5
6
7
8
#include <iostream>
int main(int argc, char *argv[])
{
std::cout << "name of the program = " << argv[0] << '\n';
for (int i = 1; i < argc; ++i)
std::cout << "argv[" << i << "] = " << argv[i] << '\n';
return 0;
}
In the following we implement a grep-like program that can read input line by line and print those lines to the standard output when there is matching with the patterns given as the first mandatory parameter.
We will implement only a small subset of grep, we will concentrate mainly on the handling of the command line parameters.
Here is a minimal version:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
/*
* gr.cpp -- a minimal grep-like program
* usage: gr pattern [files...]
*
*/
#include <iostream> // for basic io
#include <fstream> // for files
#include <string> // for string class
#include <cerrno> // for errno
#include <cstring> // for strerror()
#include <cstdlib> // for exit() in usage()
//
// matching function called for all lines
//
void gr( std::istream& in, std::string pattern)
{
std::string line;
while ( std::getline( in, line) )
{
if ( std::string::npos != line.find(pattern) )
{
std::cout << line << std::endl;
}
}
}
int main( int argc, char *argv[])
{
if ( argc < 2 ) // missing pattern
{
std::cerr << "Usage: " << argv[0]
<< " pattern [files...]" << std::endl;
std::exit(1); // terminate program
}
else if ( argc < 3 ) // missing filenames
{
gr( std::cin, argv[1]);
}
else
{
for ( int i = 2; i < argc; ++i ) // start i from 2
{
std::ifstream inpfile( argv[i]); // constructor
if ( ! inpfile ) // converted to logical value
{
// generate readable error message
std::setlocale(LC_MESSAGES, "en_EN.utf8");
std::cerr << argv[i] << ": "
<< std::strerror(errno) << std::endl;
}
else
{
gr( inpfile, argv[1]);
}
} // inpfile destructor called: closes automatically
}
return 0;
}
Let start to explain the program from the main function.
1
2
3
4
5
6
7
8
int main( int argc, char *argv[])
{
if ( argc < 2 ) // missing pattern
{
std::cerr << "Usage: " << argv[0]
<< " pattern [files...]" << std::endl;
std::exit(1); // terminate program
}
The main function declares the argc and argv parameters. As in argv the 0th element is the program name itself, and the 1st is the mandatory pattern parameter, if we have less argument, we emit error message - usually about the correct usage of the program - and finish the program. Here we use argv[0] as the program name, since the user can rename the binary which can be extracted only from here.
1
2
3
4
else if ( argc < 3 ) // missing filenames
{
gr( std::cin, argv[1]);
}
If there is further parmeter(s), we suppose they are filenames. In
an extended version of the program here we should have handle other
program parameters, i.e. flags.
The actual work for a whole file happens in gr function. If there is no further parameter after the mandatory pattern, we read from standard input and write the results to standard output. The arguments of gr are the input stream and the pattern itself.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
else
{
for ( int i = 2; i < argc; ++i ) // start i from 2
{
std::ifstream inpfile( argv[i]); // constructor
if ( ! inpfile ) // converted to logical value
{
// readable error message
std::setlocale(LC_MESSAGES, "en_EN.utf8");
std::cerr << argv[i] << ": "
<< std::strerror(errno) << std::endl;
}
else
{
gr( inpfile, argv[1]);
}
} // inpfile destructor called: closes automatically
}
Otherwise we have to walk thru all filename parameters, and construct an ifstream object representing the input stream. The constructor of ifstream tries to open the file. If successful, the object has value true and we pass it to gr. We do not need to close the file, the destructor of ifstream class will close the file at the end of the loop cycle.
If we can not open the file, we emit error message and continue with the next file.
The actual work happens in the gr file. The first parameter is the input stream and the second is the pattern to find.
We organize a loop reading lines until end of file with the help of the standard library function getline. The second parameter line is passed by reference and here placed the actual line as string.
1
2
3
4
5
6
7
8
9
10
11
void gr( std::istream& in, std::string pattern)
{
std::string line;
while ( std::getline( in, line) )
{
if ( std::string::npos != line.find(pattern) )
{
std::cout << line << std::endl;
}
}
}
The find(pattern) call on line try to find pattern as a substring of line. If this is succesfull, the actual position returns, otherwise returns the std::string::npos constant.
If the return value is not std::string::npos we have a match and we print the line.