[C++] GNU/GCC/G++ 컴파일러 설치 및 사용법

Eugene CHOI·2021년 10월 30일
2

C/C++

목록 보기
4/9
post-thumbnail

GNU 컴파일러란?

GNU 컴파일러란 GNU Project의 일환으로 제작 된 컴파일러 입니다.

GNU Project란 공개 소프트웨어 프로젝트로 개인이나 단체에 구속 되지 않는 자유의 의미에서 시작 된 프로젝트입니다.


GCC 컴파일러란?

GCC란 GNU Compiler Collection의 약자로 원래는 C언어만 지원하여 GNU C Compiler였다가 추후 타 언어로의 기능 확장으로 이름이 변경되었습니다.
따라서 이름은 GCC이지만, 실제로 C++ 언어도 컴파일이 가능합니다.

다만 C언어 확장자(.c)는 C언어로 컴파일하고, C++언어 확장자(.cc,.cpp, ...)는 C++언어로 각각 컴파일 하게 되고, default로 c언어 라이브러리가 링크되기 때문에 C++언어를 컴파일 하기 위해서는 다음과 같이 추가적인 인수(-lstdc++)를 사용하여야 합니다.

$ gcc -o "filename" "filename.cpp" -lstdc++

C언어와 C++언어가 완전 호환된다고 알고 있지만 조금은 다릅니다. 순수 C문법은 C++로 컴파일이 가능하나, 반대의 경우에는 C++에서 확장 된 C언어 문법이 있기 때문에(struct 문법과 같은) C++에서 C코드라 생각하고 짠 코드가 안 돌아가는 경우도 있습니다. 이와 같은 경우는 명확하게 하시는 것이 좋습니다.

G++ 컴파일러란?

G++(GNU C++ Compiler)은 C++ 언어를 위한 컴파일러입니다. 모던 C++의 기능을 제대로 사용하려면 G++ 컴파일러를 사용하는 것이 좋습니다.

최근엔 llvm-clang 컴파일러가 압도적인 컴파일 구조 개선으로 대세인 것 같습니다. 저는 대단한 프로젝트를 하는 것이 아니라 설치와 사용이 간편한 g++ 컴파일러를 사용하고 있습니다.

GCC 컴파일러와 다르게 기본적인 매크로 기능을 추가 제공하고 있습니다. 또한 .c 파일과 .cc 파일 모두 C++ 컴파일러로 컴파일한다는 차이점이 있습니다.


MinGW64(windows) Installation

저는 대부분의 개발 작업을 리눅스 환경에서 하는 것을 선호합니다. 다만 보고 자료를 만들 때는 어쩔 수 없이 윈도우에서 작업해야하기 때문에 비슷한 환경을 구성할 필요가 있습니다. 64bit용 윈도우 GNU 컴파일러를 쉽게 설치할 수 있는 MinGW64를 설치하는 법을 알아보겠습니다.

다음 링크로 접속하여 왼쪽 카테고리에서 Download를 선택합니다.
https://www.mingw-w64.org/

중간 즈음에 다음과 같은 항목을 클릭합니다.

그러면 다음의 SourceForge 링크로 접속할 수 있습니다.

링크로 이동하면 자동으로 설치됩니다.

설치 파일을 실행하면 다음과 같은 창이 뜨는데 Architecture에서 i68632bit 프로세서, x86_6464bit 프로세서 명령 집합을 의미합니다. 본인 컴퓨터의 환경에 따라서 선택하여 주시면 됩니다.
Threads는 Thread Model을 의미하고 posix로 선택, Exception은 예외 처리 모델을 의미하고 seh가 overhead가 없다고 알려져 있습니다.
저도 자세히는 모르기 때문에 대중적으로 사용하는 옵션을 사용하였습니다.

다음 설치로 넘어가기 전 경로를 복사해둡니다.

