3.3. The main function¶
C++ programs are made up of functions and
every executable program must have exactly one function
named main that serves as the entry point for the program.
Libraries do not need a main, because they are intended to link
with another program that contains a main.
Several restrictions apply to the main function that
don't apply to any other C++ functions. The main function:
Does not need to be declared
Cannot be overloaded
Cannot be declared as inline or static
Cannot have its address taken
Cannot be called from your program
main
There are only a few main signatures that are valid entry points:
// if main takes no arguments
int main()
// if main takes command-line arguments
int main(int argc, char* argv[])
The names argc and argv are traditional.
You could use any valid identifier, but most programs use these.
int argc: the total number of arguments in argv, strings separated by white space (space or tab characters)char *argv[]: an array of these stringschar *argv[]can also be specified aschar **argv, which is the same thing, if you remember pointers from your first semester. If not, we'll cover it soon.
Some compilers allow passing the current system environment variables as a third argument passed into main. The environment variables are also passed in as an array of C strings.
int main(int argc, char** argv, char** envp)
If the linker can't find a function that evaluates to one of the above, then it will fail and your program build is incomplete.
Run It
A simple command line argument handling program.
1#include <cstring>
2#include <iostream>
3
4const char* help (const char* name); // declare function
5
6int main( int argc, char* argv[], char* envp[] )
7{
8 using std::cout;
9 bool number_lines = false;
10
11 // If -n is passed to main, display numbers
12 // next to environment variables.
13 for (int i=1; i < argc; ++i) {
14 if (std::strncmp(argv[i], "-h", 2) == 0) {
15 std::cerr << help(* argv);
16 return 0;
17 } else if (std::strncmp(argv[i], "-n", 2) == 0) {
18 number_lines = true;
19 } else {
20 std::cerr << "Unknown command line argument\n" << help(argv[0]);
21 return 1;
22 }
23 }
24
25 // Print environment until a NULL is encountered.
26 for (int i = 0; envp[i] != NULL; ++i) {
27 if (number_lines) {
28 cout << i << ": ";
29 }
30 cout << envp[i] << '\n';
31 }
32}
33
34const char* help (const char* name) {
35 std::cout << "Program '" << name << "':";
36 constexpr const char* text = R"eot(
37Options:
38 -h Show this text
39 -n Show numbers before each env variable.
40)eot";
41
42 return text;
43}
Another convention is to store the name of the program as invoked on the
command line in argv[0].
One side effect of this is that argc is never equal to zero and
the array argv always contains at least 1 c string.
Why bother with command line programs?
Command line arguments make programs more flexible. They allow users to run the same program in different ways, often to provide more or less detailed output or otherwise change the behavior at runtime.
C++ is primarily used in systems programming and is a fundamental part of all *nix programs. *nix is short for Unix (and friends), MacOS X, and GNU/Linux. The combination of command line argument handling and taking input from standard input and writing output to standard output is the core around which most *nix programs are designed.
3.3.1. Parsing command line arguments¶
Parsing the command line is all about getting the user entered C strings from the command line and into our program in a useful form.
The important thing to remember is that argc and argv are
passed automatically to main and are available for use.
If you run a program named foo invoked as:
/home/dave/foo -n 10 www.sdmesa.edu
Then argc would be set = 4 and array argv would contain
4 arrays of length 15:
Different program foo invocations would result in different values for argc and argv.
There is nothing special about the character -.
It is a convention used to distinguish command line arguments
with special meaning (the switches) from other content.
echo
A simple echo program can demonstrate using command line parameters in a program.
1#include <iostream>
2#include <string>
3
4int main(int argc, char** argv) {
5
6 // why did I initialize this to 1 instead of 0?
7 for (int i = 1; i < argc; ++i) {
8 std::cout << "Hello, " << argv[i] << '\n';
9 }
10
11}
Try This!
Run echo with a variety of inputs, such as:
San Diego
"Mesa College"
Can you explain the differences?
Parsing values
Everything that is passed to main through argv is a C string.
If you expect to receive a number on the command line,
you need to transform the value from a character array
into the appropriate numeric value yourself.
Traditional command line argument parsing proceeds as follows:
foreach argument
do
if the current value equals an expected value
process the argument
else if the current value equals a different expected value
process the argument
else
let the user know we received something unexpected
done if
done foreach
There are many ways to check if two character arrays are equivalent. In this example, we use strcmp:
if (std::strcmp(argv[i], "-h") == 0) {
// display help text
break;
}
The strcmp and related functions are defined in the legacy
C string header <cstring>.
The function compares two null-terminated byte strings
lexicographically (the way they would sort alphabetically).
The sign of the result is the sign of the difference between the
values of the first pair of characters
(both interpreted as unsigned char) that differ in the two strings.
The behavior is undefined if either argument are not pointers to
null-terminated strings.
If the function returns 0, the the two arrays are considered
equivalent.
Sometimes a command line argument is used to communicate that a value of a particular type is expected to follow. Let's say we want our hello world program to repeat its message a certain number of times. We need a way to communicate this information to the program.
if (std::strcmp(argv[i], "-r") == 0) {
// We should try to repeat,
// increment the loop counter based on argc
++i;
if (i < argc) { // is there really a next argument?
repeat = std::stoi(argv[i]);
} else {
std::cerr << "Error using '-r' argument: no repeat value provided\n";
}
There are many other ways to process the command line and many libraries exist to aid in the task. The technique presented here is simple and only uses facilities from the standard library.
Run It
1#include <cstring>
2#include <iostream>
3#include <string>
4
5using std::string;
6
7//
8// This function simply returns any text provided.
9//
10string echo(const string& text)
11{
12 return text;
13}
14
15string usage(const char* name) {
16 string msg = "Usage: ";
17 return msg.append(name).append(" [-h] [-r] [-n name]\n");
18}
19
20string help (const char* name) {
21 auto msg = usage(name);
22 constexpr auto text = R"help_text(
23Options:
24 -h Show this text
25 -r Number of times to repeat. Default = 1.
26 -n A name to say hello to. Default = "world".
27)help_text";
28
29 return msg.append(text);
30}
31
32
33int main(int argc, char** argv) {
34 int repeat = 1;
35 string who = "world";
36 // why did I initialize this to 1 instead of 0?
37 for (int i=1; i < argc; ++i) {
38 if (std::strncmp(argv[i], "-h", 2) == 0) {
39 std::cerr << help(* argv);
40 break;
41 } else if (std::strncmp(argv[i], "-r", 2) == 0) {
42 ++i;
43 if (i < argc) {
44 repeat = std::stoi(argv[i]);
45 } else {
46 std::cerr << "Error using '-r' argument: no repeat value provided\n";
47 }
48 } else if (std::strncmp(argv[i], "-n", 2) == 0) {
49 ++i;
50 if (i < argc) {
51 who = argv[i];
52 } else {
53 std::cerr << "Error using '-n' argument: no name provided\n";
54 }
55 } else
56 std::cerr << "Unknown argument '" << argv[i] << "' provided\n";
57 }
58 }
59
60 do {
61 std::cout << "Hello, " << who << "!\n";
62 --repeat;
63 } while (repeat > 0);
64
65 return 0;
66}
Try This!
Run this program with a variety of inputs and see what happens.
Try passing no arguments or switches, the same switch more than once, and a switch with no value after it.
A common source of confusion is distinguishing between 'standard input'
and the command line.
Parameters passed to a program after the program name are only
stored in the array argv.
Most operating systems allow you to use the special characters
<, > (redirection operators)
and | pipe operators to direct data into the standard input
of a program.
Information sent to a program using redirection or pipes is immediately
available for use by any facility that can process the standard input
stream, such as cin.
You can also use cin to manage a 'scripted conversion' with a user, where you prompt for input using cout and process the input using cin, however, processing standard input using redirection is far more flexible in terms of creating reusable programs that work together.
This idea is the foundation of Unix and its many derivatives, including GNU/Linux and Mac OS.