programing tip

주 함수 대신 main이라는 전역 변수가있는 프로그램이 어떻게 작동 할 수 있습니까?

itbloger 2020. 8. 24. 07:52
반응형

주 함수 대신 main이라는 전역 변수가있는 프로그램이 어떻게 작동 할 수 있습니까?


다음 프로그램을 고려하십시오.

#include <iostream>
int main = ( std::cout << "C++ is excellent!\n", 195 ); 

Windows 7 OS에서 g ++ 4.8.1 (mingw64)을 사용하면 프로그램이 제대로 컴파일되고 실행되며 다음과 같이 인쇄됩니다.

C ++는 훌륭합니다!

콘솔에. main함수가 아닌 전역 변수로 보입니다. 이 프로그램이 기능없이 어떻게 실행될 수 main()있습니까? 이 코드는 C ++ 표준을 준수합니까? 프로그램의 동작이 잘 정의되어 있습니까? 나는 또한 -pedantic-errors옵션을 사용 했지만 프로그램은 여전히 ​​컴파일되고 실행됩니다.


무슨 일이 일어나고 있는지에 대한 질문에 들어가기 전에 결함 보고서 1886 : Language linkage for main ()에 따라 프로그램이 잘못 구성되었음을 지적하는 것이 중요합니다 .

[...] 전역 범위에서 변수 main을 선언하거나 모든 네임 스페이스에서 C 언어 연결로 main이라는 이름을 선언하는 프로그램은 형식이 잘못되었습니다. [...]

최신 버전의 clang 및 gcc는이를 오류로 만들고 프로그램이 컴파일되지 않습니다 ( gcc 라이브 예제 참조 ).

error: cannot declare '::main' to be a global variable
int main = ( std::cout << "C++ is excellent!\n", 195 ); 
    ^

그렇다면 왜 이전 버전의 gcc 및 clang에는 진단이 없었습니까? 이 결함 보고서에는 2014 년 말까지 제안 된 해결 방법이 없었기 때문에이 사례는 최근에야 명시 적으로 잘못 구성되어 진단이 필요했습니다.

이전에는 [basic.start.main] 섹션의 C ++ 표준 초안 shall 요구 사항을 위반하고 있으므로 정의되지 않은 동작 인 것 같습니다 .3.6.1

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

정의되지 않은 동작은 예측할 수 없으며 진단이 필요하지 않습니다. 행동을 재현 할 때 나타나는 불일치는 일반적인 정의되지 않은 행동입니다.

그렇다면 코드는 실제로 무엇을하고 있으며 어떤 경우에는 왜 결과를 생성합니까? 우리가 가진 것을 보자 :

declarator  
|        initializer----------------------------------
|        |                                           |
v        v                                           v
int main = ( std::cout << "C++ is excellent!\n", 195 ); 
    ^      ^                                   ^
    |      |                                   |
    |      |                                   comma operator
    |      primary expression
global variable of type int

우리는이 mainINT 전역 네임 스페이스에 선언 및 초기화되고, 변수는 정적 저장 기간을 가지고 있습니다. 호출 시도가 이루어지기 전에 초기화가 발생할지 여부는 구현에 정의 main되어 있지만 gcc가 호출하기 전에이를 수행하는 것으로 보입니다 main.

코드는 쉼표 연산자 를 사용하고 왼쪽 피연산자는 폐기 된 값 표현식이며 여기서는 호출의 부작용에만 사용됩니다 std::cout. 쉼표 연산자의 결과는이 경우 195변수에 할당 된 prvalue 오른쪽 피연산자입니다 main.

sergejcout 가 정적 초기화 중에 호출되는 생성 된 어셈블리 쇼를 지적하는 것을 볼 수 있습니다 . 토론에 대한 더 흥미로운 점 은 라이브 godbolt 세션을 참조하십시오 .

main:
.zero   4

그리고 후속 :

movl    $195, main(%rip)

