정적 라이브러리, 동적 라이브러리 (#1, Windows)

윤찬호·2022년 10월 31일
0

C/C++

목록 보기
1/1

1. 라이브러리

라이브러리는 프로그램을 개발하기 위해 사용되는 함수, 데이터, 자료형 등을 하나로 묶어 놓은 것 으로, 다른 프로그램들과 링크되기 위해 존재하며 코드 재사용 및 개발시간 단축을 위해 사용된다.

라이브러리는 프로그램 빌드 시에 포함되는 정적 라이브러리와 실행 파일과 분리되어 사용되는 동적 라이브러리로 분류된다.

1-1. 정적 라이브러리 (LIB)

정적 라이브러리는 실행 파일(프로그램)에 라이브러리의 내용이 포함되는 형식이다.

정적 라이브러리를 사용하는 경우 프로그램을 실행할 때 별도의 파일들 없이 실행 파일만들 사용하여 프로그램을 실행할 수 있다.

하지만 실행 파일에 라이브러리의 내용들이 포함되어 실행 파일의 사이즈가 커지기 때문에 프로그램이 사용하는 메모리의 양도 커진다. 또한 라이브러리 파일을 다시 만들었다면 이 라이브러리를 사용하는 모든 실행 파일은 새로 만든 라이브러리 파일을 사용해서 다시 빌드(컴파일) 해야 한다.

응용 프로그램에서 정적 라이브러리를 사용하기 위해서는 해당 라이브러리를 링크하여 사용하며, 링크된 라이브러리에 포함된 함수들을 사용할 수 있다.

프로그램을 컴파일하면 링크된 라이브러리의 코드를 링크하여 하나의 실행 파일을 만들어낸다.

정적 라이브러리는 응용 프로그램에서 링크하여 컴파일 하기 때문에 사용하지 않는 내용들도 모두 실행 파일에 포함되지만 동적 라이브러리는 실행 파일에서 사용하고자 하는 경우에만 필요한 DLL을 선택적으로 사용할 수 있다. 즉, DLL에 포함되어 있는 함수들은 실행 파일에 포함되지 않고, 별도의 파일로 존재한다.

LIB 파일은 빌드시에 실행 파일에 포함되지만, DLL 파일은 프로그램이 실행될 때 실행 파일에 포함된다. 따라서 DLL은 LIB와 달리 새로 만들었더라도 해당 DLL을 사용하는 실행 파일을 수정하지 않아도 된다. 단, DLL 파일에 함수가 추가/삭제 되거나 파라미터가 변경된 경우에는 실행 파일의 수정이 필요할 수 있다.

DLL 파일은 Windows 운영체제에서만 사용이 가능하다.


응용 프로그램에서 DLL을 링크하는 방법은 암시적(Implict) 링크와, 명시적(Explicit) 링크 방식이 존재한다.

암시적 링크
응용 프로그램의 코드에서는 아무런 작업을 하지 않고, 프로젝트에 DLL을 링크하는 내용들을 기술하여 실행 파일을 빌드하는 방식이다.
실행 파일이 실행되면 자동으로 DLL을 로드하기 때문에 프로그래머가 DLL을 사용하기 위해 별도의 작업(DLL파일을 읽고 로드하는 작업)을 하지 않아도 된다.
하지만 해당 경로에 DLL이 존재하지 않으면 응용 프로그램도 동작하지 않는다는 단점이 있다.

명시적 링크
응용 프로그램을 작성할 때 DLL의 로드가 필요한 부분에서 DLL을 로드하여 사용하도록 하는 방식이다.
프로그래머가 DLL을 로딩하고, DLL에서 제공하는 함수들의 주소를 획득하여 사용해야 한다는 단점이 있다. 하지만 필요한 시점에만 DLL을 로드해서 사용할 수 있고, 사용이 끝나면 DLL을 언로드하기 때문에 메모리를 효율적으로 사용할 수 있다.


2. 라이브러리 사용하기

2-1. 정적 라이브러리 사용하기

(1) 프로젝트 만들기

Visual Studio 새 프로젝트 만들기 → 정적 라이브러리 (프로젝트 이름: TestStaticLib)

(2) 외부에서 호출할 함수 작성

//TestStaticLib.cpp

#include "pch.h"
#include "framework.h"

void fnTestStaticLib()
{
	std::cout << "fnTestStaticLib\n";
}


//TestStaticLib.h 파일 생성
#pragma once

void fnTestStaticLib();

위에서 작성한 프로젝트를 빌드하면 TestStaticLib.lib 파일이 생성된다.

(3) lib 사용하기

(3-1) lib를 사용할 테스트 프로젝트 만들기
솔루션 우클릭 - 추가 - 새 프로젝트 - 빈 프로젝트

생성된 프로젝트 우클릭 - 시작 프로젝트로 설정

새로 만든 프로젝트 폴더에 lib폴더와, include 폴더를 생성한 후, lib폴더에는 TestStaticLib.lib파일을 복사하고 include 폴더에는 TestStaticLib.h 파일을 복사한다

lib를 사용할 소스 파일을 생성한다. (test.cpp)

lib를 사용할 프로젝트를 우클릭 해서 속성페이지로 들어가 아래 내용을 입력한다.

C/C++ - 일반 - 추가 포함 디렉터리: ./include;
링커 - 입력 - 추가 종속성: ./lib/TestStaticLib.lib;

(3-2) lib 함수 호출하기

// lib를 사용할 test.cpp파일 생성 

#include "TestStaticLib.h"

int main()
{
	fnTestStaticLib();
	return 1;
}

프로젝트 속성에서 lib파일을 추가 종속성으로 설정하지 않는 경우 아래와 같이 사용할 수 있다.

#include "TestStaticLib.h"

#pragma comment(lib, "./lib/TestStaticLib.lib")

int main()
{
	fnTestStaticLib();
	return 1;
}

2-2. 동적 라이브러리 사용하기

2-2-1. 명시적 링크

(1) 프로젝트 만들기

Visual Studio 새 프로젝트 만들기 → DLL(동적 연결 라이브러리) (프로젝트 이름: TestDLL)

(2) C++ 파일을 생성하여 외부에서 호출할 함수 작성

DLL에 포함된 함수들 중 외부 참조를 허용하고자 하는 함수들은 아래와 같은 형식으로 지정해 주어야 한다.

extern "C" __declspec(dllexport) 리턴형식 함수이름(인자, ...)

DLL을 사용하는 프로그램 에서는 위에 지정된 형식의 함수만을 호출하여 사용할 수 있다.

// test.cpp
extern "C" __declspec(dllexport) int plus(int a, int b)

위에서 작성한 프로젝트를 빌드하면 *.dll 파일과 *.lib 파일이 생성된다.

(3) 명시적 링크로 DLL 사용하기

(3-1) DLL을 사용할 테스트 프로젝트 만들기
Visual Studio 새 프로젝트 만들기 → MFC앱 → 애플리케이션 종류: 대화 상자 기반

(프로젝트 속성 - 고급 - 문자 집합: 멀티바이트 문자 집합 사용)

다이얼로그 창이 나오면 '도구 상자'를 열어서 'Button'을 드래그 해서 다이얼로그에 놓는다.

'Button1'을 더블 클릭하여 클릭 이벤트 메서드를 추가한다.

(3-2) DLL 함수 호출하기

void CTestUseMFCDlg::OnBnClickedButton1()
{   
    // 1. DLL에서 호출해서 사용할 함수의 원형과 동일한 형태로 함수 선언
    typedef int (*TestDllFunc)(int a, int b);
 	TestDllFunc lpTestFunc;
    
    // 2. DLL 인스턴스 핸들 생성
    HINSTANCE hDll;
    
    // 3. DLL 로드
    hDll = LoadLibrary("Test.dll");
    if(hDll == NULL)
    {
    	AfxMessageBox("Test.dll 파일이 없습니다.");
        return;
    }
    
    // 4. DLL 에서 호출할 함수 주소 획득
    lpTestFunc = (TestDllFunc)GetProcAddress(hDll, "plus");
    if(lpTestFunc == NULL)
    {
    	AfxMessageBox("plus 함수가 없습니다.");
        return;
    }
    
    // 5. DLL 함수 호출
    CString strTemp = "";
    strTemp.Format("5+7=%d", lpTestFunc(5, 7));
    AfxMessageBox(strTemp);
    
    return;
}

(3-3) 프로그램 실행하기

현재 상태에서 프로젝트를 빌드한 후, 생성된 exe파일을 실행하고 'Button1'을 클릭하면 아래와 같은 메세지 창이 나온다.

위에서 만들었던 dll 파일을 실행 파일(*.exe)이 있는 폴더에 복사한다.

실행 파일을 다시 실행하고 'Button1'을 클릭하면 아래와 같이 dll 파일의 함수를 정상적으로 호출하는 것을 확인할 수 있다.

명시적 링크 방법으로 DLL을 로드해서 사용하는 경우 LoadLibrary를 호출하기 전에는 메모리에 DLL의 코드들이 올라가지 않기 때문에 실행 중인 프로그램의 메모리 사용량이 줄어든다.

하나의 프로그램에서 많은 DLL 파일을 사용한다면 명시적 링크를 사용하여 메모리 사용량을 줄일 수 있다.


2-2-3. 암시적 링크

명시적 링크 방식으로 DLL을 사용하는 경우 DLL에 포함 되어 있는 함수들의 내용을 정확히 알지 못하면 사용할 수 없다는 단점이 있다.

또한 암시적 링크를 사용하면 프로그래머가 DLL을 로드하고, 함수의 주소를 획득하는 등의 작업을 하지 않아도 된다는 장점이 있다.

(1) 헤더 파일 만들기

위에서 만들었던 DLL프로젝트 (TestDLL)에 헤더 파일을 추가한다.
헤더 파일과 라이브러리 파일을 사용하면 암시적 링크로 DLL을 사용할 수 있다.

추가된 헤더 파일에 DLL프로젝트의 cpp파일에 포함되어 있는 함수와 동일한 함수 선언 코드를 작성한다.

// test.h
extern "C" __declspec(dllexport) int plus(int a, int b);

(2) 암시적 링크로 DLL 사용하기

암시적 링크로 DLL을 사용하기 위해서는 아래의 3개의 파일이 필요하다.

DLL 헤더 파일 (*.h)
LIB 파일 (*.lib)
DLL 파일 (*.dll)

DLL을 호출할 프로젝트의 소스파일이 있는 폴더에 헤더 파일과 LIB파일을 복사한다.
실행 파일이 있는 폴더에 DLL 파일을 복사한다.

명시적 링크 방식으로 DLL을 호출했던 코드를 주석 처리하고 아래와 같이 수정한다.

#include "test.h" // DLL 헤더 파일 include
#pragma comment(lib, "TestDLL.lib") 
...

void CTestUseMFCDlg::OnBnClickedButton1()
{
	CString strTemp = "";
	strTemp.Format("5 + 7 = %d", plus(5, 7)); // 암시적 링크로 DLL 사용하기
	AfxMessageBox(strTemp);
}

프로젝트를 빌드하고 실행하면 명시적 링크로 DLL을 사용했을 때와 동일한 결과를 볼 수 있다.

실행 파일이 있는 폴더에서 DLL파일을 삭제하고 실행하면 아래와 같은 오류가 발생한다.

명시적 링크 방식으로 DLL을 사용했을 때에는 DLL 파일이 없더라도 실행 파일을 실행시킬 수 있었다. 'Button1'을 클릭했을 때 DLL을 로드하고 사용하기 때문이다.

그러나 암시적 링크 방식으로 DLL을 사용했을 때에는 DLL 파일이 없으면 실행 파일 자체를 실행시킬 수 없는 것을 확인할 수 있다.

0개의 댓글