처음에 gcc를 사용하게 되면, visual studio의 경우에는 헤더파일을 단순히 분류만 잘하면 되었었는 데, gcc는 그런 gui가 없기에 당황하게 될 수 있다. Show 나도 처음 gcc를 사용했을 때, 당황한 경험이 있고, 블로그에다가 다른 블로그 포스팅한 것을 스크랩했었다. 그런데, 이제 어떻게 헤더파일을 포함해서 컴파일 하는 지 제대로 배워서 다시 포스팅하고자 한다.. MakefileMakefile은 그냥 make 커맨드를 치면 빌드를 다 해주는 파일이다. 헤더 파일을 만들어야 하는 코드를 짜는 경우에는 Makefile을 만드는 것이 필수이다. 물론 그러지 않으면 동작하지 않는 것은 아니지만, 매번 위 과정을 해주는 것은 매우 번거롭고, 코드를 공유했을 때도 Makefile이 없으면 사용자가 어떤 코드를 어떻게 컴파일 해야하는 지 모르니 Makefile을 꼭 만들어 주도록 하자. git clone을 했을 때, Makefile을 만나게 된다면, make를 한번 쳐주자. 그러면 우리가 원하는 바이너리들이 모두 완성될 것이다. 그런 Makefile을 어떻게 만드느냐? 아래 예시를 보자.
위 Makefile에서 all: 은 그냥 make를 했을 때 동작하는 것이다.
그래서 그런데, 바로 아래에 sum_test: sum.o main.o 이런식으로 텍스트가 있는 것을 확인할 수 있다.이는 무슨 뜻이냐면, 만약에 그런데 이때또,
이렇게 make파일은 동작하게 된다.제일처음에 말했던 마지막으로 이런 것이 바로 그래서 어떻게 헤더파일 포함시켜서 컴파일하는 데?
위처럼 따라하게 되면 헤더파일을 포함시켜서 컴파일 시킬 수 있다. 헤더파일은 사실 컴파일하거나 하지 않고, 각각 c코드들을 gcc -c 옵션으로 컴파일 시키고 난 다음, 마지막으로 한꺼번에 컴파일 시켜주면 된다. gcc 란?원래는 GNU C Compiler를 의미 했지만 1999년부터 GNU Compiler Collection을 의미한다. gcc가 실행시키는 프로그램
단계별 처리 방법1) 전처리 단계소스 파일(file.c)에 gcc를 동작시키면 가장 먼저 전처리기인 cpp가 동작한다. cpp는 소스 파일의 #include와 #define과 같은 #으로 시작되는 전처리기 부분을 처리 한다. 즉, 필요한 헤더 파일을 삽입하고 실행 문장의 매크로를 상수로 변환한다. 소스 파일 file.c가 전처리기를 거치면 file.i라는 이름의 파일이 생성되지만 디스크에는 저장되지 않는다. 2) 컴파일 단계컴파일러가 전처리된 파일(file.i)로부터 어셈블리어로 된 파일(file.s)을 생성한다. 그런데 일반적으로 다음 단계인 어셈블 단계를 바로 실행하므로 file.s 파일은 디스크에 저장되지 않는다. 3) 어셈블 단계어셈블리어로 된 파일(file.s)을 기계가 직접 이해할 수 있는 기계어로 된 오브젝트 파일(file.o)로 변환한다. 4) 링크 단계오브젝트 파일(file.o)은 printf, scanf와 같은 라이브러리 함수에 해당하는 코드가 없기 때문에 실행될 수 없다. 또한 여러 파일로 이루어진 프로그램의 경우에도 파일 간에 연결이 이루어지지 않아 실행될 수 없는데, 이러한 라이브러리 함수와 오브젝트파일들을 연결해 실행 파일을 생성하는 단계가 링크 단계이다. 5) 파일 확장자에 따른 처리 방법gcc는 파일 확장자에 따라 다음과 같이 처리 방법을 달리하는데, 한 가지만 설명하면 확장자가 .c인 경우 gcc로 전처리기, 컴파일, 어셈블, 링크 과정을 거쳐야 실행 파일이 완성된다는 것이다.
2. gcc 실행하기gcc를 이용해 컴파일 하는 방법은 다음과 같습니다.
gcc를 이용해 컴파일에 성공하면 a.out이라는 파일이 생성된 것을 확인할 수 있는데 여기서 그냥 명령어로 a.out을 입력하면 명령어를 발견할 수 없다는 오류 메시지가 나온다. 이는 a.out가 저장된 디렉토리를 path로 설정하지 않았기 때문이다. path로 설정되지 않는 디렉토리에 있는 명령어는 디렉토리 위치를 지정해주지 않는 한 실행되지 않는다. 그러므로 a.out를 실행하려면 다음과 같이 해야 한다.
즉, 프로그램 이름 앞에 디렉토리 위치 정보인 ./를 추가해야 현재 디렉토리에 있는 명령어가 실행되는데, 여기서 마침표(.)는 현재 디렉토리를 의미하고, 슬래시(/)는 디렉토리를 구분하는 문자다. 3. gcc 옵션
1) 자주 사용되는 옵션-o 옵션C 소스 코드를 컴파일 할 때 생성되는 출력 파일 이름을 지정하는 옵션으로 사용법은 다음과 같다.
$ gcc -o file file.c 또는 $ gcc file.c -o file을 입력한다. 그럼 출력파일이 file이라는 이름으로 생성이 된다. 이를 실행하려면 $ ./file 이라고 실행하면 된다. -o 옵션을 생략하고 컴파일을 하면 실행 파일 이름은 a.out가 된다. 그런데 이럴 경우 두 가지 다른 소스를 차례로 컴파일할 때 먼저 생성된 실행 파일 a.out를 나중에 생성된 a.out가 경고없이 덮어쓰므로 주의해야 한다. -E 옵션컴파일의 첫 단계인 전처리까지만 실행한 결과를 화면에 출력한다.
$ gcc -E file.c로 컴파일을 하면 매우 방대한 내용이 소스파일 위에 붙는 것을 확인할 수 있다. 이처럼 -E 옵션만 주면 전처리된 결과가 화면에만 출력되고 파일로 저장되지는 않는다. 그러므로 파일을 저장하려면 -o 옵션을 함께 주어야 하고, 그러면 file.i라는 전처리된 파일이 디스크에 저장된다. -c 옵션-c 옵션은 전처리, 컴파일, 어셈블까지 실행하여 오브젝트 파일(.o)을 생성한다.
file.c를 -c 옵션을 사용해 컴파일 하면 file.o라는 오브젝트 파일이 생성된다. 즉, -c 옵션을 주어 ‘소스파일이름.c'를 컴파일하면 오브젝트 파일 이름은 ’소스파일이름.o'가 된다.
그리고 이 오브젝트 파일을 이용해 실행 파일을 생성하려면 다음과 같이 gcc를 이용하면 된다.
그럼 a.out이라는 기본 출력 파일이 생성된다. 여기서 file 이라는 이름의 실행파일을 생성하려면 -o 옵션만 주면 된다.
-c 옵션은 하나의 프로그램을 여러 파일로 분리해 작성한 다음, 함께 컴파일 하는 분리 컴파일 시 많이 사용된다. 예를 들어 main.c와 hi.c 두 개의 소스로 구성된 프로그램을 살펴보자.
이 둘을 함께 컴파일 하는 방법은 다음과 같으며, 이를 분리 컴파일이라 한다.
만약 다음과 같이 파일별로 컴파일 하면 컴파일 오류가 발생하는데, main.c 파일에 대한 오류는 호출하는 함수 hi라는 함수가 정의되지 않았기 때문이고, hi.c 파일에대한 오류는 main 함수가 없기 때문이다.
그러나 다음과 같이 각 파일별로 오브젝트 파일을 만들고 나중에 링크하는 것은 가능하다.
이런 식으로 분리 컴파일을 하게 되면 만일 hi.c 파일이 수정되면 main.c와 hi.c를 모두 컴파일할 필요 없이 hi.c 파일에 대한 오브젝트 파일만을 생성하고 링크하면 원하는 실행 파일이 생성된다.
-I 옵션-I 옵션은 C 소스가 표준 디렉토리가 아닌 위치에 있는 헤더 파일을 가질 때 그 디렉토리 위치를 지정해준다.
먼저 myheader.h 파일이 age.c 파일이 있는 디렉토리의 하위 디렉토리인 mydir에 있다고 하자.
age.c 파일을 컴파일하면 다음과 같이 myheader.h 파일이 없다는 메시지의 컴파일 오류가 발생한다.
myheader.h 파일이 표준 디렉토리에 없기 때문에 오류가 발생하는 것이다. 이와 같이 표준 디렉토리가 아닌 디렉토리에 있는 헤더 파일을 이용하려면 -I 옵션으로 디렉토리 위치를 지정해야 한다. 즉, myheader.h 파일이 있는 mydir 디렉토리를 다음과 같이 지정해 컴파일하면 성공한다.
2) 라이브러리 지정 옵션라이브러리란?자주 사용되는 유용한 함수에 대한 오브젝트 파일을 모아둔 것이 라이브러리로, 사실 라이브러리에는 함수 목록(index)도 포함된다. 시스템에서 제공하는 라이브러리는 /usr/lib 디렉토리에 있으니 확인해보면 된다. 아주 많은 라이브러리가 있는데, 이름은 lib로 시작하고 ar 명령어에 의해 생성되므로 확장자는 .a다. 이 중에서 libc.a는 표준 라이브러리고 libm.a는 수치 연산 라이브러리다. ar 명령어를 이용하면 libc.a가 어떠한 오브젝트 파일로 이루어졌는지 알 수 있다. ar 명령어의 t 옵션은 .a 파일의 내용을 표시해주는 것이다.
라이브러리를 직접 만들어 보기 위해 우선 다음과 같이 plus.c와 minus.c 두 개의 파일을 mylib 디렉토리에 만들자.
plus.c와 minus.c 파일을 생성했으면 이들 파일에 대한 오브젝트 파일인 plus.o와 minus.o를 다음과 같이 생성한다.
그러면 라이브러리 파일을 생성할 준비가 끝났다. 다음과 같이 ar 명령어를 이용하면 plus.o와 minus.o에 대한 libmy.a가 생성되는데, r옵션은 .a 파일을 생성한다.
그리고 라이브러리 파일에 목록을 추가해야 하는데, 다음과 같이 s 옵션을 주어 ar 명령어를 실행하면 라이브러리 파일이 생성된다.
-l 옵션-l 옵션은 표준 라이브러리가 아닌 라이브러리를 사용하고자 할 때 그 라이브러리를 지정해준다.
그럼 -l 옵션의 예를 살펴보면 다음과 같다.
수치 연산 라이브러리의 이름은 libm.a다. 그러므로 이 라이브러리 이름을 -l 옵션 뒤에 지정해야 하는데, lib와 .a를 제외한 m만 쓴다.
이제 앞서 직접 만든 libmy.a는 표준 라이브러리가 아니므로 -l 옵션을 주어 지정해야 한다.
오류가 발생하는 이유는 링커인 ld가 라이브러리를 찾을 때 /lib, /usr/lib와 같이 정해진 디렉토리만 찾기 때문이다. libmy.a 라이브러리는 현재 작업 디렉토리의 하위 디렉토리인 mylib에 있으므로 링커가 찾지 못하는데, 이는 -L 옵션을 사용해 해결할 수 있다. -L 옵션-L 옵션은 사용할 라이브러리의 위치를 지정해주므로 사용자가 라이브러리 파일을 직접 만들어 사용하거나 새 라이브러리를 내려 받아 사용할 때 이용된다.
컴파일 오류가 발생했던 test2.c를 다음과 같이 -L 옵션을 주어 libmy.a 라이브러리가 있는 디렉토리인 mylib를 지정하면 성공적으로 컴파일이 된다.
3) 디버깅 관련 옵션
-g 옵션에는 실행파일에 삽입될 디버깅 정보의 양에 따라 -g1, -g2, -g3와 같이 세 가지 단계가 있는데, 숫자 없이 -g 옵션을 주면 기본적으로 -g2의 디버깅 정보가 삽입된다. -g 옵션의 단계
-g 옵션 없이 컴파일 할때와 -g 옵션을 주었을 때 생성된 실행 파일의 크기가 훨씬 커지는데, 그 이유는 디버깅 정보가 추가되었기 때문이다. 4) 최적화 옵션성능을 개선시키기 위해 코드를 최적화하면 불필요하거나 비효율적인 계산 과정이 효율적 게산 과정으로 대체되어 코드의 크기와 실행 시간을 줄일 수 있다. 대신, 컴파일 시간이 늘고 컴파일 과정에서 메모리 사용량이 늘어나는 단점이 있다.
-O 옵션은 -ON(숫자)을 써줌으로써 최적화 단계를 구분할 수 있는데, N 값은 gcc 버전마다 차이가 나며 값이 커질수록 더욱 최적화된 코드가 나온다. 일반적으로, -O1, -O2를 많이 사용하며, -O1, -O2, -O3에 의한 최적화 내용은 다음과 같다.
|