[Visual Studio Code]5. C언어 파일 분할 컴파일(GCC)

수성컴|프로그래밍/VS Code|2022. 5. 13. 16:11

수성비전자방입니다. 오늘은 Visual Studio Code 시리즈 중 하나로, C언어 파일 분할 컴파일을 하는 방법을 알아보겠습니다.

목차

1. 설치
2. C언어 컴파일
3. C언어 디버깅(gcc, gdb 디버거)
4. C언어 빌드 및 디버깅
5. C언어 파일 분할 컴파일(현재 글)
5.1. 사용자 정의 헤더 파일
5.2. 예제 소스코드
5.3. GCC 파일 분할 컴파일
5.4. VS Code 파일 분할 컴파일
5.5. 글 마무리
5.6. 참고 문헌

이 글은 VS Code 카테고리에 올라와 있지만, 1~4편은 Windows 카테고리에 올라가 있는 점 참고하시기 바랍니다.

5.1. 사용자 정의 헤더 파일

C언어 파일 분할 컴파일이 무조건 사용자 정의 헤더 파일을 이용하는 것은 아니지만, 이 글에서는 사용자 정의 헤더 파일을 사용하겠습니다. 시작하기에 앞서 관련 개념을 설명 드리겠습니다.

사용자 지정 헤더 파일의 폴더 구조
꼭 위와 같은 구조로 이루어져야 하는 것은 아닙니다. 위의 그림은 참고만 하세요.
위의 그림에 있는 파일들은 모두 한 폴더 안에 있다고 가정하겠습니다.
main.c 파일에는 main 함수가 들어가며, header.h를 include 합니다.
저는 그 외의 C code를 func1.c와 func2.c 두 개로 만들었습니다.(이하 '외부 파일')
그 다음 header.h도 만들어 주었습니다.(물론 헤더 파일도 2개로 분리할 수 있습니다.)
여기까지 보시면 무슨 내용인지 모를 것입니다. 다음 절인 5.2절에서 예제 소스코드를 보시면 이해하기 수월해집니다.

5.2. 예제 소스코드

main.c

#include <stdio.h>
#include "header.h"

int main(void)
{
    int a=1, b=2, add;
    double ave;
    point dot={1, 2};

    add=sum(a, b);
    printf("Sum is %d\n", add);

    ressum(&a, &b, &add);
    printf("Sum is %d\n", add);

    ave=average(a, b);
    printf("Average is %f\n", ave);

    resaverage(&a, &b, &ave);
    printf("Average is %f\n", ave);

    dot.x++; dot.y++;
    printf("점을 x방향으로 1, y방향으로 1 평행이동: (%f, %f)", dot.x, dot.y);

    return 0;
}

1~2행을 보세요. stdio.h는 표준 라이브러리라서 #include <stdio.h>로 <, >를 사용해 include 합니다. 그러나 header.h는 사용자 정의 헤더 파일이라서 #include "header.h"와 같이 ", "를 사용해 include 해야 합니다.
그 외에 main.c에서는 main 함수를 작성했습니다.

func1.c

int sum(int a, int b)
{
    return a+b;
}

void ressum(int* a, int* b, int* result)
{
    *result=*a+*b;
    return;
}

(func1.c는 func2.c와 함께 설명하겠습니다.)

func2.c

double average(int a, int b)
{
    return (double)(a+b)/2;
}

void resaverage(int* a, int* b, double* result)
{
    *result=(double)(*a+*b)/2;
    return;
}

func1.c와 func2.c에는 함수의 definition을 작성했습니다.

header.h

#ifndef HEADER
    #define HEADER

typedef struct _point{
    double x;
    double y;
} point;

int sum(int a, int b);
void ressum(int* a, int* b, int* result);
double average(int a, int b);
void resaverage(int* a, int* b, double* result);

#endif

헤더파일은 좀 더 설명을 드려야 할 것 같습니다.

[Line 1~2, 14]
여러 헤더 파일을 사용하면서 header가 여러 번 include 되는 것을 방지하는 코드입니다. 헤더 파일의 중복 자체는 심각한 문제가 되지 않는데(사소한 문제는 됨.ㅋ), 헤더 파일 내부의 구조체 정의가 반복되면 컴파일 에러가 발생합니다. 사실 예제에서는 헤더 파일을 1번 include 해서 딱히 필요 없습니다.
아무튼 방식을 설명 드리자면, HEADER가 정의되지 않은 경우에만 Line 2~13을 실행합니다. 그 중에서도 Line 2에서는 HEADER를 정의합니다. 즉, 다음에 또 이 헤더 파일을 include하면 Line 2~13을 실행하지 않겠다는 뜻입니다.