가능한 시나리오는 프로그램이 main유효한 코드가있을 것으로 예상 하는 기호로 점프 하고 경우에 따라 seg-fault 합니다. 따라서이 경우 유효한 기계어 코드를 변수에 저장 하면 코드 실행을 허용하는 세그먼트에 있다고 가정 main하고 실행 가능한 프로그램으로 이어질 수 있습니다. 우리는 볼 수 있습니다 이 1984 IOCCC 항목이 수행 그냥 .

우리가 GCC가 (사용하여 C에서이 일을 얻을 수 있습니다 나타납니다 살고 볼 ) :

const int main = 195 ;

main실행 가능한 위치에 있지 않기 때문에 아마도 변수 가 const가 아닌 경우 seg-faults , Hat Tip은 여기 에이 아이디어를 제공했습니다.

Also see FUZxxl answer here to a C specific version of this question.


From 3.6.1/1:

A program shall contain a global function called main, which is the designated start of the program. It is implementation defined whether a program in a freestanding environment is required to define a main function.

From this it looks like g++ happens to allow a program (presumably as the "freestanding" clause) without a main function.

Then from 3.6.1/3:

The function main shall not be used (3.2) within a program. The linkage (3.5) of main is implementation defined. A program that declares main to be inline or static is illformed. The name main is not otherwise reserved.

So here we learn that it's perfectly fine to have an integer variable named main.

Finally if you're wondering why the output is printed, the initialization of the int main uses the comma operator to execute cout at static init and then provide an actual integral value to do the initialization.


gcc 4.8.1 generates the following x86 assembly:

.LC0:
    .string "C++ is excellent!\n"
    subq    $8, %rsp    #,
    movl    std::__ioinit, %edi #,
    call    std::ios_base::Init::Init() #
    movl    $__dso_handle, %edx #,
    movl    std::__ioinit, %esi #,
    movl    std::ios_base::Init::~Init(), %edi  #,
    call    __cxa_atexit    #
    movl    $.LC0, %esi #,
    movl    std::cout, %edi #,
    call    std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)   #
    movl    $195, main(%rip)    #, main
    addq    $8, %rsp    #,
    ret
main:
    .zero   4

Note that cout is called during initialization, not in the main function!

.zero 4 declares 4 (0-initialized) bytes starting at location main, where main is the name of the variable[!].

The main symbol is interpreted as the start of the program. The behavior depends on the platform.


That is an ill-formed program. It crashes on my test environment, cygwin64/g++ 4.9.3.

From the standard:

3.6.1 Main function [basic.start.main]

1 A program shall contain a global function called main, which is the designated start of the program.


The reason I believe this works is that the compiler does not know it is compiling the main() function so it compiles a global integer with assignment side-effects.

The object format that this translation-unit is compiled into is not capable of differentiating between a function symbol and a variable symbol.

So the linker happily links to the (variable) main symbol and treats it like a function call. But not until the runtime system has run the global variable initialization code.

When I ran the sample it printed out but then it caused a seg-fault. I assume that's when the runtime system tried to execute an int variable as if it were a function.


I've tried this on a Win7 64bit OS using VS2013 and it compiles correctly but when I try to build the application I get this message from the output window.

1>------ Build started: Project: tempTest, Configuration: Debug Win32 ------
1>LINK : fatal error LNK1561: entry point must be defined
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========

You are doing tricky work here. As main( somehow) could declared to be integer. You used list operator to print message & then assign 195 to it. As said by someone below, that it doesn't comfort with C++, is true. But as compiler didn't find any user defined name, main, it didn't complaint. Remember main is not system defined function, its user defined function & thing from which program starts executing is Main Module, not main(), specifically. Again main() is called by startup function which is executed by loader intentionally. Then all of your variables are initialized, & while initializing it output like that. That's it. Program without main() is ok, but not standard.

참고URL : https://stackoverflow.com/questions/32851184/how-can-a-program-with-a-global-variable-called-main-instead-of-a-main-function

반응형