1. JNI 변수 타입


JNI 변수 타입은 Java의 변수를 C++/C 에서 사용 할수 있게끔 호환해주는 변수 타입이다. 

jni.h를 인클루드하면 사용이 가능한데 자세한 정보는 아래의 표를 보면 알 수 있다.



JAVA 

C++/C 

C++/C 배열 

boolean

jboolean 

jbooleanArray 

byte

jbyte 

jbytArray

char 

jchar 

jcharArray 

short 

jshort 

jshortArray 

int 

jint 

jintArray 

long

jlong 

jlongArray 

float 

jfloat 

jfloatArray 

void 

jvoid 

jvoidArray 

Object 

jobject 

jobjectArray 

String 

jstring 

jstringArray 


java의 기본 변수형은 c++/c언어에서는 j가 붙은 클래스로 제공된다. int -> jint로 객체를 선언해서 사용해주면 되고, 배열도 마찬가지로 j+자료형+Array 객체형태로 생성해서 사용하면 된다.



c++ 에서의 사용 예


void main() {     jstring str;     jint num;     jobject obj;     ... }




2. JNI 시그니처

JNI 시그니처를 사용하는 이유는 JAVA에서 생성한 클래스나 함수에 접근하기위해 C++/C에서 사용하는 구분자라고 보면 된다. 


시그니처 

Type 

byte 

char 

double 

float 

int 

long 

short 

void 

boolean 

L클래스_이름 

패키지 경로/ 클래스 이름 

[ type 

type[] 배열 


public class aa {
	public int testInt(int a) {return a;}
	public String testString(String a) {return a;}
}


위와같은 java 소스에서 aa클래스 안에는 int testint(int) 메서드와 String testString(String) 메서드가 있다.

c++에서 위의 메서드를 콜하기 위해서는 아래와 같은 문법으로 접근해야한다.



void main() {
	jclass cls = (*env)->GetObjectClass(env, obj);
	jmethodID testInt = (*env)->GetMethodID(env, cls, "testInt", "(I)I");
	jint jnum = (*env)->CallIntMethod(env, obj, testInt, num);

	jmethodID testString = (*env)->GetMethodID(env, cls, "testString", "(Ljava.lang.String;)Ljava.lang.String;");
	jstring jstr = (*env)->CallObjectMethod(env, obj, testString, str);
}


GetMethodID()를 이용하여 java함수를 콜할수 있는데 JvmEnv, jclass, "함수명", 시그니처가 매개변수로 들어간다. aa클래스를 담고있는 jclass와 함게 함수명 "testInt" 그리고 시그니처 (I)I 를 넣어줘야 위의 Java에서 생성한  int testInt(int)를 가져올 수 있다.

String 형 같은 경우에는 기본 변수타입이 아니기때문에 Object형식으로 접근해야한다. Ljava.lang.String;으로 시그니처를 입력해야하는데 주의할 점은 ; 세미클론을 빼먹을 경우 인식을 하지 못한다. 이점 유의해서 사용하길 바란다.


String형과같은 Object형을 함수에서 return시키거나 input시킬때에는 해당 클래스의 임포트 경로를 입력해주면된다. 간단하져?




Posted by 루우지

앞서 C++에서 JNI를 사용하기위해 프로젝트 셋팅작업을 끝냈습니다.

이번에는 간단하게 Java class를 생성하여 클래스안에 있는 메서드를 호출하는 예제를 만들어보겠습니다. 

코드는 어려운게 없으니 천천히 따라오시면 쉽게 구현하실수 있습니다.




1. Hello.class 만들기


public class Hello {
	void sayHello() {
		System.out.println("Say HELLOOO");
	}
}


생성자가없는 Hello.java 파일입니다. 간단하게 sysout으로 인사말을 프린트 하는 메서드입니다. 위의 코드를 코딩하셔서 java파일로 만드시고

javac 명령어를 사용하여 java파일은 class파일로 컴파일 시켜주세요


javac Hello.java


위에서 생성한 Hello.class파일을 C:\Java\src 경로에 넣어주세요! 해당 경로는 c++ 에서 java 클래스파일을 읽어올 경로입니다. c++ 프로젝트 내부에 상대경로로 설정하셔도 됩니다만 저는 C드라이브 밑에  간단하게 테스트용으로 생성했습니다.



2. c++ 코딩


#include 
#include 
#include "stdafx.h"
#include 

using namespace std;

void main() {
	// JavaVM 생성 & JVM 환경설정
	JavaVMOption options;
	JavaVMInitArgs vm_args;
	JNIEnv *env;
	JavaVM *jvm;
	long status;

	options.optionString = "-Djava.class.path=C:\\Java\\src"; // 자바 클래스파일이 있는 경로
	memset(&vm_args, 0, sizeof(vm_args));
	vm_args.version = JNI_VERSION_1_6;
	vm_args.nOptions = 1;
	vm_args.options = &options;
	status = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);

	cout << "JVM 구동!" << endl;
	jclass cls;
	jmethodID mid;
	jobject obj;
	int staticresult = 0;
	int result = 0;

	if (status != JNI_ERR)
	{
		cls = env->FindClass("Hello");		// Hello.class를 찾아 jclass를 생성한다.

		if (cls != 0)
		{
			jmethodID cls_constructor = env->GetMethodID(cls, "", "()V");		// Hello.class의 기본생성자를 찾아 생성자 메소드를 생성한다.
			if (cls_constructor != 0) {
				obj = env->NewObject(cls, cls_constructor, "()V");		// 클래스와 클래스 생성자를 이용해 객체(object)를 생성한다.
				mid = env->GetMethodID(cls, "sayHello", "()V");			// Hello.class에 정의한 sayHello() 메소드를 찾아 JmethodID 담는다.
				env->CallIntMethod(obj, mid);							// 메소드를 호출하는데 GetMethodID 함수를 이용해 위의 JmethodID를 파라메터로 넘겨준다.
			}
		}
		else
		{
			cout << "클래스를 찾을 수 없습니다." << endl;
			return;
		}

		jvm->DestroyJavaVM();
		cout << "JVM 삭제" << endl;
	}
}

}


