main 函数和命令行参数

所有 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 的参数可进行方便的命令行分析。 argcargv 的类型由语言定义。 名称 argcargv 是传统名称,但你可以按自己的意愿命名。

自变量定义如下所示:

argc
包含 argv 后面的参数计数的整数。 argc 参数始终大于或等于 1。

argv
表示由杂注用户输入的命令行自变量的以 null 结尾的字符串的数组。 按照约定,argv[0] 是用于调用程序的命令。 argv[1] 是第一个命令行参数。 命令行的最后一个参数是 argv[argc - 1],并且 argv[argc] 始终为 NULL。

有关如何禁用命令行处理的信息,请参阅自定义 C++ 命令行处理

注意

按照约定,argv[0] 是程序的文件名。 但在 Windows 上,可以使用 CreateProcess 来生成进程。 如果同时使用了第一个和第二个参数(lpApplicationNamelpCommandLine),则 argv[0] 可能不是可执行名称。 可使用 GetModuleFileName 来检索可执行名称及其完全限定的路径。

特定于 Microsoft 的扩展

以下部分介绍特定于 Microsoft 的行为。

wmain 函数和 _tmain

如果将源代码设计为使用 Unicode 宽 character,则可以使用特定于 Microsoft 的 wmain 入口点,即宽 character 版的 main。 下面是 wmain 的有效声明语法:

int wmain();
int wmain(int argc, wchar_t *argv[]);

还可以使用特定于 Microsoft 的 _tmain,它是 tchar.h 中定义的预处理器宏。 除非定义了 _UNICODE,否则 _tmain 解析为 main。 在该示例中,_tmain 将解析为 wmain。 对于需要分别生成窄版和宽版 character 集的代码来说,_tmain 宏和以 _t 开头的其他宏非常有用。 有关详细信息,请参阅使用一般文本映射

从 main 返回 void

mainwmain 函数作为 Microsoft 扩展,可以声明为返回 void(没有返回值)。 此扩展在其他一些编译器中也可用,但不建议使用它。 当 main 不返回值时,它可用于保持对称。

如果将 mainwmain 声明为返回 void,则无法使用 return 语句将 exit 代码返回到父进程或操作系统中。 若要在将 mainwmain 声明为 void 时返回 exit 代码,则必须使用 exit 函数。

envp 命令行参数

mainwmain 签名允许可选的 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

传递给 mainwmain 的环境块是当前环境的“冻结”副本。 如果随后通过调用 putenv_wputenv 来更改环境,则当前环境(由 getenv_wgetenv 以及 _environ_wenviron 变量返回)将发生更改,但 envp 指向的块不会更改。 有关如何禁用环境处理的更多信息,请参阅自定义 C++ 命令行处理envp 参数与 C89 标准兼容,但与 C++ 标准不兼容。

main 的示例参数

下面的示例演示如何使用 mainargcargvenvp 变量:

// 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] 输出中。 用双引号括起来的部分可以防止将空格或 tab character 解释为参数的末尾。 此列表中的后续规则不适用。

  • 将双引号括起来的字符串解释为单个参数,哪怕其中可能包含空格 character。 带引号的字符串可以嵌入在自变量内。 未将插入点 (^) 识别为转义 character 或者分隔符。 在带引号的字符串中,一对双引号被解释为单个转义的双引号。 如果命令行结束时未发现后双引号,则到目前为止读取的所有 character 将输出为最后一个参数。

  • 前面有反斜杠的双引号 (\") 被解释为原义双引号 (")。

  • 反斜杠按其原义解释,除非它们紧位于双引号之前。

  • 如果偶数个反斜杠后跟双引号,则每对反斜杠 (\\) 中有一个反斜杠 (\) 被置于 argv 数组中,而双引号 (") 被解释为字符串分隔符。

  • 如果奇数个反斜杠后跟双引号,则每对反斜杠 (\\) 中有一个反斜杠 (\) 被置于 argv 数组中。 将双引号解释为包含 remaining 反斜杠的转义序列,导致将原义双引号 (") 置于 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 编译器根据需要允许你使用通配符 character、问号 (?) 和星号 (*),以在命令行上指定文件名和路径参数

命令行参数由运行时启动代码中的内部历程处理,默认情况下,该例程不会将通配符扩展到 argv 字符串数组的单独字符串中。 通过将 setargv.obj 文件(对 wmain 来说为 wsetargv.obj 文件)包括在 /link 编译器选项或 LINK 命令行中,可以启用通配符扩展。

有关运行时启动链接器选项的详细信息,请参阅链接选项

自定义 C++ 命令行处理

如果程序不采用命令行参数,则可以取消命令行处理例程来节省少量空间。 若要禁止使用该方法,请在 /link 编译器选项或 LINK 命令行中包含 noarg.obj 文件(用于 mainwmain)。

同样,如果从不通过 envp 参数访问环境表,则可以取消内部环境处理例程。 若要禁止使用该方法,请在 /link 编译器选项或 LINK 命令行中包含 noenv.obj 文件(用于 mainwmain)。

程序可以调用 C 运行时库中的 spawnexec 系列例程。 如果是这样,则不应取消环境处理例程,因为可使用它将环境从父进程传递到子进程中。

另请参阅

基本概念