C++main函数与命令行参数,退出程序

本文翻译自:https://docs.microsoft.com/en-us/cpp/cpp/main-function-command-line-args?view=vs-2019

  (除动态链接库dll,静态链接库lib工程外)所有的C++程序都必须有一个main函数。如果你编译一个没有main函数的C++exe工程,编译器会提示错误。main函数是你的代码开始执行的地方,但在main函数调用前,所有的没有被显示初始化的static类成员都被设置为零。在微软C++中,调用main函数前全局静态对象也会被初始化。main函数相对于其他函数而言有以下不同之处:

  1. 不能被重载
  2. 不能被声明为inline,内联函数
  3. 不能被声明为static
  4. 不能获取它的地址,Cannot have its address taken
  5. 不能被调用

  main函数并没有声明,因为它是语言内置的。如果有的话,其形式如下所示:

int main();
int main(int argc, char *argv[], char *envp[]);
  • Microsoft Specific

  如果你的代码文件使用的是Unicode wide character,你可以使用wmain,是main函数的宽字符版本,其声明如下:

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

  你还可以使用_tmain,该函数定义在tchar.h。_tmain被解释为main,除非你定义了_UNICODE(在这种情况下,_tmain被解释为wmain)。

  如果main函数的返回值没有指定,编译器会提供一个值为零的返回值。另外,main wmain可以被声明为返回为void(没有返回值)。如果你将main wmain声明为void,那么你就不能通过return语句将exit code返回给程序的父进程或系统。在main wmain是void的时候,你需要使用the exit function来返回exit code。

  • END Microsoft Specific
  • Command line arguments

  main或wmain的参数可以方便地在命令行中解析参数,并可以选择性地获取环境变量。argc argv的类型由语言进行定义。argc argv envp是典型的变量名称,但是你也可以任意命名:

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

  这些变量的定义如下:

  • argc:用于表明argv变量中参数的个数的一个int值。argc的值总是大于等于1
  • argv:一个空终止符的string组成的数组,该参数由用户输入。按照惯例,argv[0]是该程序被调用的命令;argv[1]是第一个命令行参数,之后的是第二三。。。个参数,直到argv[argc],该参数总是NULL。

  注意:

  1. 第一个命令行参数总是argv[1],最后一个总是argv[argc-1]
  2. By convention, argv[0] is the command with which the program is invoked. However, it is possible to spawn a process using CreateProcess and if you use both the first and second arguments (lpApplicationName and lpCommandLine), argv[0] may not be the executable name; use GetModuleFileName to retrieve the executable name, and its fully-qualified path.
  • envp

  The envp array, which is a common extension in many UNIX systems, is used in Microsoft C++. It is an array of strings representing the variables set in the user's environment. This array is terminated by a NULL entry. It can be declared as an array of pointers to char (char *envp[]) or as a pointer to pointers to char (char **envp). If your program uses wmain instead of main, use the wchar_t data type instead of char. The environment block passed to main and wmain is a "frozen" copy of the current environment. If you subsequently change the environment via a call to putenv or _wputenv, the current environment (as returned by getenv or _wgetenv and the _environ or _wenviron variable) will change, but the block pointed to by envp will not change. See Customizing Command Line Processing for information on suppressing environment processing. This argument is ANSI compatible in C, but not in C++.

  •  例子:
// argument_definitions.cpp
// compile with: /EHsc
#include <iostream>
#include <string.h>

using namespace std;
int main( int argc, char *argv[], char *envp[] ) {
    int iNumberLines = 0;    // 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 )
         iNumberLines = 1;

    // Walk through list of strings until a NULL is encountered.
    for( int i = 0; envp[i] != NULL; ++i ) {
        if( iNumberLines )
            cout << i << ": " << envp[i] << "\n";
    }
}