설치가 완료되고 나면 윈도우 설정 중 시스템 속성-> 고급 -> 환경 변수 로 이동합니다. (시작 검색 창에 시스템 속성을 검색하면 쉽게 접근할 수 있습니다.)

설치 중 복사해 놓았던 경로에 다음을 추가합니다.

\mingw64\bin

그러면 다음과 같은 경로가 완성됩니다.

C:\Program Files\mingw-w64\x86_64-8.1.0-posix-seh-rt_v6-rev0\mingw64\bin

이 경로를 유저의 환경변수 경로에 추가합니다. 위에 있는 항목 중 Path를 선택하고 편집을 클릭합니다.

새로 만들기를 추가한 후 조금 전 완성하였던 경로 문자열을 적고 확인을 눌러 환경 변수 편집을 완료합니다.

그리고 Windows Powershell을 연 다음 다음 명령을 입력하면 성공적으로 g++ 버전이 확인되는 모습을 확인할 수 있습니다. 여기까지 되었다면 완료입니다.

$ g++ --version


Build-Essential(Linux) Installation

우분투를 기준으로 리눅스에서 gcc와 g++을 설치하기 쉬운 방법은 build-essential 패키지를 설치하는 것입니다. gcc, g++, make와 같은 개발에 필요한 필수툴이 포함되어 있는 패키지입니다.
포함된 패키지의 자세한 설명은 다음을 참고하세요.

$ sudo apt update
$ sudo apt install build-essential

쓰면서 보니 새삼 리눅스가 또 편리하다는 것이 느껴지는군요.


Compile Process

소스 코드의 컴파일 과정은 다음과 같은 네 가지의 단계를 거치게 됩니다.
a.c라는 소스파일이 존재한다고 하였을 때, 다음 과정들을 거치면서 각각 다음의 파일로 변환됩니다. a.c->a.s->a.o->a.out

Preprocessor(a.i)

이 단계에서는 C언어 코드 작성 시 #으로 시작하는 전처리기 구문을 처리하게 됩니다. 단순 치환으로 이루어지기 때문에 원치 않는 연산 순서 변경을 막기 위해 항상 매크로 구문이나 전처리 구문에 괄호를 추가합니다.

#include로 특정 파일 포함하라는 명령입니다. 전처리기 처리 시 특정 파일을 찾아서 소스 코드를 그대로 #include 자리에 추가합니다.

Compile(a.s)

컴파일 과정에서 컴파일러는 우리가 작성한 고수준의 언어를 저수준의 언어(Assembly)로 번역하는 역할을 합니다. 어셈블리어로 바꾸는 알고리즘에 따라서 컴파일러의 성능이 달라지게 됩니다.

예를 들어 clang 컴파일러는 일반 if문도 switch문처럼 동작하도록 하여 다른 컴파일러보다 if를 사용하였을 때, 미약하게 빠르게 동작합니다.

Assembler(a.o)

어셈블러는 어셈블리어를 완전한 기계어로 번역합니다.

인간이 이해할 수 있는 고수준의 언어는 자연어라고 하고, 기계가 이해할 수 있는 저수준의 언어를 기계어라고 합니다.

보통 이 결과물을 object 파일 혹은 binary 파일이라고 합니다.
대부분 어떤 데이터가 어떤 메모리 주소로 가야하는지가 명세되어 있다고 생각하면 됩니다. binary나 hexdump로 파일을 열어보면 인간이 이해할 수 없습니다. 하지만 전 이 세상 어딘가에 이걸 또 뜯어 볼 수 있는 사람이 있을 것이라는 사실이 무섭습니다.

Linker(a.out)

일반적으로 프로젝트를 만들 때, 아무리 소규모의 프로젝트라도 소스 코드 파일 하나로 만드는 경우는 잘 없습니다. 여러가지의 파일을 함께 엮어서 프로젝트로 만드는데, 마지막에 Linker가 여러가지 파일과 라이브러리들을 하나로 엮는 역할을 합니다.


