섹션 3-2: 빌드단계

63. 소스코드에서 실행파일까지, C프로그램의 빌드 과정

  • 소스코드가 어떻게 기계어로 변화되는가
  • C 프로그램의 빌드과정
    • 빌드란 소스코드를 기계어 명령어로 변환하는 과정
    • 빌드는 전처리, 컴파일, 어셈블, 링크의 4단계로 나뉘어져 있음. 컴파일 + 링크의 2단계로 분류하기도 함.
  • C 프로그램의 빌드과정 2
    • 소스코드 -> 컴파일러 -> 어셈블리 코드 -> 어셈블러 -> 오브젝트 코드 -> 링커 -> 실행파일.
  • clang에서 4단계를 한번에 처리해 줬었음.
int adder(const int a, const int b)

64. .c와 .h 파일

  • .h
    • C 파일 : 함수정의, 전역변수, 매크로 포함, 실제 프로그램을 도는 로직
    • 헤더파일 : 함수선언, 매크로, extern 변수 포함, 여러 소스코드에서 공유하고 싶은 것들.
  • 메인함수에서 컴파일시 함수의 원형을 알아야 하는데, include된 헤더파일을 참조하여 함수의 원형을 파악한다.

65. 헤더파일이 필요한 이유

  • 반드시 필요하진 않지만 편의상 헤더를 활용하는게 좋음
    • 모든 소스를 한파일에 삽입시 관리가 매우매우 어렵다.
    • 동일한 함수를 여러 파일에서 사용시 중복문제 해결?
    • 헤더를 활용해서 함수 선언을 여러 파일과 공유 가능
  • 헤더파일의 존재로 정의없이 선언만 된 함수를 컴파일 가능 (링크 단계에서 문제 생기긴 함)

66. #include < >, " "의 차이

  • < >는 시스템 경로에서만 헤더파일을 검색
  • " "는 현재 작업중인 디렉터리에서 검색 후 시스템 경로를 검색

67. 빌드과정 : 전처리 단계

  • 전처리 단계
    • 전처리기가 담당
    • 주석제거 -> 매크로 복붙 -> 인클루드 파일 복붙 (예외적으로 컴파일 속도향상을 위해 .c파일을 인클루드 하기도 함)
  • 전처리 단계의 출력
    • 확장된 소스코드 : 트랜슬레이션 유닛

68. 트랜슬레이션 유닛 보는 법

  • 컴파일 도중에 -E 플래그를 넣으면 화면에 결과가 출력됨.

69. 빌드과정 : 컴파일 단계

  • 컴파일 단계
    • 트랜슬레이션 유닛을 입력으로 받아 어셈블리어코드 출력
  • 어셈블리어는 하드웨어와 아주 가깝지만 텍스트파일이어서 읽기 가능하긴 함.(기계는 모름)
  • 어셈블리어코드는 정의를 모르는 심볼을 사용가능.
    • 정의를 못찾을 경우 비워둠
    • 추후 링커가 구멍을 보완

70. 어셈블리어 보는 방법

  • 컴파일 -S 플래그 사용
  • 어셈블리어 코드가 나왔다는 의미
    • 이제부터 특정 플랫폼에 종속된다는 뜻
    • 플랫폼에 따른 자료형 크기를 반영하여 뱉어낸 코드임.

71. 빌드과정: 어셈블 단계

  • 어셈블 단계
    • 어셈블러 라는 프로그램이 담당
    • 어셈블리어 입력을 오브젝트 코드(기계어)로 출력
    • 아직 메꿔야 하는 구멍이 있음

72. 오브젝트 코드 보는법

  • 오브젝트 보는 법 : main.o
    • -c 플래그를 넣어 컴파일 한다.
    • 일반 텍스트 편집기에서는 깨짐 hex editor 등으로 볼 수 있음.

73. 빌드과정 : 링크단계

  • 링크 단계
    • 모든 오브젝트 코드를 입력으로 받아 실행파일을 출력함.
    • 어셈블리 코드의 레이블을 상응하는 메모리 주소로 점프하는 코드로 바꾼다.

74. 링크 단계가 분리되어 있는 이유

  • 수많은 .c파일의 구멍을 컴파일 단계에서 메꾸기 어려움
  • 여러 개의 .c파일에서 동일한 외부 함수를 사용할 경우 해당 함수 정의가 중복으로 들어가는 것도 막아야 함.
  • 하나의 .c파일만 변경되었을 때 모든 파일을 다시 컴파일해야됨?
  • 결론은 .c 파일 개별적으로 컴파일해서 obj파일로 저장해 두는 방법이 낫다.
    • obj 파일로 .exe 만드는 방법
    clang -std=c89 -W -Wall -pedantic-errors main.o adder.o