解析C++命令行参数

  Microsoft C / C ++启动代码在解释操作系统命令行上给定的参数时使用以下规则:

  • 参数由空格(while space)分割,空格可能是space,也可能是tab
  • The caret character (^) is not recognized as an escape character or delimiter.字符^并不会被识别为转义字符或分隔符。命令行参数在传递给argv数组前,完全由系统的command-line parser处理。
  • 由双引号(“”)包围的字符串不管里面有多少个空格都会被解释为一个参数,带有引号的字符串可以嵌入到一个参数中
  • 以反斜杠(\)开头的双引号(即:\")被解释为双引号字符。A double quotation mark preceded by a backslash (\") is interpreted as a literal double quotation mark character (").
  • 斜杠(backslash)按照字面意思进行解释,不管它是否在双引号之前
  • 如果偶数个斜杠后面跟着一个双引号,则每连续的两个斜杠被解释为一个斜杠到argv中,双引号被解释为字符串定界符,具体看下面的表格
  • 如果奇数个斜杠后面跟着一个双引号,则每连续的一对(pair)斜杠被解释为一个斜杠,双引号被解释为''到argv(and the double quotation mark is "escaped" by the remaining backslash, causing a literal double quotation mark (") to be placed in argv

Results of parsing command lines

 
Command-Line Inputargv[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

例子:

// 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";
}

通配符扩展Wildcard expansion

  你可以使用通配符-问好(?)和星号(*)-用于在命令行上指定文件名和路径变量。

  Command-line arguments are handled by a routine called _setargv (or _wsetargv in the wide-character environment), which by default does not expand wildcards into separate strings in the argv string array. For more information on enabling wildcard expansion, refer to Expanding Wildcard Arguments.

Customizing C++ command-line processing

Microsoft Specific

If your program does not take command-line arguments, you can save a small amount of space by suppressing use of the library routine that performs command-line processing. This routine is called _setargv and is described in Wildcard Expansion. To suppress its use, define a routine that does nothing in the file containing the main function, and name it _setargv. The call to _setargv is then satisfied by your definition of _setargv, and the library version is not loaded.

Similarly, if you never access the environment table through the envp argument, you can provide your own empty routine to be used in place of _setenvp, the environment-processing routine. Just as with the _setargv function, _setenvp must be declared as extern "C".

Your program might make calls to the spawn or exec family of routines in the C run-time library. If it does, you shouldn't suppress the environment-processing routine, since this routine is used to pass an environment from the parent process to the child process.

END Microsoft Specific

 


 

退出程序

  在C++中,可以使用如下三种方式退出程序:

  1. 调用exit函数
  2. 调用abort函数
  3. 在main函数中执行return语句

exit函数

  exit函数是在头文件<stdlib.h>中声明的,用于结束C++程序。传递给exit函数的值作为程序的return code或exit code被传递给操作系统。通常情况下,返回一个零表示程序成功地结束。你可以使用常量EXIT_FAILURE,EXIT_SUCCESS来表明程序是成功退出,还是失败退出,这两个常量也定义在<stdlib.h>。

  在main函数中使用return语句与调用exit函数并将return的值作为其参数是等价的。

abort函数

   abort函数也是在<stdlib.h>头文件中声明的。exit与abort函数的不同之处在于exit允许C++运行时终止进程(run-time termination processing)生效(调用全局对象的析构函数),而abort函数是立即终止程序。abort函数绕过(bypass)已初始化的全局静态对象的正常销毁过程,并绕过使用atexit函数指定的任何特殊过程。 

atexit函数

   使用atexit函数可以指定一些特殊行为在程序结束前执行。在执行退出处理函数前,不会再atexit函数调用前销毁已经初始化的全局静态对象。 No global static objects initialized prior to the call to atexit are destroyed prior to execution of the exit-processing function.

return statement in main

   在main函数中使用return语句与调用exit函数是等价的。看下面的例子:

// return_statement.cpp
#include <stdlib.h>
int main()
{
    exit( 3 );
    return 3;
}

  上面例子中的exit和return在功能上是等价的。然而,C++要求main函数有返回类型,而不是返回一个void。However, C++ requires that functions that have return types other than void return a value.return语句允许你从main函数中返回一个值。

静态对象的销毁Destruction of static objects

  当你调用exit或在main函数中使用return时,静态对象以它们初始化时相反的顺序进行销毁(如果atexit函数存在的话,则这些对象的销毁晚于atexit),下面的例子展示了静态对象的初始化和清除是如何工作的。

  在下面的例子中,静态对象sd1 sd2是在进入main函数之前创建及初始化的。在程序通过return结束后,sd2首先被销毁,之后是sd1。ShowData类的析构函数关闭与这些静态对象相关联的文件。

// using_exit_or_return1.cpp
#include <stdio.h>
class ShowData {
public:
   // Constructor opens a file.
   ShowData( const char *szDev ) {
   errno_t err;
      err = fopen_s(&OutputDev, szDev, "w" );
   }

   // Destructor closes the file.
   ~ShowData() { fclose( OutputDev ); }

   // Disp function shows a string on the output device.
   void Disp( char *szData ) {
      fputs( szData, OutputDev );
   }
private:
   FILE *OutputDev;
};

//  Define a static object of type ShowData. The output device
//   selected is "CON" -- the standard output device.
ShowData sd1 = "CON";

//  Define another static object of type ShowData. The output
//   is directed to a file called "HELLO.DAT"
ShowData sd2 = "hello.dat";

int main() {
   sd1.Disp( "hello to default device\n" );
   sd2.Disp( "hello to file hello.dat\n" );
}

  上述代码的另一种写法是通过block scope来声明ShowData对象,当离开范围(scope)时销毁这些对象:

 

int main() {
   ShowData sd1, sd2( "hello.dat" );

   sd1.Disp( "hello to default device\n" );
   sd2.Disp( "hello to file hello.dat\n" );
}

 

posted @ 2020-07-03 14:39  adfas  阅读(1707)  评论(0编辑  收藏  举报