GNU 컴파일러 사용법

다음 내용이 들어 있는 a.cc라는 파일이름의 c++ 소스코드를 작성하였습니다.

#include <iostream>

int main(){
    int i;
    std::cout << "Hello World!" << std::endl;
    return 0;
}

결과물 생성

확장자만 설정하여도 컴파일러가 어디까지 거친 결과물을 출력할지 결정한다는 말이 있는데, 이는 틀린 정보입니다. 인수를 추가해주어야 컴파일러가 중단 단계를 설정합니다.

  • -E: 전처리 과정까지만 마치고(컴파일러를 수행하지 않고) 결과물을 저장.
  • -S: 컴파일 과정까지만 마치고(어셈블러를 수행하지 않고) 어셈블리어 결과물 저장.
  • -c: 어셈블리 과정까지만 마치고(링커를 수행하지 않고) 오브젝트 파일 결과물을 저장.
  • -o "filename": "filename"을 가지는 결과물을 만든다.

다음 명령어는 a.cc 소스코드를 전처리 과정까지만 수행한 결과물을 a.i라는 이름으로 저장하라 라는 의미를 가진다.

$ g++ -E a.cc -o a.i

언어 버전

  • -std="version": 특정 c/c++ 개정 버전을 지정한다.

다음 명령은 C++17 개정판 버전으로 a.cc 파일을 컴파일 하여 a.o 라는 이름의 결과물을 생성하라는 의미입니다.

$ g++ a.cc -o a.o -std=c++17

중간 과정 출력

  • -v: 컴파일 과정을 출력

경고

  • -Wall: 모든 모호한 코드에 대하여 경고 출력.
  • -Wextra: Wall flag에서 확인하지 않는 예외 경고만 출력.
  • -Werror: 모든 warning을 error로 취급함. 경고가 하나라도 발생하면 컴파일 중단.
  • -w: 모든 경고 메세지를 출력하지 않음.

다음은 a.cc 소스 코드의 모호한(오류를 발생시킬 여지가 있는) 코드들에 대한 오류를 출력하고 a.o라는 이름의 실행 파일을 생성하라는 명령입니다.
실행 후, 사용하지 않은 변수에 대한 경고가 발생하는 모습을 확인할 수 있습니다.

$ g++ -Wall a.cc -o a.o
a.cc: In function 'int main()':
a.cc:4:6: warning: unused variable 'i' [-Wunused-variable]
  int i;
      ^

최적화

대부분의 임베디드 프로그래머는 메모리와 포인터를 많이 다루기 때문에 최적화를 수행하지 않습니다. 일반적으로는 O2 최적화를 많이 사용합니다.

  • -O0: 최적화를 수행하지 않습니다.
  • -O1: 약간의 최적화만 수행합니다.
  • -O2: 일반적인 최적화 작업들을 수행합니다.(가장 많이 사용)
  • -O3: 할 수 있는 모든 최적화를 수행합니다.(코드의 변형이 큼)
  • -O5: 결과물의 사이즈 최적화를 수행합니다.(임베디드에서 사용)

라이브러리 옵션

  • -shared: 공유 라이브러리(동적 라이브러리)와 정적 라이브러리가 동시에 존재한다면 공유 라이브러리를 우선적으로 링크합니다.

  • -static: 공유 라이브러리와 정적 라이브러리가 동시에 존재한다면 정적 라이브러리를 우선적으로 링크합니다.

  • -nostdlib: 표준 라이브러리를 링크하지 않습니다.

  • -listdc++: GCC는 기본적으로 C 언어 라이브러리와 링크 되어 있기 때문에, C++ 파일을 컴파일 할 수 없습니다. 이 인수를 통하여 C++ 라이브러리와 링크합니다.

$ gcc "filename" -o "filename.cpp" -lstdc++
profile
Hi, my name is Eugene CHOI the Automotive MCU FW developer.

0개의 댓글