[Line 4~7]
구조체의 정의 부분입니다. 구조체를 분리하려면 C 소스파일(외부파일)보다는 헤더 파일에 넣는 것이 좋습니다. 구조체를 C file에서 정의했다면, 해당 구조체를 여러 C file에서 사용해야 할 때 구조체의 정의가 모든 C file에 존재해야 합니다. 만약 구조체의 정의를 헤더 파일에 넣었다면, 해당 구조체를 여러 C file에서 사용해야 할 때 #include "header.h"만 넣으면 됩니다.(물론 헤더파일이 header.h라고 가정하고요.)

[Line 9~12]
func1.c와 func2.c에서 정의한 함수의 prototype을 넣었습니다. 5.1절 그림에서 func.1과 header.h, func.2와 header.h가 선으로 연결된 이유입니다.

5.3. GCC 파일 분할 컴파일

그냥 아무 생각 없이 터미널에 gcc .\main.c를 입력하면 오류가 발생합니다. main.c에서 사용한 sum 함수, ressum 함수, average 함수, resaverage 함수의 정의 부분이 없기 때문이죠.

C언어 빌드 과정
5.3.1. 문제를 해결하기 위해서는 -c 옵션을 사용해야 합니다. -c를 사용하면 Linking 전까지만 진행하며, 목적 코드 파일(.o)을 얻을 수 있습니다.
func1.c, func2.c, main.c, header.h *
모두 한 폴더 안에** 있다고 가정하고
gcc .\func1.c -c
gcc .\func2.c -c
gcc .\main.c -c
터미널에서 이 3가지 명령어를 실행하면 func1.o, func2.o, main.o 파일이 생성됩니다.

Linking
5.3.2. gcc .\func1.o .\func2.o .\main.o -o gccFileSeperateCompile을 입력하여 실행하면 gccFileSeperateCompile.exe가 생성됩니다. -o는 실행파일명을 지정할 수 있는 옵션입니다. 이제 빌드가 끝났습니다.

실행
5.3.3. .\gccFileSeperateCompile.exe를 입력해서 완성된 파일을 실행해 보시면 잘 작동되는 것을 보실 수 있습니다.

5.4. VS Code 파일 분할 컴파일

gcc 명령어로 파일 분할 컴파일 하기가 번거롭죠? 이번에는 Visual Studio Code를 이용해서 파일 분할 컴파일과 디버깅을 해 보겠습니다.

F5를 누름
5.4.1. 아무 C file이나 연 채로 F5를 누르면 환경 선택 창이 뜹니다. C++(GDB/LLDB)를 클릭합니다.

구성 선택
5.4.2. C/C++:gcc.exe 활성 파일 빌드 및 디버그를 클릭합니다.

main.exe 미존재 오류 발생
5.4.3. 오류가 발생합니다. 침착하게 취소를 누릅니다.

.vscode 폴더
작업 폴더 안에 .vscode 폴더가 생겼을 것입니다.

5.4.4. task.json 파일에서 "${file}""${fileDirname}\\*.c"로, "${fileDirname}\\a.exe""${workspaceFolderBasename}.exe"로 바꿉니다. 그러면 아래와 같이 됩니다.

{
    "tasks": [
        {
            "type": "cppbuild",
            "label": "C/C++: gcc.exe 활성 파일 빌드",
            "command": "C:\\Program Files\\mingw64\\bin\\gcc.exe",
            "args": [
                "-fdiagnostics-color=always",
                "-g",
                "${fileDirname}\\*.c",
                "-o",
                "${workspaceFolderBasename}.exe"
            ],
            "options": {
                "cwd": "${fileDirname}"
            },
            "problemMatcher": [
                "$gcc"
            ],
            "group": {
                "kind": "build",
                "isDefault": true
            },
            "detail": "디버거에서 생성된 작업입니다."
        }
    ],
    "version": "2.0.0"
}

