main ()이 실제로 C ++ 프로그램의 시작입니까?


131

C ++ 표준의 $ 3.6.1 / 1 섹션은 다음과 같습니다.

프로그램 은 프로그램 의 지정된 시작main 이라는 전역 함수를 포함해야합니다 .

이제이 코드를 고려하십시오.

int square(int i) { return i*i; }
int user_main()
{ 
    for ( int i = 0 ; i < 10 ; ++i )
           std::cout << square(i) << endl;
    return 0;
}
int main_ret= user_main();
int main() 
{
        return main_ret;
}

이 샘플 코드는 내가, 0부터 9까지의 정수의 제곱을 인쇄, 즉 그것을 할 의도 무엇을 수행 하기 전에 체결 main()(가) 프로그램의 "시작"있어야한다 기능.

또한 -pedanticGCC 4.5.0 옵션으로 컴파일했습니다 . 경고하지 않고 오류가 없습니다!

제 질문은

이 코드는 실제로 표준을 준수합니까?

표준 준수 인 경우 표준의 내용을 무효화하지 않습니까? main()이 프로그램의 시작이 아닙니다! user_main()전에 실행되었습니다 main().

나는 그 전역 변수를 초기화 이해 main_retuse_main()첫 번째가 실행을하지만 전혀 다른 일이다; 포인트는, 즉 않는 등, $ 3.6 / 1 표준에서 인용 문을 무효화 main()하지 않 시작 프로그램; 그것은 실제로입니다 프로그램!


편집하다:

'시작'이라는 단어를 어떻게 정의합니까?

"프로그램 시작" 구의 정의로 요약됩니다 . 정확히 어떻게 정의합니까?

답변:


85

C ++은 main을 호출하기 전에 "환경을 설정"하기 위해 많은 일을합니다. 그러나 main은 C ++ 프로그램의 "사용자 지정"부분의 공식 시작입니다.

std :: cout을 설정하는 초기 코드와 같은 일부 환경 설정은 제어 할 수 없지만 일부 환경은 정적 전역 변수 (정적 전역 변수 초기화 용)와 같이 제어 할 수 있습니다. 메인 전에 제어, 정적 블록이 초기화되는 순서를 완전히 제어 할 수 없습니다.

메인 후에는 수행 할 명령과 수행 순서를 모두 지정할 수 있다는 점에서 코드가 개념적으로 프로그램을 "완전히 제어"합니다. 멀티 스레딩은 코드 실행 순서를 재정렬 할 수 있습니다. 그러나 코드 섹션이 순서대로 실행되도록 지정했기 때문에 여전히 C ++로 제어 할 수 있습니다.


9
+1 "메인 이전에 모든 권한이 없으므로 정적 블록이 초기화되는 순서를 완전히 제어 할 수 없습니다. 메인 이후 코드는 개념적으로"완전히 제어 "됩니다. 이 프로그램은, 의미에서 당신은 모두가 수행 할 수있는 지침 및있는 "을 수행하는 순서를 지정할 수있다 . 이것은 또한이 답변을 수용 가능한 답변으로 표시하게합니다 ... 이것들은 "프로그램의 시작"으로 충분히 정당화 main()되는 매우 중요한 포인트라고 생각합니다
Nawaz

13
@Nawaz : 초기화 순서를 완전히 제어 할 수없는 것 외에도 초기화 오류를 제어 할 수 없습니다. 전역 범위에서 예외를 잡을 수는 없습니다.
André Caron

@Nawaz : 정적 글로벌 블록이란 무엇입니까? 간단한 예를 사용하여 설명해 주시겠습니까? 감사
소멸자

@meet : 네임 스페이스 수준에서 선언 된 객체가 static저장 기간과 같은 다른 번역 단위에 속하는 이러한 개체에 초기화 할 수 있는 (순서이기 때문에 순서 지정 기준에 의해). 그것이 귀하의 질문에 대한 답변인지 확실하지 않지만, 이것이이 주제의 맥락에서 말할 수있는 것입니다.
Nawaz

88

문장을 잘못 읽고 있습니다.

프로그램은 main이라는 글로벌 기능을 포함해야하며, 이는 프로그램의 지정된 시작입니다.

표준은 나머지 표준의 목적을 위해 "시작"이라는 단어를 정의하고 있습니다. main호출 되기 전에 코드가 실행되지 않는다고 말하는 것은 아닙니다 . 그것은 프로그램의 시작이 기능에 있다고 간주된다 main.

