Menu

C++ 컴파일: g++로 소스 코드에서 실행 파일까지

C++가 .cpp 소스를 네이티브 실행 파일로 바꾸는 방법: g++(또는 clang++/MSVC)로 컴파일하고, 바이너리를 실행하고, 문제가 생겼을 때 컴파일러 오류를 읽는 법.

이 페이지에는 실행 가능한 에디터가 있습니다 - 편집하고 실행하면 결과를 바로 볼 수 있습니다.

먼저 컴파일, 그다음 실행

이제 컴파일러를 설치했으니, C++ 프로그램을 실행하는 일은 두 단계 사이클입니다. 인터프리터가 파일을 한 줄씩 읽는 스크립트 언어와 달리, C++는 먼저 소스 코드를 네이티브 실행 파일(기계어로 된 독립 실행 바이너리)로 컴파일하고, 그다음 여러분이 그 바이너리를 직접 실행합니다. 실행 시점에 프로그램과 CPU 사이에 끼어 있는 인터프리터는 없습니다.

이 분리가 바로 C++가 빠른 이유이자, 문법 실수가 실행 도중이 아니라 컴파일 시점에 여러분을 멈춰 세우는 이유입니다. 컴파일러는 무언가를 만들어 내기 전에 파일 전체를 검사합니다.

자체 완결적인 예제라면 이 페이지에서 바로 실행할 수 있습니다. 아래 에디터가 대신 컴파일하고 실행해 줍니다. 하지만 자기 컴퓨터에서 무슨 일이 일어나는지 알아 둘 가치가 있습니다. 실제 프로젝트는 그곳에 있으니까요.

내 컴퓨터에서의 컴파일-실행 사이클

이것을 main.cpp로 저장했다고 해 봅시다:

#include <iostream>
using namespace std;

int main() {
    cout << "Hello from the terminal" << endl;
    return 0;
}

파일이 들어 있는 폴더에서 터미널을 열고 컴파일러를 호출하세요. 가장 흔한 컴파일러는 g++(GCC의 일부)입니다. 여기서 다루는 내용은 clang++도 똑같이 동작합니다:

g++ main.cpp -o main

코드가 문제없이 컴파일되면 g++는 아무것도 출력하지 않고, 소스 옆에 새 파일을 만듭니다. main(Windows에서는 main.exe)이라는 실행 파일입니다. 이 바이너리는 기계어이며, 읽을 수 있는 텍스트가 아니고, 여러분의 운영체제와 CPU에 묶여 있습니다. 이제 실행해 보세요:

./main
Hello from the terminal

Windows에서는 같은 터미널에서 main.exe(또는 그냥 main)로 실행합니다. macOS와 Linux의 ./는 셸에게 "프로그램은 PATH의 어딘가가 아니라 바로 이 폴더 안에 있다"고 알려 줍니다. 초보자가 자주 빠뜨리는 단계인데, 그러고는 main이 왜 command not found라고 하는지 의아해합니다.

-o가 하는 일 (그리고 a.out)

-o 플래그는 출력 이름을 지정합니다. 빼더라도 g++는 여전히 컴파일하지만, 실행 파일을 기본 이름으로 씁니다. macOS/Linux에서는 a.out, Windows에서는 a.exe입니다.

g++ main.cpp        # produces a.out, not main
./a.out

이건 사람들이 끊임없이 헷갈리는 부분입니다. 컴파일했는데 오류가 없고, ./main을 실행하면 No such file or directory가 나옵니다. 바이너리의 실제 이름이 a.out이기 때문입니다. 무엇을 실행하는지 정확히 알 수 있도록 항상 -o를 넘기세요.

소스를 바꿀 때마다 한 번 컴파일합니다. 그 뒤로는 실행 파일이 독립적이어서, 다시 컴파일하지 않고도 ./main을 백 번이든 실행할 수 있습니다. g++를 다시 실행하는 건 .cpp 파일을 수정한 뒤뿐입니다.

C++ 표준 고르기

C++에는 버전이 있습니다. C++11, C++14, C++17, C++20, C++23이며 각각 언어 기능을 추가합니다. 함정은 컴파일러가 기대보다 오래된 기본 표준을 고를 수 있다는 점이라, 최신 코드가 뚜렷한 이유 없이 컴파일에 실패할 수 있습니다. -std로 표준을 명시적으로 지정하세요:

g++ -std=c++17 main.cpp -o main
g++ -std=c++20 main.cpp -o main

다음은 C++17 기능(structured bindings)을 쓰는 코드입니다. 에디터에서는 문제없이 컴파일되지만, 여러분의 컴퓨터에서는 -std=c++17 이상이 필요합니다:

만약 'structured bindings' only available with '-std=c++17' 같은 오류를 보게 된다면, 고쳐야 할 곳은 코드가 아니라 올바른 -std 플래그를 추가하는 일입니다. 이 강좌 전체에서 C++17 이상을 전제로 하세요.

경고 켜기

C++ 프로그램은 문제없이 컴파일되고도 여전히 틀릴 수 있습니다. 요청하면 컴파일러는 코드가 실행 시점에 여러분을 물기 전에 수상한 코드를 짚어 줍니다. -Wall -Wextra를 추가하세요:

g++ -std=c++17 -Wall -Wextra main.cpp -o main

이 프로그램을 봅시다. -Wall 없이도 컴파일되지만, 진짜 버그가 있습니다. 값을 한 번도 주지 않은 변수를 읽는 것이고, 이는 **정의되지 않은 동작(undefined behavior)**입니다:

#include <iostream>
using namespace std;

int main() {
    int count;                 // never initialized
    cout << count << endl;     // reads garbage - undefined behavior
    return 0;
}

경고를 켜면 컴파일러가 이를 표시합니다:

warning: 'count' is used uninitialized [-Wuninitialized]

첫날부터 -Wall -Wextra로 컴파일하는 습관을 들이세요. 경고는 컴파일러가 무료로 해 주는 코드 리뷰입니다. 그걸 무시하는 것이 미묘한 버그가 살아남는 방식입니다. 여기서의 해결책은 간단히 int count = 0;입니다.

컴파일러 오류 읽기

g++가 코드를 거부할 때는 파일, 줄, 그리고 무엇이 잘못됐는지를 알려 줍니다. 이 메시지를 읽을 줄 아는 것이 막힌 상황에서 벗어나는 일의 절반입니다. 고전적인 예가 여기 있습니다. 빠진 세미콜론입니다:

#include <iostream>
using namespace std;

int main() {
    cout << "Oops"      // no semicolon
    return 0;
}
main.cpp:5:5: error: expected ';' before 'return'
    5 |     return 0;
      |     ^~~~~~

몇 가지 눈여겨볼 점이 있습니다. 오류는 5번째 줄을 가리키지만, 실수는 4번째 줄에 있습니다. 컴파일러는 다음 토큰에 다다라서야 세미콜론이 빠졌다는 걸 알아차립니다. 그러니 오류가 멀쩡해 보이는 줄을 가리킬 때는 그 줄을 확인하세요. main.cpp:5:5는 파일, 줄, 그다음 열입니다. 컴파일러가 짚은 그 한 가지만 고치고 다시 컴파일하세요. 추측은 자제하세요.

컴파일러 오류는 아직 아무것도 실행되지 않았다는 뜻입니다. 이는 프로그램이 시작된 뒤 죽는 런타임 오류와는 다릅니다. 프로그램이 실행되기도 전에 컴파일 시점에 실수를 잡아내는 것은 C++의 가장 큰 강점 중 하나입니다.

점검용 프로그램

이것을 에디터에서 실행하거나, main.cpp로 저장한 뒤 자기 컴퓨터에서 g++ -std=c++17 -Wall main.cpp -o main을 하고 ./main을 해 보세요. 세 줄이 모두 나타나면 여러분의 툴체인이 처음부터 끝까지 동작하는 것입니다:

여기에는 곧 제대로 만나게 될 세 가지가 등장합니다. int 변수, vector(C++의 크기 조절 가능한 배열), 그리고 출력용 cout입니다. 지금은 프로그램이 컴파일되고 세 줄을 순서대로 출력하는 것만으로 충분합니다.

다음: C++ 문법

프로그램을 몇 개 컴파일하고 실행해 봤지만, 문장 부호는 그냥 지나쳐 왔습니다. #include 줄, 중괄호, 세미콜론, int main(), 그리고 각 줄이 왜 그런 모습인지 말이죠. 다음 페이지에서는 C++ 문법을 하나씩 뜯어봅니다. 그러면 구조가 그저 정형화된 틀처럼 느껴지지 않고 의미를 갖기 시작할 것입니다.

자주 묻는 질문

C++ 프로그램을 어떻게 컴파일하고 실행하나요?

코드를 main.cpp로 저장하고, 그 폴더에서 터미널을 연 뒤 g++ main.cpp -o main을 실행하면 실행 파일이 만들어집니다. 그다음 macOS/Linux에서는 ./main, Windows에서는 main.exe로 실행하세요. 컴파일은 한 번만 하면 되고, 만들어진 바이너리는 원하는 만큼 몇 번이든 실행할 수 있습니다.

g++의 -o 플래그는 무엇을 하나요?

-o는 출력 파일의 이름을 지정합니다. g++ main.cpp -o hellohello라는 실행 파일을 만듭니다. -o를 빼면 g++는 기본값인 a.out(Windows에서는 a.exe)을 사용합니다. 초보자가 자기도 모르게 만든 파일을 실행하게 되는 이유가 바로 이것입니다.

C++17이나 C++20 같은 특정 표준으로 C++를 어떻게 컴파일하나요?

-std 플래그를 넘기세요: g++ -std=c++17 main.cpp -o main 또는 -std=c++20. 이 플래그가 없으면 컴파일러는 자체 기본값을 쓰는데, 기대보다 오래된 표준일 수 있습니다. 그래서 structured bindings나 <ranges> 같은 최신 기능은 표준을 명시적으로 지정하기 전까지 컴파일에 실패할 수 있습니다.

Coddy programming languages illustration

Coddy로 코딩 배우기

시작하기