Expert C++
上QQ阅读APP看书,第一时间看更新

main()

Program execution starts with the main() function, the designated start of the program as stated in the standard. A simple program outputting the Hello, World! message will look like this:

#include <iostream>
int main() {
std::cout << "Hello, World!" << std::endl;
return 0;
}

You may have encountered or used in your programs the arguments of the main() function. It has two arguments, argc and argv, allowing strings to be passed from the environment, usually referred to as the command-line arguments

The names argc and argv are conventional and can be replaced with anything you want. The argc argument holds the number of command-line arguments passed to the main() function; the argv argument holds the arguments:

#include <iostream>
int main(int argc, char* argv[]) {
std::cout << "The number of passed arguments is: " << argc << std::endl;
std::cout << "Arguments are: " << std::endl;
for (int ix = 1; ix < argc; ++ix) {
std::cout << argv[ix] << std::endl;
}
return 0;
}

For example, we can compile and run the preceding example with the following arguments:

$ my-program argument1 hello world --some-option

This will output the following to the screen:

The number of passed arguments is: 5
Arguments are:
argument1
hello
world
--some-option

When you look at the number of arguments, you'll notice that it is 5. The first argument is always the name of the program; that's why we skipped it in the example by starting the loop from number 1.

Rarely, you can see a widely supported but not standardized third argument, most commonly named envp. The type of envp is an array of char pointers and it holds the environment variables of the system.

The program can contain lots of functions, but the execution of the program always starts with the main() function, at least from the programmer's perspective. Let's try to compile the following code:

#include <iostream>

void foo() {
std::cout << "Risky foo" << std::endl;
}

// trying to call the foo() outside of the main() function
foo();

int main() {
std::cout << "Calling main" << std::endl;
return 0;
}

g++ raises an error on the foo(); call C++ requires a type specifier for all declarations. The call was parsed as a declaration rather than an instruction to execute. The way we tried to call a function before main() might seem silly for seasoned developers, so let's try another way. What if we declare something that calls a function during its initialization? In the following example, we define a BeforeMain struct with a constructor printing a message, and then we declare an object of type BeforeMain in the global scope:

#include <iostream>

struct BeforeMain {
BeforeMain() {
std::cout << "Constructing BeforeMain" << std::endl;
}
};

BeforeMain b;

int main() {
std::cout << "Calling main()" << std::endl;
return 0;
}

The example successfully compiles and the program outputs the following:

Constructing BeforeMain
Calling main()

What if we add a member function to BeforeMain and try to call it? See the following code to understand this:

struct BeforeMain {
// constructor code omitted for brevity
void test() {
std::cout << "test function" << std::endl;
}
};

BeforeMain b;
b.test(); // compiler error

int main() {
// code omitted for brevity
}

The call to test() won't be successful. So we cannot call a function before main(), but we can declare variables- objects that would be initialized by default. So there is definitely something that does an initialization before main() is actually called. It turns out that the main() function is not the true starting point of a program. The actual starting function of the program prepares the environment, that is, collects the arguments passed to the program, and then calls the main() function. This is required because C++ supports global and static objects that need to be initialized before the program begins, which means before the main() function is called. In the Linux world, this function is referred to as __libc_start_main. The compiler augments the generated code with the call of __libc_start_main, which in turn may or may not call other initialization functions before the main() function gets called. Going abstract, just imagine that the preceding code will be altered to something similar to the following:

void __libc_start_main() {
BeforeMain b;
main();
}
__libc_start_main(); // call the entry point

We will examine the entry point in more detail in the upcoming chapters.