본문 바로가기

CS/컴퓨터 시스템

Chap 3 - 2. 부분 컴파일

부분 컴파일이란?

= 여러 개의 파일들을 각각 컴파일하고 링커를 통해 합쳐져서 실행 파일로 만드는데,

    여러 개의 파일들을 각각 컴파일하는 것을 부분 컴파일이라고 한다.

- 부분 컴파일을 하게 되면 symbol table이 만들어지게 된다.

    심볼 테이블은 각각의 변수들은 어떤 것을 사용하는지에 대해 정리되어 있다.

    그것들을 이용해서 컴파일을 한 오브젝트 파일들 몇개를 서로 링크하여 하나의 실행 파일로 만드는 것이다.

-변수나 함수는 거의 비슷한 개념이다. ⇒ symbol이라고 함 (변수나 함수 등의 이름을 말한다.)

int a = 1; // 변수 선언 

void func(int a) // 함수 선언
{
	a++;
}

-변수 선언을 할 때는 변수에 해당되는 메모리의 주소를 가리키고 있는 것이 'a' 라고 할 수 있다.

  마찬가지로 함수인 func를 선언하면 얼만큼 차지하고 있는지 미리 메모리에 할당한다.

     => 즉, 메모리 크기의 차이이지 그 과정은 함수나 변수나 같다고 보면 된다.

 

 

static과 extern선언

static과 extern의 사용 예시는 다음과 같다. 

int a = 3, b = 4;
static int c = 5; 
extern int func(int, int);

void main()
{
	int i = 1, j = 2;
	c = c + func(i, j);
	printf("%d \\n", c);
}

int c = 6;
extern int a, b;
int func(int x, int y)
{
	a++;
	b++;
	return (x+y+a+b+c);
}

 

 

(1) static = 다른 오브젝트 파일에 영향을 주지 않겠다!

[static int c = 5;] 이 부분을 보면 

static은 범위를 제약한다 (ex) 해당 파일인 "one.c"에서만 사용하겠다)

ex) 만약 static을 사용하지 않았는데 "one.c"에서도 int c가 있고 "two.c"파일에서도 int c가 있다면

     각각 파일을 컴파일하여 오브젝트 파일을 만들었을 때 에러가 나지 않지만 이 두 개의 파일을 서로 링크 시켰을 때       에러가 난다. c라는 변수는 전체 실행 파일에서 하나만 있어야 하는데 두 개가 있다고 에러가 날 것이다.

- 이런 에러를 방지하기 위해서 변수명을 변경하는 방법이 있는데 이는 변수가 여러 곳에 사용하고 있기 때문에 수정하기 귀찮다.

- 이런 위험성들을 피하기 위해서 “static”이라는 선언을 하는 것이다. static으로 선언된다면( ex) static int c = 5;)

   오브젝트 파일을 만들 때만 사용을 하게 되고 실제로 다른 파일들과 링킹할 때는 이 c라는 변수를 사용하지 않겠다고 하는 것이다. ⇒ 다른 오브젝트 파일에 영향을 주지 않겠다.

 

(2) extern = 다른 파일에도 사용하겠다!

- 함수 extern = 해당 파일에서 함수가 정의되어 있지 않아도 가져다가 쓸 수 있다. 다른 파일에서 이 함수가 정의되어 있다는 것을 의미이다. 

- 변수 extern = 이 파일에서 해당 변수를 정의하는 것은 아니지만 다른 파일에 정의되어 있는 변수를 가져다가 쓸 것이라는 것을 의미이다. 

*만약 다른 파일에서 발견되지 않는다면 링크 과정에서 에러가 날 것이다.

 

심볼 테이블

오브젝트 파일은 심볼 테이블을 갖게 된다.

심볼 테이블에서 심볼은 변수 혹은 함수의 이름을 말한다.

이름 - 주소 - 타입이 명시되어 있다.
 z - (빈칸 혹은 의미 없는 값) - extern

만약 어떤 파일에 extern int z;가 있는 상태에서 부분 컴파일을 통해 file.o를 만들었다면 주소를 모르기 때문에

"load r1, XXX" 로 빈칸으로 둘 것이다.

 

하지만 링킹을 하면 보류된 주소값들이 채워지게 된다.

소스파일을 컴파일할 때 extern으로 선언했다면 ‘변수의 위치가 있으니 나중에 그것을 사용할거야’라는 식으로 알고 성공적으로 컴파일 한다.

 

만약, 주소값이 없으면 오브젝트 파일에서 실행파일을 만드는 과정에서 에러가 난다. 이런 것은 심볼 테이블을 사용해서 위치만 참조하고 있다가 (실제로 심볼 테이블의 위치 공간이 표현이 안되고 있다가) 실행파일이 만들어질 때 심볼 테이블에 채워지는 것으로 해결될 수 있다. 

 

[source file] ⇒ “컴파일” >[ object file ] > “링크” >[executable file]

 

예시로 살펴보자.

a.c 파일

int x;
x = 0;

b.c

extern int x;
x++;

b.c 파일에서 extern int x;라면 해당 파일이 컴파일 되었을 때 x가 어느 위치에 있는지 알 수는 없다.

하지만 int이며 x++을 연산한다는 것은 알고 있다. 

즉, 심볼의 위치는 불명확, 타입만 파악

c.c

extern int x;
x--;
  • (1) 부분 컴파일컴파일 결과 = object file (”.o” or “.obj”) 이때, 심볼의 위치는 불명확하다. 
  • (2) 링킹 (오브젝트 파일들을 링킹한다)심볼의 위치 명확
  • (3) 링크 결과 = “executable file” (”a.out” or “.exe”)
    • 링킹을 하면 a.c파일이 각각 b.c파일과 c.c파일에게 x의 위치가 어디인지 알려줄 것이다.

⇒ 즉, extern을 통해 한 군데서 정의하고 여러 곳에서 사용하게 된다는 것이다.

그리고 컴파일을 하면 extern으로 선언된 부분은 심볼 테이블에 정확하게 만들지 않는 부분인데 실제로 4개의 파일이 링킹이 되면 심볼의 위치가 정확하게 만들어진다.


 

 

어떻게 extern을 활용하여 효율적으로 사용할 수 있을까

예시로 살펴보자 

 

a.c 파일

int x;
x = 0;

b.c 파일

extern int x;
x++;

c.c 파일

extern int x;
x--;

다른 파일에 있는 x를 내 파일 에서 사용할거야라고 선언

 

만약 int x라고 선언된 것을 char x로 바꾸고 싶다면 모든 파일에 있는 int x를 바꾸는 법

(1) extern문구를 선언한 헤더파일은 만든다.

 

[ x.h ]

extern int x; // x.h 헤더 파일을 만들고 

 

(2) 다른 파일에 헤더 파일을 삽입한다. (=헤더 파일을 참조할 것이다)

#include "x.h" // 다른 파일에 헤더 파일을 삽입한다. 

 

(3) cpp가 헤더 파일에 있는 extern 문장을 “#include “x.h” 부분에 대체시킨다.

    - c preprocesspr (전처리기) ⇒ include을 통해 헤더 파일을 가지고 온다.

    - 장점 = 수정시 한 번에 가능 , 효율적 프로그래밍 가능 , 에러 줄이기 가능

만약 int x에서 char x로 바꾸고 싶다면 a.c파일과 x.h파일만 바꾸면 된다.

(라이브러리 방식도 이러한 방식)