main 함수

main 함수 (The main function)

프로그램은 호스티드 환경(hosted environment)에서 프로그램의 시작점으로 지정된, main 이라는 전역 함수를 반드시 포함해야 해요. main은 다음 형태 중 하나를 가져야 해요.

int main() { body }                             (1)
int main(int argc, char* argv[]) { body }       (2)
int main(/* implementation-defined */) { body } (3)
  1. 환경이 제공하는 인자와 독립적으로 실행되는 main 함수.
  2. 환경이 제공하는 인자를 받는 main 함수. argcargv의 이름은 임의로 정할 수 있고 매개변수 타입의 표현도 마음대로예요. int main(int ac, char** av)도 마찬가지로 유효해요.
  3. 구현 정의 타입의 main 함수로 int를 반환해요. C++ 표준은 구현 정의 main 함수가 선택적(추가) 매개변수를 argv 뒤에 두기를 권장해요.
  • argc — 프로그램이 실행되는 환경에서 프로그램에 넘겨진 인자 수를 나타내는 음이 아닌 값.
  • argvargc + 1개의 포인터 배열의 첫 요소를 가리키는 포인터. 마지막 포인터는 null 이고, 그 앞의 포인터들(있으면)은 실행 환경에서 프로그램에 넘겨진 인자를 나타내는 null 로 끝나는 멀티바이트 문자열들을 가리켜요. argv[0]이 null 포인터가 아니면(또는 동등하게 argc > 0이면), 프로그램을 호출하는 데 쓰인 이름을 나타내는 문자열이나 빈 문자열을 가리켜요.
  • body — main 함수의 본문.

출처: cppreference

본문

설명 (Explanation)

main 함수는 정적 저장 기간의 비지역 객체들이 초기화된 뒤, 프로그램 시작 시 호출돼요. main은 운영 체제가 있는 호스티드 환경에서 실행되는 프로그램의 지정된 진입점이에요. 프리스탠딩(freestanding) 프로그램(부트 로더, OS 커널 등)의 진입점은 구현 정의예요.

두 매개변수 형태의 main 함수 매개변수들은 실행 환경에서 임의의 멀티바이트 문자열(보통 명령줄 인자로 알려진)을 넘길 수 있게 해요. 포인터 [argv[1], argv[argc - 1]]는 이 문자열들 각각의 첫 문자를 가리켜요. argv[0](null 이 아니면)은 프로그램 자체를 호출하는 데 쓰인 이름을 나타내는 null 로 끝나는 멀티바이트 문자열의 첫 문자를 가리키는 포인터예요 (실행 환경이 지원하지 않으면 빈 문자열 ""일 수도 있어요). 이 문자열들은 수정 가능하지만, 이런 수정은 실행 환경으로 다시 전파되지 않아요. 예를 들어 std::strtok에 쓸 수 있죠. argv가 가리키는 배열의 크기는 적어도 argc + 1이고, 마지막 요소 argv[argc]는 null 포인터임이 보장돼요.

main 함수는 몇 가지 특별한 성질이 있어요.

  1. main 함수의 본문에 return 문이 없어도 돼요. 제어 흐름이 return 문을 만나지 않고 main 의 끝에 도달하면 return 0;을 실행한 것과 같은 효과가 있어요.
  2. return(또는 main 끝에 도달해 암시적으로 return)의 실행은, 우선 함수를 정상적으로 떠나는 것(자동 저장 기간 객체를 파괴하고 main 의 사후 조건 어서션을 평가하는 것, C++26부터)과 동등하고, 그 다음 return의 인자와 같은 인자로 std::exit을 호출하는 것과 동등해요 (std::exit는 그다음 정적 객체를 파괴하고 프로그램을 종료해요).

main 함수에는 몇 가지 제약이 있어요 (위반하면 프로그램은 규칙 위반이 돼요).

  1. 프로그램 어디에서도 이름을 붙일 수 없어요.
    • 특히 재귀적으로 호출할 수 없어요.
    • 그 주소를 취할 수 없어요.
    • typeid 표현식이나 decltype 지정자에 쓸 수 없어요 (C++11부터).
  2. 미리 정의할 수 없고 오버로드할 수 없어요. 사실상 전역 네임스페이스의 main 이름은 함수에게 예약돼 있어요 (단 클래스, 네임스페이스, 열거형, 비전역 네임스페이스의 모든 개체에 이름을 붙이는 데는 쓸 수 있어요. 단 main이라는 개체는 어떤 네임스페이스에서도 C 언어 링키지로 선언될 수 없어요).
  3. "C++" 이외의 언어 링키지로 선언될 수 없고, constexpr(C++11부터), consteval(C++20부터), inline, static으로도 선언될 수 없어요.
  4. 삭제된(deleted) 것으로 정의될 수 없어요. (C++11부터)
  5. 반환 타입을 추론할 수 없어요 (auto main() {...}는 허용되지 않아요). (C++14부터)
  6. 코루틴이 될 수 없어요.
  7. 명명된 모듈에 붙을 수 없어요. (C++20부터)

참고 (Notes)

  • main 함수가 함수 try 블록으로 정의되면, 정적 객체의 소멸자가(함축된 std::exit에 의해 파괴되면서) 던지는 예외는 그 try 블록이 잡아내지 못해요.
  • OS 명령줄에서 주어진 인자가 argv가 가리키는 멀티바이트 문자 배열로 변환되는 방식은 구현 정의 처리가 개입할 수 있어요. (Parsing C++ Command-Line Arguments MSDN, Shell Introduction POSIX)
  • 매우 흔한 구현 정의 형태의 main 함수는 argc, argv 외에 char** 타입의 세 번째 인자를 가지는데, 실행 환경 변수를 가리키는 포인터 배열을 가리켜요.

예제 (Example)

프로그램에게 입력을 어디서 찾고 결과를 어디에 쓸지 알려 주는 방법을 보여 줘요. 가능한 호출 형태는 ./convert table_in.dat table_out.dat.

#include <cstdlib>
#include <iomanip>
#include <iostream>

int main(int argc, char *argv[])
{
    std::cout << "argc == " << argc << '\n';

    for (int ndx{}; ndx != argc; ++ndx)
        std::cout << "argv[" << ndx << "] == " << std::quoted(argv[ndx]) << '\n';
    std::cout << "argv[" << argc << "] == "
              << static_cast<void*>(argv[argc]) << '\n';

    /* ... */

    return argc == 3 ? EXIT_SUCCESS : EXIT_FAILURE; // optional return value
}

가능한 출력:

argc == 3
argv[0] == "./convert"
argv[1] == "table_in.dat"
argv[2] == "table_out.dat"
argv[3] == 0

결함 보고 (Defect reports)

동작 변경 결함 보고로 주요하게 조정된 내용은 이래요. main 의 매개변수 이름을 지나치게 제한하던 것을 모든 유효한 이름을 지원하게 함(CWG 1003), main 이 언어 링키지로 선언되는 것이 금지됨(CWG 1886), main 이 consteval로 선언되는 것이 금지됨(CWG 2479), N3214 이후 main 의 사용 여부가 명확해지고 이름이 붙으면 사용된 것으로 간주됨(CWG 2811), main 이 "C++" 언어 링키지로 선언되는 것이 허용됨(P3618R0) 등이에요.

더 알아보기 (Learn more)