그냥 복사해서 쓰지 마시고, Line 6은 여러분의 컴퓨터에서 gcc가 설치된 경로를 입력하셔야 합니다. 1~4편 내내 제 글을 따라 오셨다고 안심하지 마시고, 여러분의 PC와 제 PC가 다를 수 있으니 주의하세요!(아니면 Line 6는 그냥 F5 눌렀을 때 생긴 그대로 냅두세요.)
반면, Line 10은 제가 쓴 대로 수정하세요. "${fileDirname}\\*.c"로 말이죠. 이것은 해당 폴더에 있는 모든 파일을 컴파일하는 역할을 합니다. 이거 안 하면 컴파일 에러가 발생해요!
Line 12는 사실 선택사항입니다. ${workspaceFolderBasename}는 작업 폴더를 의미합니다. 따라서 바로 위의 -o가 만나면 실행파일명이 작업 폴더명으로 됩니다.(오히려 좋아)

5.4.5. launch.json 파일의 모든 내용을 지우고 아래의 소스 코드를 복사해 넣으세요.

{
    // IntelliSense를 사용하여 가능한 특성에 대해 알아보세요.
    // 기존 특성에 대한 설명을 보려면 가리킵니다.
    // 자세한 내용을 보려면 https://go.microsoft.com/fwlink/?linkid=830387을(를) 방문하세요.
    "version": "0.2.0",
    "configurations": [
        {
            "name": "gcc.exe - 활성 파일 빌드 및 디버그",
            "type": "cppdbg",
            "request": "launch",
            "program": "${fileDirname}\\${workspaceFolderBasename}.exe",
            "args": [],
            "stopAtEntry": false,
            "cwd": "${fileDirname}",
            "environment": [],
            "externalConsole": false,
            "MIMode": "gdb",
            "miDebuggerPath": "C:\\Program Files\\mingw64\\bin\\gdb.exe",
            "setupCommands": [
                {
                    "description": "gdb에 자동 서식 지정 사용",
                    "text": "-enable-pretty-printing",
                    "ignoreFailures": true
                }
            ],
            "preLaunchTask": "C/C++: gcc.exe 활성 파일 빌드"
        }
    ]
}

Line 11은 task.json에서 지정한 실행파일명으로 하셔야 합니다.
Line 18은 gdb가 설치된 경로를 넣으세요.(gcc 설치된 경로에서 'gcc'를 'gdb'로 바꾸기만 하면 돼요. 제 것을 복사해서 쓰지 마시고, 여러분 PC에 gdb가 설치된 경로를 넣으셔야 합니다!)

5.4.6. task.json, launch.json 수정이 다 끝났으면 다시 아무 C file이나 열고 F5를 누르세요.

디버깅이 안 되는 오류
이런 오류가 발생하는 이유는 파일 경로에 한글이 포함되어 있기 때문입니다. 사실 디버깅만 안 되는 것이고, 빌드는 완료됐어요.

빌드는 됐음
EXE 파일이 잘 생성되었죠?

명령어로 실행
5.4.7. .\실행파일명.exe 즉, .\작업폴더명.exe를 입력하여 실행하시면 실행이 잘 됩니다.

그런데 명령어로 실행하려고 VS Code 쓰는 게 아니잖아요? 대충 작업 폴더(.vscode 포함)를 바탕화면에 옮겨놓은 후 F5를 눌러 봅시다.

디버깅 장면
실행도 잘 되고 디버깅도 잘 되는 것을 보실 수 있습니다.(사실 F5는 원래 디버깅 단축키입니다.)
아무래도 파일 경로는 다 영어로 쓰는 것이 좋을 것 같습니다.

5.5. 글 마무리

긴 글 읽어 주셔서 감사합니다. 지적 또는 질문 있으시면 댓글 달아 주세요. 로그인 하지 않아도 댓글 다실 수 있습니다. 또한 로그인 하지 않아도 공감 누르실 수 있습니다. 도움이 되었다면 공감 눌러 주시면 감사하겠습니다.

5.6. 참고 문헌

1) 윤성우. 2010. 열혈 C 프로그래밍. 오렌지미디어.
2) Microsoft. 2022. "Variables Reference", Visual Studio Code. (2022. 05. 13. 방문). https://code.visualstudio.com/docs/editor/variables-reference
3) cmaven. 2018. "Visual Studio Code(vs code) 파일 분할 컴파일", cmaven 작업노트. (2022. 05. 13. 방문). https://cmaven.github.io/vscode/VS-Code-Multi-Compile/
4) GonoBae. 2021. "VS Code Compile & Debugging (C/C++)", Medium. (2022. 05. 13. 방문). https://medium.com/@qldrhqorhsh/vs-code-compile-debugging-c-c-b8a568afa60c
5) 데브로그. 2019. "VS code GDB 사용 시 -environment-cd 에러", 데브로그. (2022. 05. 13. 방문). https://ttum.tistory.com/4

댓글()