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 Input 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

例子:

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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM