所有 C++ 程序都必须具有 main 函数。 如果尝试在没有 main 函数的情况下编译 C++ 程序,编译器将引发错误。 (动态链接库和 static 库没有 main 函数。)main 函数是源代码开始执行的位置,但在程序进入 main 函数之前,没有显式初始值设定项的所有 static 类成员都设为零。 在 Microsoft C++ 中,全局 static 对象在进入 main 前也进行初始化。 一些限制适用于 main 函数,而不适用于任何其他 C++ 函数。
main 函数:
- 无法重载(请参阅函数重载)。
- 无法声明为
inline。 - 无法声明为
static。 - 无法提取其地址。
- 无法从程序调用。
main 函数签名
main 函数没有声明,因为它内置于语言中。 如果有,则 main 的声明语法如下所示:
int main();
int main(int argc, char *argv[]);
如果 main 中未指定返回值,编译器会提供零作为返回值。
标准命令行参数
main 的参数可进行方便的命令行分析。
argc 和 argv 的类型由语言定义。 名称 argc 和 argv 是传统名称,但你可以按自己的意愿命名。
自变量定义如下所示:
argc
包含 argv 后面的参数计数的整数。
argc 参数始终大于或等于 1。
argv
表示由杂注用户输入的命令行自变量的以 null 结尾的字符串的数组。 按照约定,argv[0] 是用于调用程序的命令。
argv[1] 是第一个命令行参数。 命令行的最后一个参数是 argv[argc - 1],并且 argv[argc] 始终为 NULL。
有关如何禁用命令行处理的信息,请参阅自定义 C++ 命令行处理。
注意
按照约定,argv[0] 是程序的文件名。 但在 Windows 上,可以使用 CreateProcess 来生成进程。 如果同时使用了第一个和第二个参数(lpApplicationName 和 lpCommandLine),则 argv[0] 可能不是可执行名称。 可使用 GetModuleFileName 来检索可执行名称及其完全限定的路径。
特定于 Microsoft 的扩展
以下部分介绍特定于 Microsoft 的行为。
wmain 函数和 _tmain 宏
如果将源代码设计为使用 Unicode 宽字符,则可以使用特定于 Microsoft 的 wmain 入口点,即宽字符版的 main。 下面是 wmain 的有效声明语法:
int wmain();
int wmain(int argc, wchar_t *argv[]);
还可以使用特定于 Microsoft 的 _tmain,它是 tchar.h 中定义的预处理器宏。 除非定义了 _tmain,否则 main 解析为 _UNICODE。 在该示例中,_tmain 将解析为 wmain。 对于需要分别生成窄字符集和宽字符集的代码来说,_tmain 宏和以 _t 开头的其他宏非常有用。 有关详细信息,请参阅使用一般文本映射。
从 void 返回 main
main 和 wmain 函数作为 Microsoft 扩展,可以声明为返回 void(没有返回值)。 此扩展在其他一些编译器中也可用,但不建议使用它。 当 main 不返回值时,它可用于保持对称。
如果将 main 或 wmain 声明为返回 void,则无法使用 exit 语句将 return 代码返回到父进程或操作系统中。 若要在将 exit 或 main 声明为 wmain 时返回 void 代码,则必须使用 exit 函数。
envp 命令行参数
main 或 wmain 签名允许可选的 Microsoft 特定扩展访问环境变量。 此扩展在 Windows 和 UNIX 系统的其他编译器中也很常见。 名称 envp 是传统名称,但你可以根据自己的意愿命名环境参数。 下面是包含环境参数的参数列表的有效声明:
int main(int argc, char* argv[], char* envp[]);
int wmain(int argc, wchar_t* argv[], wchar_t* envp[]);
envp
可选 envp 参数是表示用户环境中设置的变量的字符串数组。 该数组由 NULL 项终止。 它可以声明为指向 char (char *envp[]) 的指针数组,也可以声明为一个指针来指向多个指向 char (char **envp) 的指针。 如果程序使用 wmain 而不是 main,请使用 wchar_t 数据类型而不是 char。
传递给 main 和 wmain 的环境块是当前环境的“冻结”副本。 如果随后通过调用 putenv 或 _wputenv 来更改环境,则当前环境(由 getenv 或 _wgetenv 以及 _environ 或 _wenviron 变量返回)将发生更改,但 envp 指向的块不会更改。 有关如何禁用环境处理的更多信息,请参阅自定义 C++ 命令行处理。
envp 参数与 C89 标准兼容,但与 C++ 标准不兼容。
main 的示例参数
下面的示例演示如何使用 argc 的 argv、envp 和 main 变量:
// argument_definitions.cpp
// compile with: /EHsc
#include <iostream>
#include <string.h>
using namespace std;
int main( int argc, char *argv[], char *envp[] )
{
bool numberLines = false; // Default is no line numbers.
// If /n is passed to the .exe, display numbered listing
// of environment variables.
if ( (argc == 2) && _stricmp( argv[1], "/n" ) == 0 )
numberLines = true;
// Walk through list of strings until a NULL is encountered.
for ( int i = 0; envp[i] != NULL; ++i )
{
if ( numberLines )
cout << i << ": "; // Prefix with numbers if /n specified
cout << envp[i] << "\n";
}
}
分析 C++ 命令行自变量
Microsoft C/C++ 代码使用的命令行分析规则特定于 Microsoft。 在解释操作系统命令行上给出的参数时,运行时启动代码使用这些规则:
参数用空白分隔,空白可以是一个空格或制表符。
第一个参数 (
argv[0]) 是经过专门处理的。 它表示程序名称。 因为它必须是有效的路径名,因此允许用双引号 (") 括起来一些部分。 双引号不包含在argv[0]输出中。 用双引号括起来的部分可以防止将空格或制表符解释为参数的末尾。 此列表中的后续规则不适用。将双引号括起来的字符串解释为单个参数,哪怕其中可能包含空格字符。 带引号的字符串可以嵌入在自变量内。 插入点 (
^) 未被识别为转义字符或者分隔符。 在带引号的字符串中,一对双引号被解释为单个转义的双引号。 如果在找到右双引号之前命令行结束,则到目前为止读取的所有字符都将输出为最后一个参数。前面有反斜杠的双引号 (
\") 被解释为原义双引号 (")。反斜杠按其原义解释,除非它们紧位于双引号之前。
如果偶数个反斜杠后跟双引号,则每对反斜杠 (
\) 中有一个反斜杠 (argv) 被置于\\数组中,而双引号 (") 被解释为字符串分隔符。如果奇数个反斜杠后跟双引号,则每对反斜杠 (
\) 中有一个反斜杠 (argv) 被置于\\数组中。 双引号由剩余反斜杠解释为转义序列,导致原义双引号 (") 被置于argv中。
命令行参数分析示例
以下程序演示了如何传递命令行参数:
// command_line_arguments.cpp
// compile with: /EHsc
#include <iostream>
using namespace std;
int main( int argc, // Number of strings in array argv
char *argv[], // Array of command-line argument strings
char *envp[] ) // Array of environment variable strings
{
int count;
// Display each command-line argument.
cout << "\nCommand-line arguments:\n";
for( count = 0; count < argc; count++ )
cout << " argv[" << count << "] "
<< argv[count] << "\n";
}
命令行分析结果
下表显示了示例输入和预期输出,演示了前面列表中的规则。
| 命令行输入 | argv[1] | argv[2] | argv[3] |
|---|---|---|---|
"abc" d e |
abc |
d |
e |
a\\b d"e f"g h |
a\\b |
de fg |
h |
a\\\"b c d |
a\"b |
c |
d |
a\\\\"b c" d e |
a\\b c |
d |
e |
a"b"" c d |
ab" c d |
通配符扩展
Microsoft 编译器根据需要允许你使用通配符字符、问号 (?) 和星号 (*),以在命令行上指定文件名和路径参数。
命令行参数由运行时启动代码中的内部历程处理,默认情况下,该例程不会将通配符扩展到 argv 字符串数组的单独字符串中。 通过将 setargv.obj 文件(对 wsetargv.obj 来说为 wmain 文件)包括在 /link 编译器选项或 LINK 命令行中,可以启用通配符扩展。
有关运行时启动链接器选项的详细信息,请参阅链接选项。
自定义 C++ 命令行处理
如果程序不采用命令行参数,则可以取消命令行处理例程来节省少量空间。 若要禁止使用该方法,请在 noarg.obj 编译器选项或 main 命令行中包含 wmain 文件(用于 /link 和 LINK)。
同样,如果从不通过 envp 参数访问环境表,则可以取消内部环境处理例程。 若要禁止使用该方法,请在 noenv.obj 编译器选项或 main 命令行中包含 wmain 文件(用于 /link 和 LINK)。
程序可以调用 C 运行时库中的 spawn 或 exec 系列例程。 如果是这样,则不应取消环境处理例程,因为可使用它将环境从父进程传递到子进程中。