75. 라이브러리, 정적/동적 라이브러리와 링크

  • 라이브러리로도 빌드 가능
    • 빌드 결과를 라이브러리로도 가능
    • 라이브러리란 함수 등을 기계어로 변환하여 파일에 저장해 놓은 것. 추후에 링크해서 활용가능
    • 정적/동적 라이브러리가 있다.
  • 정적 라이브러리와 링크
    • 정적링킹이라 함.
    • 라이브러리의 기계어를 실행파일에 복사함
    • 파일의 크기가 커지고 메모리를 많이 차지하지만 실행속도가 빠르다는 장점 존재
  • 동적 라이브러리와 링크
    • 동적링킹
    • 실행파일에 여전히 구명을 남겨둠
    • 실행할 때 운영체제에 의해 링킹 일어남
    • 게임할 때 dll 오류가 뜰때
  • 동적 링킹의 장점
    • 실행파일 크기가 작다
    • 여러 실행파일이 동일한 라이브러리를 공유하여 메모리가 절약됨

76. 분할 컴파일과 전역변수

  • 여러개의 파일과 C 빌드
    • 굉장히 큰 프로젝트면 비슷한 기능별로 파일에 저장함.
    • 여러 개의 파일이 어떻게 컴파일되는지 알 필요 있음.
  • 분할 컴파일
    • 각 파일을 개별적으로 컴파일해서 obj파일 만듦
    • obj파일을 서로 링크해서 실행파일을 만듬

77. 다른 파일에 있는 전역변수 사용시 문제점

  • 왜 컴파일 오류가 나지?
    • 다른 파일에 정의된 전역변수는 알 수 없음.
    • 동일 이름의 전역변수를 중복정의하면 링커오류 뱉어냄
  • 다른 방법이 필요하다.
    • 컴파일러에게 특정 파일안의 전역변수를 가져다 쓴다고 말해줘야 한다.
    • 해당 변수를 비워 두고 컴파일

78. extern

  • extern 키워드
    • .h 파일에 extern int g_mob_count; 삽입하고,
      main.c에 .h를 인클루드 하거나 extern 선언을 직접해준다. (사실 둘은 동일함)
    • 다른 파일에 있는 전역변수에 접근하기 위해선 extern 키워드 사용한다.
  • extern 키워드의 사용 방법
    • c파일에 넣을시 해당 파일에서만 사용
    • h파일에 넣을시 누구라도 사용할 수 있게
  • 이렇게 해도 링커오류

79. 코드보기 : extern 키워드

80. 전역 변수의 문제, static 키워드

  • 전역 변수의 문제
    • 전역변수는 문제가 있음
    • extern 사용시 누구나 확인, 접근 가능하고
    • 값이 어디서 변경되는지 파악 어려움
    • 내 파일에서만 쓰기 위해선 static 키워드
  • static ignores extern
    • static 변수의 접근 범위는 해당 파일로 한정됨
    • 여전히 전역변수로 실행동안 공간을 차지하고는 있음
  • static의 또 다른 의미
    • static을 쓰면 개념상 전역변수처럼 활용된다.
    • 함수가 최초로 호출될때만 초기화되고, 이후에는 함수 내부의 전역변수로 작동함.
    • 접근제어자처럼 흉내내서 활용가능.
    • 함수도 static을 넣으면 외부접근을 막을 수 있음.

81. 코드보기 : static 키워드

82. .c와 .h 정리, 순환 헤더 인클루드

  1. 헤더 파일에는 선언만 들어간다.
  2. .c 파일에는 정의가 들어간다.
  • 순환 헤더 인클루드 방지
    • 인클루드 가드 활용한다.

83. 인클루드 가드 작동법

  • #ifndef 전처리기 지시문을 통해서 조건적으로 코드를 컴파일하라고 지시함.
  • #pragma once
    • 표준아니고, 최신 컴파일러에서만 적용됨
    • 포팅 생각한다면 인클루드가드 사용할 것

84. 인클루드 가드 예제

  • 요약은 생략

85. C 컴파일러의 종류와 특징

  • GCC (GNU Compiler Collection)
  • Visual C++
    • 윈도우 기반에서 주로 활용
  • Clang
    • LLVM 컴파일러 구조를 사용하는 컴파일러 프론트엔드
    • gcc 대신 Clang으로 옮겨가는 추세

86. 정리

  • 앞의 헤더를 살펴볼 것.
  • 여기까지 다룬 내용은 넘 많아서 한번에 정리가 쉽지 않음

좋은 웹페이지 즐겨찾기