[1-3] 함수와 엔트리 포인트

프로그램에서 함수는 한 문장이 쓰일 곳에 여러 문장을 쓸 수 있게 만든 복합문입니다. 코드 블록(block)이라고도 부른다.

함수 원형은 다음과 같습니다.

반환값 함수명(매개변수) {
    // 본체(처리할 내용)
}

그리고 다음과 같이 사용합니다. 함수 사용을 ‘함수를 호출한다’고 합니다.

반환값을 받을 변수 = 함수명(인수);

반환값을 받을 변수가 없는 경우에는 다음과 같이 씁니다.

함수명(인수);

함수에 전달할 인수가 없는 경우에는 다음과 같이 씁니다.

함수명();

다음은 처음 만들었던 예제 코드입니다. 여기서 함수를 찾아보세요!

#include <stdio.h>

int main(void) {
	printf("hello world);
	return 0;
}

그렇습니다. main()과 printf()가 함수입니다. 위 코드 전체는 main() 함수의 정의이라고 할 수 볼 수 있습니다. main() 함수에서 printf() 함수를 사용했습니다. 이처럼 함수 안에서 함수를 호출할 수 있습니다.

둘다 함수지만 사실 printf()와 main()은 많이 다릅니다. printf() 원형은 다음과 같이 호출합니다.

int printf(const char *format, ...);

어? 뭔가 이상합니다. 분명히 반환값을 받지 않고 printf(“hello world”)라고만 호출했었는데요? 위에서도 말씀드렸지만 반환하는 값을 받을 수도 안 받을 수도 있습니다. printf() 함수는 출력에 성공하면 출력한 문자 숫자를, 실패하면 음수를 반환하는데요, 여기서는 반환값을 받지 않은 것뿐입니다.

그럼 main() 함수도 다음과 같이 호출할 수 있을까요?

main();

아쉽게도 main() 함수는 위와 같은 방법으로 우리가 직접 호출하지 못합니다. main() 함수는 C 언어에서 굉장히 특별하거든요.

OS만 호출할 수 있고 무조건 해당 애플리케이션의 맨처음 호출됩니다. 이런 함수를 엔트리 포인트라고 합니다.

main() 함수에 관한 관우의 기본적인 설명은 여기까지입니다. 더 깊은 이야기가 궁금하시다면 제갈량의 동남풍을 참고하세요.

제갈량의 동남풍

  1. 누가 main() 함수를 호출했나?
  2. main() 함수는 어디에?

1. [동남품] 누가 main() 함수를 호출했나?

엔트리 포인트는 프로그램이 처음 시작되는 함수를 말합니다. 언어마다 그 이름이 다른데 C 언어에서는 main() 함수입니다.

우리가 만든 프로그램에서 main을 myMain으로 이름을 바꾼 후 실행합니다. submit 버튼을 눌러도 좋지만 이제부터는 ctlr + enter 키를 누릅시다. 그럼 자동으로 저장한 후 컴파일하고 실행됩니다.

실행에러

이런! 출력창에 컴파일 에러(Compilation error)라는 메시지가 출력되었습니다. main()을 찾지 못했다는 내용이 보입니다. myMain()을 main()으로 바꾸어 실행하면 제대로 컴파일되고 실행에 성공할 겁니다.

이유는 간단합니다.

OS가 우리가 만든 실행 파일을 호출하면, 제일 먼저 실행 파일의 헤더를 읽습니다. 그곳에서 실행에 필요한 정보를 얻은 후에 우리가 만든 프로그램의 엔트리 포인트인 main() 함수를 호출하게 되는데, main() 함수를 찾지 못해서 생긴 겁니다.

기억하세요! C 언어에서 main() 함수의 이름을 바꾸면 프로그램이 실행되지 않습니다. 그러니 절대로 main() 함수의 이름을 바꾸지 마세요!

2. [동남풍] main()은 어디에?

유닉스 계열의 실행 파일은 ELF 포맷을 사용합니다.

실행에러

C 언어는 네이티브 언어이기 때문에 실행 파일은 기계어로 되어 있습니다. 따라서 OS가 ELF (Executable and Linkable Format) 파일을 읽어 (VM이나 인터프리터 없이) 곧바로 실행할 수 있습니다.

OS가 파일을 메모리에 로드할 때 당연히 파일의 맨 앞부터 읽게 됩니다. 그래서 파일의 맨 앞에는 자신의 구조를 설명하는 ELF 헤더가 있습니다. 그곳에서 자신의 구조에 대해 여러 정보(OS 정보, 섹션 위치, 명령어 집합 등)를 OS에 전달합니다. 그다음 읽어야 하는 곳을 (오프셋이) 0x18인 곳(e_entry)에 적어둡니다. 즉,

  1. OS가 ELF 파일 헤더를 읽습니다.
  2. CPU의 PC 레지스터(프로그램 카운터 레지스터)를 e_entry에 쓰인 주소를 향합니다(PC가 향하는 곳이 현재 CPU가 수행하는 곳입니다). 바로 _start() 함수가 있는 곳입니다.
  3. _start()함수는 우리가 만든 프로그램이 실행될 수 있게 몇 가지 작업을 합니다(스택을 초기화 등)
  4. 그러고 나서 우리가 만든 프로그램이 본격적으로 실행하고자 프로그램 엔트리 포인트, 즉 main() 함수를 호출합니다.

maim() 함수의 이름을 바꿔 실행했을 때 출력된 에러 메시지인 ‘in function _start’이라는 문구의 비밀이 풀렸습니다. ELF 파일의 시작함수인 _start에서 생긴 에러라는 뜻이군요.

이 내용은 여러분께 너무 깊은 내용입니다. 하지만 C 언어가 저수준 언어이고, 저수준 프로그래밍에 사용되므로 C 언어 프로그래머가 된다면 만나게 될 내용입니다. 하지만 간단히 언어 입문으로 이 글을 보고 계신다면 이 내용을 전혀 알 필요가 없으니 그냥 잊고 넘어가세요.

하지만 깊은 내용이 궁금한 분들을 위해 가끔 제가 나타나 설명을 보탤 겁니다. 이상 제갈량이었습니다.