- 책 또는 웹사이트의 내용을 복제하여 다른 곳에 게시하는 것을 금지합니다.
- 책 또는 웹사이트의 내용을 발췌, 요약하여 강의 자료, 발표 자료, 블로그 포스팅 등으로 만드는 것을 금지합니다.
C 언어 배열과 Go 언어 슬라이스 사용하기
이재홍 http://www.pyrasis.com 2014.12.17 ~ 2015.02.07
Go 언어의 슬라이스는 C 언어에서 배열로 사용할 수 있습니다. 그리고 C 언어의 배열을 Go 언어의 슬라이스로 만들 수 있습니다.
다음은 Go 언어의 슬라이스를 C 언어의 배열로 사용하는 방법입니다.
package main
/*
#include <stdio.h>
void CExample(void *p) { // 슬라이스를 void * 타입으로 받음
char *a = (char *)p; // char * 타입으로 변환
printf("%c\n", a[0]); // H
printf("%s\n", a); // Hello, world!
}
*/
import "C"
import "unsafe"
func main() {
b := []byte("Hello, world!") // 바이트 형식으로 슬라이스 선언
C.CExample(unsafe.Pointer(&b[0])) // 슬라이스 첫 번째 요소의 포인터를 unsafe.Pointer로 변환하여 넣어줌
}
H
Hello, world!
[]byte 형식으로 슬라이스를 선언한 뒤 슬라이스 첫 번째 요소의 레퍼런스(포인터)를 unsafe.Pointer로 변환하여 넘겨줍니다. 그리고 C 언어쪽에서는 void * 타입으로 받은 뒤 char * 타입으로 변환합니다. 변환한 뒤에는 배열처럼 사용하면 됩니다.
이번에는 C 언어의 배열을 Go 언어의 슬라이스로 만들어보겠습니다.
package main
/*
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define ARRAY_LENGTH 5
int *GetArray()
{
int *a = (int *)malloc(sizeof(int) * ARRAY_LENGTH); // 메모리 할당
memset(a, 0, sizeof(int) * ARRAY_LENGTH); // 0으로 초기화
a[0] = 21; // 배열의 요소에 값을 넣어줌
a[1] = -15;
a[2] = 68;
a[3] = 72;
a[4] = -33;
return a; // 할당한 메모리 리턴
}
int GetLength() // 배열의 길이를 구하는 함수
{
return ARRAY_LENGTH;
}
*/
import "C"
import (
"fmt"
"reflect"
"unsafe"
)
func main() {
var a *C.int = C.GetArray() // 배열의 포인터를 받음
length := int(C.GetLength()) // 길이도 구함
hdr := reflect.SliceHeader{ // 배열을 감싸는 슬라이스 헤더 생성
Data: uintptr(unsafe.Pointer(a)), // unsafe.Pointer로 변환하여 넣어줌
Len: length, // 배열의 길이
Cap: length, // 배열의 길이
}
// 슬라이스 헤더 hdr은 []C.int, hdr의 레퍼런스는 포인터이므로 *[]C.int
s := *(*[]C.int)(unsafe.Pointer(&hdr)) // 슬라이스 헤더의 포인터로 변환한 뒤 역참조
fmt.Println(s) // [21 -15 68 72 -33]
fmt.Println(s[2:5]) // [68 72 -33]
C.free(unsafe.Pointer(a)) // C 언어에서 malloc 함수로 할당한 메모리는 반드시 해제
}
먼저 C 언어 코드에서 malloc 함수로 메모리 공간을 할당한 뒤 0으로 초기화합니다.
int *a = (int *)malloc(sizeof(int) * ARRAY_LENGTH); // 메모리 할당
memset(a, 0, sizeof(int) * ARRAY_LENGTH); // 0으로 초기화
Go 언어에서 슬라이스를 만들려면 배열 길이도 필요합니다. 따라서 다음과 같이 배열 길이를 구하는 함수도 만들어줍니다.
int GetLength() // 배열의 길이를 구하는 함수
{
return ARRAY_LENGTH;
}
이제 Go 언어에서 배열의 포인터와 길이를 이용해 슬라이스로 만듭니다.
var a *C.int = C.GetArray() // 배열의 포인터를 받음
length := int(C.GetLength()) // 길이도 구함
hdr := reflect.SliceHeader{ // 배열을 감싸는 슬라이스 헤더 생성
Data: uintptr(unsafe.Pointer(a)), // unsafe.Pointer로 변환하여 넣어줌
Len: length, // 배열의 길이
Cap: length, // 배열의 길이
}
Go 언어의 슬라이스는 기본적으로 배열을 감싸고 있는 일종의 구조체입니다. 따라서 reflect.SliceHeader 함수로 배열을 감싸는 슬라이스 구조체(헤더)를 생성합니다. Data 필드에는 배열의 포인터 a를 unsafe.Pointer 타입으로 변환한 뒤 다시 uintptr 타입으로 변환하여 넣어줍니다. Len, Cap 필드에는 배열의 길이를 넣어줍니다.
슬라이스 헤더에서 슬라이스를 가져옵니다.
// 슬라이스 헤더 hdr은 []C.int, hdr의 레퍼런스는 포인터이므로 *[]C.int
s := *(*[]C.int)(unsafe.Pointer(&hdr)) // 슬라이스 헤더의 포인터로 변환한 뒤 역참조
fmt.Println(s) // [21 -15 68 72 -33]
fmt.Println(s[2:5]) // [68 72 -33]
C.free(unsafe.Pointer(a)) // C 언어에서 malloc 함수로 할당한 메모리는 반드시 해제
처음 배열의 포인터는 *C.int 타입이었습니다. 이 포인터를 슬라이스 헤더로 만들면서 []C.int 타입으로 바꾸었습니다. 그리고 &hdr과 같이 헤더의 레퍼런스(포인터)를 구했으므로 []C.int 타입의 포인터인 *[]C.int가 됩니다. 이제 헤더 포인터를 unsafe.Pointer 포인터로 변환한 뒤 *[]C.int 타입으로 역참조하여 슬라이스를 가져오면 됩니다.
fmt.Println 함수로 출력해보면 슬라이스 형태로 출력이되며 부분 슬라이스도 만들 수 있습니다. 그리고 Go 언어의 슬라이스 형태가 되었지만 가비지 컬렉터에서 관리되지 않습니다. 따라서 슬라이스 사용 후 배열 a의 메모리는 반드시 C 언어의 free 함수로 해제해야 합니다.
저작권 안내
이 웹사이트에 게시된 모든 글의 무단 복제 및 도용을 금지합니다.- 블로그, 게시판 등에 퍼가는 것을 금지합니다.
- 비공개 포스트에 퍼가는 것을 금지합니다.
- 글 내용, 그림을 발췌 및 요약하는 것을 금지합니다.
- 링크 및 SNS 공유는 허용합니다.