리눅스에서 C++ 함수를 C언어세서 사용하기
리눅스에서 C++ 함수를 C언어세서 사용하는 두 가지 방법을 알아본다.
목차
개요
C++ 함수를 C언어에서 사용하는 방법은 크게 두 가지가 있다.
정적 라이브러리 (.a) 로 임포트하는 방법과 동적 라이브러리 (.so) 로 임포트하는 방법이다.
이 문서에서는 두 가지 방법을 간단히 알아본다.
방법
testfunc.cpp
예시를 들어보자. 이런 함수가 존재한다. 이 함수는 두 문자열을 합쳐서 반환하는 함수이다. 그러나 그 과정에서 C++에서만 사용할 수 있는 std::string
을 사용하고 있다. 이 함수를 C언어에서 사용하려면 어떻게 해야할까?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <iostream>
#include <string>
#include <cstdlib>
#include <cstring>
char* str_plustest(char *str1, char *str2)
{
std::string s1(str1);
std::string s2(str2);
std::string s3 = s1 + s2;
size_t len = s3.length() + 1;
char *result = (char *)malloc(s3.length() + 1);
memset(result, 0, len);
strcpy(result, s3.c_str());
return result;
}
testfunc.h
헤더를 만들고 함수의 선언을 넣어준다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#ifndef __TESTFUNC_H__
#define __TESTFUNC_H__
// C++ 함수를 C에서 사용하기 위해 extern "C" 필요
#ifdef __cplusplus
extern "C" {
#endif
// 여기에 함수 선언을 넣어준다.
/**
* @brief 두 문자열을 합쳐서 반환하는 함수
* 반환하는 과정에서 malloc을 사용하므로 함수 호출 시 반환값에 대한 free가 필요하다.
*
* @return char* 합쳐진 문자열 (free 필요)
*/
char* str_plustest(char *str1, char *str2);
#ifdef __cplusplus
}
#endif
#endif /* __TESTFUNC_H__ *
그리고 testfunc.cpp
파일에 testfunc.h 헤더를 include 해준다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include "testfunc.h"
#include <iostream>
#include <string>
#include <cstdlib>
#include <cstring>
char* str_plustest(char *str1, char *str2)
{
std::string s1(str1);
std::string s2(str2);
std::string s3 = s1 + s2;
size_t len = s3.length() + 1;
char *result = (char *)malloc(s3.length() + 1);
memset(result, 0, len);
strcpy(result, s3.c_str());
return result;
}
C언어 소스에서 str_plustest
함수를 사용하기 위해 다음과 같이 작성했다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <stdio.h>
#include <stdlib.h>
#include "testfunc.h"
#define free_safe(ptr) if((_p) != NULL) { free((_p)); (_p) = NULL; }
int main()
{
const char *str1 = "Hello ";
const char *str2 = "World!";
char *result = str_plustest((char *)str1, (char *)str2);
printf("CONSOLE | result : %s\n", result);
free_safe(result);
return 0;
}
그리고 testfunc.cpp
와 testfunc.h
파일은 cpp 디렉토리에 위치하도록 하였다.
이렇게 하면 소스는 준비가 끝난 것이다.
ls -al ./
결과
-
cpp/
testfunc.cpp
testfunc.h
main.c
리눅스에서 C 관련 라이브러리를 사용하기 위해서는 라이브러리의 이름이 lib로 시작해야 한다.
1. 정적 라이브러리 (.a) 로 임포트
1
2
3
4
5
6
7
# cd cpp
LIBRARY_NAME="testfunc"
g++ -c testfunc.cpp -o testfunc.o
ar rcs "lib${LIBRARY_NAME}.a" testfunc.o
# 별다른 문제가 없다면 libtestfunc.a 파일이 생성된다.
그리고 main.c
파일을 빌드한다.
1
2
3
4
5
6
7
8
# -I : include 헤더를 참조할 디렉토리 경로 (testfunc.h를 cpp 디렉토리에 넣었으므로 추가.)
# -L : 라이브러리를 참조할 디렉토리 경로
# -l : 라이브러리 이름 (libtestfunc.a 파일을 참조한다.)
# libtestfunc.a 파일을 참조하고 싶다면, lib를 뺀 이름을 넣어야 한다.
# -lstdc++ : C++ 표준 라이브러리 링크
gcc main.c -I./cpp -L./cpp -ltestfunc -lstdc++ -o main
# 별다른 문제가 없다면 main 바이너리 파일이 생성된다.
생성된 ./main
파일을 실행하면 Hello World!
가 출력된다.
2. 동적 라이브러리 (.so) 로 임포트
명령어가 조금 다르다.
1
2
3
4
5
6
7
# cd cpp
LIBRARY_NAME="testfunc"
g++ -fPIC -c testfunc.cpp -o testfunc.o
g++ -shared -o "lib${LIBRARY_NAME}.so" testfunc.o
# 별다른 문제가 없다면 libtestfunc.so 파일이 생성된다.
main.c
파일을 빌드하는 과정은 똑같다.
1
2
3
4
5
6
7
# -I : include 헤더를 참조할 디렉토리 경로 (testfunc.h를 cpp 디렉토리에 넣었으므로 추가.)
# -L : 라이브러리를 참조할 디렉토리 경로
# -l : 라이브러리 이름 (libtestfunc.a 파일을 참조한다.)
# libtestfunc.a 파일을 참조하고 싶다면, lib를 뺀 이름을 넣어야 한다.
# -lstdc++ : C++ 표준 라이브러리 링크
gcc main.c -I./cpp -L./cpp -ltestfunc -lstdc++ -o main
# 별다른 문제가 없다면 main 바이너리 파일이 생성된다.
동적 라이브러리이기 때문에 동적 라이브러리 실행에 대한 환경변수를 설정해 줘야 한다.
다음과 같이 실행한다.
1
2
# LD_LIBRARY_PATH : 동적 라이브러리를 참조할 디렉토리 경로
LD_LIBRARY_PATH=./cpp ./main
참고
정적 라이브러리와 동적 라이브러리의 차이
- 정적 라이브러리는 컴파일 시점에 라이브러리를 링크한다. 실행 파일에 내용이 모두 들어가기 때문에 실행 파일이 커진다.
- 대신 프로그램 안에 모든 내용이 들어가기 때문에 실행 파일만 있으면 실행이 가능하다.
- 프로그램이 로드되는 시점에는 정적 라이브러리의 내용을 메모리에 로드해야 하기에 시간이 느리지만, 수행 시간은 동적 라이브러리에 비해 빠르다.
- 동적 라이브러리는 실행 시점에 라이브러리를 링크한다. 실행 파일이 작아진다.
- 런타임 시점에서 라이브러리를 로드하기 때문에 실행 파일만 있으면 실행이 불가능하다.
- 동적 라이브러리에 대한 의존성이 있고, 어떤 동적 라이브러리 모듈을 참조할지 알아야 하기에 추가적인 환경변수 지정 등이 필요하다.
- ex )
/etc/ld.so.conf.d
수정 이후ldconfig
적용 또는LD_LIBRARY_PATH
환경변수 설정 등..
- ex )
- 프로그램이 로드되는 시점에는 동적 라이브러리의 내용을 메모리에 로드해야 하기에 시간이 빠르지만, 함수 호출 마다 라이브러리의 주소에 접근해야 하기에 수행 시간이 느리다.
라이브러리마다 각각의 장단점이 있으므로 상황에 맞게 사용하면 된다.