간단하게 메인함수에서 호출하게 만들어봤습니다. 1편에서 셋팅을 제대로 하셨다면 컴파일 에러가 없으실꺼에요~


Posted by 루우지

JNI란 JAVA에서 NATIVE영역으로 들어가 호출 또는 NATAVIE에서 JAVA로 호출하는 인터페이스를 말한다.

쉽게 말해 JNI는 JAVA와 다른 언어를 연동하는 라이브러리? 솔루션? 이라고 말할 수있다.



JAVA <---> JNI <---> C/C++



이런 형태로 서로 다른 언어에서 메소드(함수)를 호출하여 사용할 수 있는데 기존에 작성된 방대한 C/C++ 라이브러리를 JAVA에서 이용하기 위한 방법으로 사용된다고 한다.


회사 프로젝트로 이번에 JNI를 담당하게 됬는데 생각보다 C++에서 사용하기위해 JAVA함수를 호출하는 예제가 없거나 너무 옛날 글들이 많았다. 

수많은 뻘짓을 통해 성공을 했는데 막상 해보고나니 소스가 어렵다기보단 프로젝트 환경설정해주는거에서 막혔던 부분이였다.


포스팅을 하기 앞서 이번 가이드에서 사용할 환경을 미리 안내하고 시작하려고 한다.


1. Visual Studio 2015

2. JDK 1.8 (32 or 64)


사실 비쥬얼 스튜디오 버전은 크게 상관없을것이다. 문제는 빌드하려는 환경(32비트,64비트)에 맞는 jdk를 설치해줘야 한다는점.




1) JDK 설치 및 환경 변수 셋팅

- 오라클 홈페이지에서 jdk 1.8 버전을 다운로드 받은뒤 설치를 하도록 하자.


http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html


나는 32 / 64비트 모두 필요하기에 전부 받아 설치하였다.

우선 32비트 환경에서 빌드하기 위해 32비트 환경 설정을 하도록 하겠다. 64비트 운영체제에서 32비트 프로그램은C:\Program Files (x86) 에 저장이 된다.



시스템 속성 -> 환경 변수 버튼 클릭





시스템 변수 새로만들기 버튼 클릭





변수 이름 JAVA_HOME 변수 값 JDK 설치 경로 지정 후 확인





시스템 변수 Path 선택하여 편집버튼 클릭





새로 만들기 버튼 클릭하여 %JAVA_HOME%\bin, %JAVA_HOME%\jre\bin\server 작성 후 확인 버튼



여기까지하면 시스템 변수 잡아주는 과정은 끝났다. 제대로 잡혔는지 확인해보고 싶으면 cmd창을 띄운뒤 javac -version 을 입력해보길 바란다.






2) Visual Studio Project 생성 및 속성 수정



Win32 콘솔 어플리케이션으로 프로젝트를 생성한다.





뷰에서 프로젝트 우클릭하여 프로퍼티 속성을 클릭하거나 alt + enter 키를 눌러 속성창을 킨다





좌측 메뉴에서 C/C++ -> General 탭에서 Addtionnal Include Directories에 패스를 입력해준다.




C:\Program Files (x86)\Java\jdk1.8.0_181\include\win32;

C:\Program Files (x86)\Java\jdk1.8.0_181\include;%(AdditionalIncludeDirectories)


위의 2개 경로 입력




Linker -> General 탭에서 Addtional Library Directories 수정


C:\Program Files (x86)\Java\jdk1.8.0_181\lib




Linker -> input