귀하의 프로그램은 준수합니다. 메인이 시작될 때까지 프로그램이 "시작되지 않았습니다". 생성자는 표준에서 "시작"의 정의에 따라 프로그램이 "시작"되기 전에 호출되지만 그다지 중요하지 않습니다. 코드의 많은 전에 실행 main되어 지금 뿐만 아니라 예, 모든 프로그램에서 호출.

토론을 위해 생성자 코드는 프로그램의 '시작'전에 실행되며 표준을 완전히 준수합니다.


3
죄송하지만 해당 조항에 대한 귀하의 해석에 동의하지 않습니다.
궤도에서 가벼움 레이스

Adam Davis가 맞다고 생각합니다. "main"은 일종의 코딩 제한과 비슷합니다.
laike9m

@LightnessRacesinOrbit 나는 후속 조치를 취하지 않았지만 문장은 논리적으로 "main이라는 글로벌 함수 는 프로그램 의 지정된 시작 입니다"로 강조됩니다 (강조 추가). 그 문장에 대한 당신의 해석은 무엇입니까?
Adam Davis

1
@AdamDavis : 나는 내 관심사가 무엇인지 기억하지 못한다. 나는 지금 생각할 수 없다.
궤도에서 가벼움 레이스

23

메인이 없으면 프로그램이 연결되지 않으므로 실행되지 않습니다. 그러나 파일 수준의 객체에는 미리 실행되는 생성자가 있고 main ()에 도달하기 전에 수명을 실행하는 전체 프로그램을 작성하고 main 자체를 가질 수 있기 때문에 main ()은 프로그램 실행을 시작하지 않습니다. 빈 몸.

실제로 이것을 시행하려면 프로그램의 모든 흐름을 호출하기 위해 main과 해당 생성자보다 먼저 생성 된 하나의 객체가 있어야합니다.

이거 봐요:

class Foo
{
public:
   Foo();

 // other stuff
};

Foo foo;

int main()
{
}

프로그램의 흐름은 효과적으로 Foo::Foo()


13
+1. 그러나 번역 단위가 다른 전역 객체가 여러 개인 경우 생성자가 호출되는 순서가 정의되어 있지 않기 때문에 문제가 발생할 수 있습니다. 싱글 톤과 게으른 초기화로 벗어날 수 있지만 멀티 스레드 환경에서는 상황이 매우 빠릅니다. 한마디로, 실제 코드에서는 이것을하지 마십시오.
Alexandre C.

3
main ()에 코드에서 적절한 본문을 제공하고 실행을 허용해야하지만 시작 외부의 객체 개념은 많은 LD_PRELOAD 라이브러리가 기반으로하는 것입니다.
CashCow

2
@Alex :이 표준은 정의되지 않았지만 실제 문제 링크 순서 (일반적으로 컴파일러에 따라 다름)로 시작 순서를 결정합니다.
ThomasMcLeod

1
@ 토마스 : 나는 심지어 원격으로 그것에 의존하려고하지 않을 것입니다. 또한 빌드 시스템을 수동으로 제어하려고 시도하지 않을 것입니다.
Alexandre C.

1
@Alex : 더 이상 중요하지는 않지만 링크 메모리를 사용하여 빌드 이미지를 제어하여 물리적 메모리 페이징을 줄입니다. 시작 성능 비교 테스트와 같이 프로그램 의미에 영향을 미치지 않는 경우에도 초기화 순서를 제어하려는 다른 이유가 있습니다.
ThomasMcLeod

15

질문에 "C"로 태그를 지정한 다음 C에 대해 엄격하게 말하면 ISO C99 표준의 6.7.8 "초기화"섹션에 따라 초기화가 실패합니다.

이 경우 가장 관련성이 높은 것은 제약 조건 # 4입니다.

정적 저장 시간을 갖는 객체에 대한 이니셜 라이저의 모든 표현식은 상수 표현식 또는 문자열 리터럴이어야합니다.

따라서 귀하의 질문에 대한 답변은 코드가 C 표준을 준수하지 않는다는 것입니다.

C ++ 표준에만 관심이 있다면 "C"태그를 제거하고 싶을 것입니다.


4
@ Remo.D이 섹션에 무엇이 있는지 알려주세요. 우리 모두에게 C 표준이있는 것은 아닙니다 :).
UmmaGumma

2
매우 까다롭기 때문에 : 아아, ANSI C는 1989 년부터 폐기되었습니다. ISO C90 또는 C99는 인용해야 할 관련 표준입니다.
Lundin

@Lundin : 아무도 까다 롭지 않습니다 :) ISO C99를 읽고 있었지만 C90에도 적용됩니다.
Remo.D

