#include <stdio.h>
#define decode(s,t,u,m,p,e,d) m##s##u##t
#define begin decode(a,n,i,m,a,t,e)
int begin()
{
printf("Ha HA see how it is?? ");
}
이것은 간접적으로 호출 main
합니까? 어떻게?
#include <stdio.h>
#define decode(s,t,u,m,p,e,d) m##s##u##t
#define begin decode(a,n,i,m,a,t,e)
int begin()
{
printf("Ha HA see how it is?? ");
}
이것은 간접적으로 호출 main
합니까? 어떻게?
답변:
C 언어는 독립형 및 호스트 형의 두 가지 범주로 실행 환경을 정의합니다 . 두 실행 환경 모두에서 프로그램 시작을 위해 환경에 의해 함수가 호출됩니다.
A의 자립 환경 프로그램 기동 기능 구현 중에 정의 될 수 호스팅 환경이 있어야한다 main
. 정의 된 환경에서 프로그램 시작 기능 없이는 C 프로그램을 실행할 수 없습니다.
귀하의 경우 main
에는 전 처리기 정의에 의해 숨겨져 있습니다. begin()
에 확장됩니다 decode(a,n,i,m,a,t,e)
더 확장 될 것이다 main
.
int begin() -> int decode(a,n,i,m,a,t,e)() -> int m##a##i##n() -> int main()
decode(s,t,u,m,p,e,d)
7 개의 매개 변수가있는 매개 변수화 된 매크로입니다. 이 매크로의 대체 목록은 m##s##u##t
입니다. m, s, u
및 t
(4)이다 일 , 1 일 , 3 번째 및 2 차 교체리스트에 사용되는 파라미터.
s, t, u, m, p, e, d
1 2 3 4 5 6 7
나머지는 소용이 없습니다 ( 단지 난독 화하기 위해 ). 에 전달 인자 decode
"이다 , N , I는 , m이 때문에, 식별자, A, t, E는" 과는 인수로 대체 하고 각각.m, s, u
t
m, a, i
n
m --> m
s --> a
u --> i
t --> n
_start()
. 또는 더 낮은 수준에서 부팅 후 IP가 설정된 주소와 프로그램의 시작을 정렬 할 수 있습니다. main()
C 표준 라이브러리 입니다. C 자체는 이에 제한을 두지 않습니다.
decode(a,n,i,m,a,t,e)
될지 설명해 주 m##a##i##n
시겠습니까? 문자를 대체합니까? decode
함수 문서에 대한 링크를 제공 할 수 있습니까 ? 감사.
begin
는 이전에 정의 된 것으로 대체되도록 decode(a,n,i,m,a,t,e)
정의됩니다. 이 함수는 인수 s,t,u,m,p,e,d
를 받아이 형식으로 연결합니다 m##s##u##t
( ##
연결을 의미합니다). 즉, p, e 및 d의 값을 무시합니다. 당신이 "전화"로 decode
S = A, t = N, U 난을 =와, m = m 효과적으로 대체 begin
와 함께 main
.
사용 시도 gcc -E source.c
와 함께, 출력단을 :
int main()
{
printf("Ha HA see how it is?? ");
}
따라서 main()
함수는 실제로 전처리기에 의해 생성됩니다.
문제의 프로그램은 수행 전화를 main()
인해 매크로 확장에, 그러나 당신의 가정은 결함이 - 그것은 하지 않습니다 호출 할 필요가 main()
전혀!
엄밀히 말하면, C 프로그램을 가지고 main
심볼 없이 컴파일 할 수 있습니다 . main
는 c library
자체 초기화를 마친 후 점프 할 것으로 예상되는 것입니다 . 일반적으로 main
libc 기호에서 _start
. main없이 어셈블리를 실행하는 매우 유효한 프로그램을 항상 가질 수 있습니다. 이것 좀보세요 :
/* This must be compiled with the flag -nostdlib because otherwise the
* linker will complain about multiple definitions of the symbol _start
* (one here and one in glibc) and a missing reference to symbol main
* (that the libc expects to be linked against).
*/
void
_start ()
{
/* calling the write system call, with the arguments in this order:
* 1. the stdout file descriptor
* 2. the buffer we want to print (Here it's just a string literal).
* 3. the amount of bytes we want to write.
*/
asm ("int $0x80"::"a"(4), "b"(1), "c"("Hello world!\n"), "d"(13));
asm ("int $0x80"::"a"(1), "b"(0)); /* calling exit syscall, with the argument to be 0 */
}
위를로 컴파일하고 인라인 어셈블리에서 시스템 호출 (인터럽트)을 실행하여 화면에 gcc -nostdlib without_main.c
인쇄 Hello World!
되는 것을 확인하십시오 .
이 특정 문제에 대한 자세한 내용은 ksplice 블로그를 확인하십시오.
또 다른 흥미로운 문제 main
는 C 함수에 해당 하는 기호 없이 컴파일되는 프로그램을 가질 수도 있다는 것 입니다. 예를 들어 다음을 매우 유효한 C 프로그램으로 사용할 수 있으며 경고 수준이 올라갈 때만 컴파일러가 우는 소리를냅니다.
/* These values are extracted from the decimal representation of the instructions
* of a hello world program written in asm, that gdb provides.
*/
const int main[] = {
-443987883, 440, 113408, -1922629632,
4149, 899584, 84869120, 15544,
266023168, 1818576901, 1461743468, 1684828783,
-1017312735
};
배열의 값은 Hello World를 화면에 인쇄하는 데 필요한 지침에 해당하는 바이트입니다. 이 특정 프로그램의 작동 방식에 대한 자세한 설명은이 블로그 게시물을 참조 하십시오.
이 프로그램에 대해 마지막으로 알려 드리고 싶습니다. C 언어 사양에 따라 유효한 C 프로그램으로 등록되어 있는지는 모르겠지만, 사양 자체를 위반하더라도 컴파일하고 실행하는 것은 확실히 가능합니다.
_start
정의 된 표준 의 일부 이름입니까 , 아니면 구현에 특정한 것입니까? 확실히 "배열로서의 메인"은 아키텍처에 따라 다릅니다. 또한 보안 제한으로 인해 런타임에 "배열로서의 기본"트릭이 실패하는 것은 부당하지 않을 것입니다 (정규자를 사용하지 않고 const
여전히 많은 시스템에서이를 허용하는 경우 더 많음).
_start
는 AMD64 psABI가에 대한 참조 포함 불구하고, ELF 표준에없는 _start
에서 3.4 프로세스 초기화 . 공식적으로 ELF e_entry
는 ELF 헤더 의 주소 만 알고 _start
있으며 구현에서 선택한 이름 일뿐입니다.
const
이진 실행 파일의 기호 이름은 main
. 그 이상도 이하도 아닌. const
실행 시간에 아무 의미가없는 C 구조입니다.
누군가 마술사처럼 행동하려고합니다. 그는 우리를 속일 수 있다고 생각합니다. 그러나 우리 모두는 c 프로그램 실행이 main()
.
는 int begin()
대체 될 decode(a,n,i,m,a,t,e)
전처리 단계 중 하나 개를 통과하여. 그런 다음 다시 decode(a,n,i,m,a,t,e)
m ## a ## i ## n으로 대체됩니다. 매크로 호출의 위치 연관과 마찬가지로 s
will의 값은 character a
입니다. 마찬가지로 u
'i' t
로 대체되고 'n'으로 대체됩니다. 그리고 그렇게 m##s##u##t
될 것입니다.main
관한 ##
매크로 팽창 기호는 전처리 연산자이며 토큰 붙여 넣기를 행한다. 매크로가 확장되면 각 '##'연산자의 양쪽에있는 두 개의 토큰이 단일 토큰으로 결합 된 다음 매크로 확장에서 '##'및 두 개의 원래 토큰을 대체합니다.
나를 믿지 않는다면 -E
플래그로 코드를 컴파일 할 수 있습니다 . 전처리 후 컴파일 과정이 중단되고 토큰 붙여 넣기 결과를 볼 수 있습니다.
gcc -E FILENAME.c
귀하의 예제에서는 컴파일러가 매크로로 대체하고 차례로 m ## s ## u ## t 표현식으로 대체되는 매크로 main()
이므로 함수가 실제로 존재 begin
합니다 decode
. 매크로 확장을 사용하면 from ##
이라는 단어에 도달하게 main
됩니다 decode
. 이것은 추적입니다.
begin --> decode(a,n,i,m,a,t,e) --> m##parameter1##parameter3##parameter2 ---> main
를 갖는 것은 단지 속임수 main()
이지만 main()
C 프로그래밍 언어에서는 프로그램의 입력 함수에 이름 을 사용할 필요가 없습니다. 운영 체제와 해당 도구 중 하나 인 링커에 따라 다릅니다.
Windows에서 항상 사용하지 않는 main()
하지만, 오히려 WinMain
나wWinMain
있지만, 사용할 수있는 main()
, 심지어 마이크로 소프트의 툴 체인과 함께 . Linux에서는 _start
.
언어 자체가 아니라 진입 점을 설정하는 운영 체제 도구로서의 링커에 달려 있습니다. 당신도 할 수 있습니다 우리 자신의 진입 점을 설정하고, 또한 실행 가능한 라이브러리를 만들 수 있습니다 !
main()
C 프로그래밍 언어 에 함수를 바인딩하는 첫 번째 답변을 칭찬 / 수정하기 위해 작성한 부분적인 답변 입니다.