jvm.lib 추가



여기까지가 프로젝트 속성 셋팅이 끝났다

다음 포스팅에서는 간단하게 구현을 해보겠다~


Posted by 루우지

c언어 배열

C 2018.07.22 21:51

1. 배열의 선언과 사용

- 동일한 자료형을 저장할 저장공간이 많이 필요한 경우 일일이 변수를 선언하는것을 비효율적이다. 이때 배열을 사용하여 대체할수 있다.

배열을 사용하기 위해서는 선언을 먼저 해야한다. 배열의 선언은 간단하다. 요소의 자료형에 이름을 붙이고 필요한 요소 수를 표시한다.


int ary[5];


int = 요소 형태

ary = 배열명

[5] = 요소 개수


 

#include<stdio.h>


int main(void)

{

int ary[5];


ary[0] = 10;

ary[1] = 20;

ary[2] = ary[0] + ary[1];

scanf("%d", &ary[3]);


printf("%d\n", ary[2]);

printf("%d\n", ary[3]);

printf("%d\n", ary[4]);


return 0;

}


INT형 변수 5개를 하나씩 선언하는 것과 전체 저장 공간의 크기는 같다. 그러나 메모리할당되는 방식에는 차이가 있다. 변수를 선언하면 각 변수는 독립적인 저장공간을 갖고 각각의 이름으로 사용되는데

int a, b, c, d, e;
a(4byte), b(4byte), c(4byte), d(4byte), e(4byte)


반면에 배열은 저장공간이 연속으로 할당되어 배열명이 전체 공간의 이름이 된다.

int ary[5]; // 메모리 공간이 연속적으로 할당되며 이름은 하나다.

ary(20byte)


int형 변수의 크기가 4바이트이므로 5개를 연속으로 할당하면 총 20바이트를 할당된다. 배열의 나누어진 조각을 배열 요소라 하는데, 각각의 배열 요소는 int형 변수와 똑같이 쓰인다. 배열 요소는 배열명에 첨자(index)를 붙여 표현하며 첨자는 0부터 시작한다.


ary[0], ary[1], ary[2], ary[3], ary[4]



배열을 선언할 때와 배열 요소를 사용할때 대괄호 안의 숫자는 의미가 다른다. 선언할 때는 배열 요소의 전체 개수를 표시하며, 사용할 때는 각 요소가 ㅁ배열에서 몇 번째에 있는지 의미한다. 이값이 첨자며 첨자를 사용할 때는 다음 내용을 반드시 지켜야한다.


배열의 첨자는 0부터 시작하므로 최대 '배열 요소 수 -1'까지 사용가능하다. 

-> 배열의 ary 크기가 5일때 ary[5]를 사용하면? compile error

배열의 요소를 사용할때 첨자의 사용 범위를 벗어나면 그 결과를 예측할 수 없다. 배열에 할당된 영역을 벗어난 메모리를 사용하므로 침범함 영역이 어떤 용도에 사용되느냐에 따라 결과가 달라진다. 







2. 배열의 초기화

- 변수와 마찬가지로 배열도 최초 할당된 저장공간에는 쓰레기값이 있다. 배열이 선언과 동시에 원하는 값을 갖도록 하려면 초기화를 해야 한다.


배열은 중괄호로 묶어서 초기화한다.


단, 반드시 선언과 동시에 초기화해야하며, 선언하고 난 후에 값을 저장하려면 배열 요소에 하나씩 값을 대입해야한다.


int main(void)

{

int ary1[5] = { 1,2,3,4,5 };

int ary2[5] = { 1,2,3 };

int ary3[] = { 1,2,3 };

double ary4[5] = { 1.0, 2.0, 3.0, 4.0, 5.4 };

char ary5[5] = { 'a','b','c','d','e' };


ary1[0] = 10;

ary1[1] = 20;

ary1[2] = 30;

ary1[3] = 40;

ary1[4] = 50;


return 0;

}


초깃값은 첫 번째 요소부터 차례로 초기화된다. ary2의 경우 초깃값이 배열 요소 수보다 적은 경우인데 이대는 다음의 규칙을 따른다

왼쪽에서 차례로 초기화되고 남는 배열의 요소는 모두 0으로 채운다.

이런 자동초기화 기능을 사용하면 배욜 요소 수가 많아도 모든 요소를 쉽게 0으로 초기화 가능하다.







'C' 카테고리의 다른 글

[C언어] 열거형 enum  (0) 2018.08.24
[C언어] 구조체와 공용체 (struct, union)  (0) 2018.08.24
c언어 배열  (0) 2018.07.22
C언어 산술, 관계, 논리연산자  (0) 2018.07.22
C언어 포인터의 크기와 데이터 타입  (0) 2018.07.14
C언어 포인터 기초 개념  (0) 2018.07.14
Posted by 루우지


티스토리 툴바