@샷. 당신 말이 맞아요, 여기 내가 가장 관련이 있다고 생각하는 문장을 추가했습니다.
Remo.D

3
@Remo : 유효하지 않은 정보를 제공 한 +1 C; 나는 그것을 몰랐다. 사람들이 때때로 계획에 따라, 때로는 우연히 배우는 방법을보십시오!
Nawaz

10

3.6 절은 전체적으로 상호 작용 main및 동적 초기화에 대해 매우 명확합니다 . "프로그램의 지정된 시작"은 다른 곳에서는 사용되지 않으며 일반적인 의도를 설명합니다 main(). 한 구절을 표준에서보다 상세하고 명확한 요구 사항과 모순되는 규범적인 방식으로 해석하는 것은 의미가 없습니다.


9

컴파일러는 종종 주 ()하기 전에 코드를 추가해야 표준을 준수. 표준 은 프로그램을 실행 하기 전에 전역 / 정적 초기화를 수행해야한다고 지정 하기 때문입니다. 언급 한 바와 같이 파일 범위 (전역)에 배치 된 객체의 생성자도 마찬가지입니다.

따라서 원래 질문 C 와도 관련이 있습니다. C 프로그램에서는 프로그램을 시작하기 전에 전역 / 정적 초기화를 수행해야하기 때문입니다.

표준은이 변수들이 프로그램 초기화 전에 어떻게 설정되어야 하는지 말하지 않기 때문에 "매직"을 통해 초기화된다고 가정합니다 . 나는 그것들을 프로그래밍 언어 표준의 범위를 벗어난 것으로 생각했다.

편집 : 예를 들어 ISO 9899 : 1999 참조 5.1.2 :

정적 저장 기간을 가진 모든 객체는 프로그램 시작 전에 초기화되어야합니다 (초기 값으로 설정). 그러한 초기화의 방식 및 타이밍은 달리 명시되지 않는다.

이 "매직"이 수행되는 방식에 대한 이론은 RAM 기반 컴퓨터에서 UNIX OS에만 사용되도록 프로그래밍 언어 인 C의 탄생으로 거슬러 올라갑니다. 이론적으로 프로그램은 프로그램 자체가 RAM에 업로드 된 동시에 실행 파일에서 RAM으로 사전 초기화 된 모든 데이터를로드 할 수 있습니다.

그 이후로 컴퓨터와 OS가 진화했으며 C는 원래 예상보다 훨씬 넓은 영역에서 사용됩니다. 최신 PC OS에는 가상 주소 등이 있으며 모든 임베디드 시스템은 RAM이 아닌 ROM에서 코드를 실행합니다. 따라서 RAM을 "자동으로"설정할 수없는 상황이 많이 있습니다.

또한 표준은 스택 및 프로세스 메모리 등에 대해 알기에는 너무 추상적입니다. 이러한 작업은 프로그램을 시작하기 전에 수행해야합니다.

따라서 거의 모든 C / C ++ 프로그램에는 표준의 초기화 규칙을 준수하기 위해 main이 호출되기 전에 실행되는 init / "copy-down"코드가 있습니다.

예를 들어, 임베디드 시스템에는 일반적으로 성능상의 이유로 전체 초기화 단계를 건너 뛰고 코드가 실제로 메인에서 직접 시작되는 "비 ISO 호환 시작"옵션이 있습니다. 그러나 전역 / 정적 변수의 초기 값에 의존 할 수 없으므로 이러한 시스템은 표준을 준수하지 않습니다.


4

"프로그램"은 단순히 전역 변수에서 값을 반환합니다. 다른 모든 것은 초기화 코드입니다. 따라서 표준에 따르면 매우 간단한 프로그램과 더 복잡한 초기화가 있습니다.



2

영어 의미 론적 퀴즈처럼 보입니다. OP는 먼저 코드 블록을 "코드"라고하고 나중에 "프로그램"이라고합니다. 사용자가 코드를 작성한 다음 컴파일러가 프로그램을 작성합니다.


1

main은 모든 전역 변수를 초기화 한 후에 호출됩니다.

표준이 지정하지 않은 것은 모든 모듈과 정적으로 링크 된 라이브러리의 모든 전역 변수의 초기화 순서입니다.


0

예, main은 구현 별 확장을 제외하고 모든 C ++ 프로그램의 "입력 지점"입니다. 그럼에도 불구하고 main_ret과 같은 전역 초기화와 같은 일부 작업이 메인 전에 발생합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.