My Profile Photo

Deniz G


Bilingual Blog: Turkish and English on Software and Life.


Run C program on Java

I will make a confession. Although I am an OCA/OCP Java programmer, I just learned what native methods are.

One day, I wondered about the implementation of hashCode in the Object class and saw that it has no implementation and it uses the native keyword. The native keyword specifies that a method is implemented in another language, not Java. Platform dependent native code (compiled code) is dynamically called by Java and the result of these methods is returned to Java. This feature is called JNI (Java Native Interface). So somewhere in the JVM, there is a hashCode code written in non-Java language (probably C/C++). So I will try to write C code and run it in Java.

Writing C

I will implement stack in the sample C program. This program will includes 2 parts; stack header file which includes method definitions, stack c file which includes header implementation.

Stack.h

#ifndef STACK_H_
#define STACK_H_

void push(int data);
void pop();
void display();

#endif

Stack.c

#include <stdio.h>
#include <stdlib.h>
#include "Stack.h"

struct node {
    int info;
    struct node *ptr;
} *top;

void push(int data) {
    struct node* temp = (struct node*) malloc(1 * sizeof(struct node));
	if (top == NULL) {
    	top = temp;
        top->ptr = NULL;
        top->info = data;
    }
    else {
        temp->ptr = top;
        temp->info = data;
        top = temp;
    }
}

void pop() {
    struct node *temp = top;
    if (temp == NULL) {
        return;
    }
    else {
    	temp = temp->ptr;
	}
    free(top);
    top = temp;
}

void display() {
    struct node *temp = top;
    if (temp == NULL) {
        printf("stack is empty\n");
        return;
    }
    while (temp != NULL) {
        printf("%d -> ", temp->info);
        temp = temp->ptr;
    }
    printf("NULL\n");
}

Let’s test stack implementation by gcc -o stack_app Main.c Stack.c then stack_app.exe command.

Main.c

#include "Stack.h"

int main() {
    display();
    push(4);
    push(1);
    push(6);
    display();
    pop();
    display();
    pop();
    pop();
    display();
    push(11);
    display();
}

cconsoleoutput

Writing Java and Native Methods

Let’s create native methods in our Java class. JNI will be bridge between our Java codes and C native codes. Static code block will loads native codes (we will create shared lib files later) from the file system into memory, so Stack class is able to use native methods when it needs.

Stack.java

public class Stack {

    static {
        System.loadLibrary("native-stack");
    }

    public native void push(int val);

    public native void pop();

    public native void display();

}

We will compile Stack class by using -h flag (javac -h . Stack.java), so it will create Stack.h header file which includes all native method definitions.

Stack.h

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class Stack */

#ifndef _Included_Stack
#define _Included_Stack
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     Stack
 * Method:    push
 * Signature: (I)V
 */
JNIEXPORT void JNICALL Java_Stack_push
  (JNIEnv *, jobject, jint);

/*
 * Class:     Stack
 * Method:    pop
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_Stack_pop
  (JNIEnv *, jobject);

/*
 * Class:     Stack
 * Method:    display
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_Stack_display
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif

We will create Stack.c file for native method implementation of
Stack.h file generated by compiler. We will use the contents of the Stack.c file we created earlier. The only difference is that we will replace the old method definitions with new ones in the new Stack.h file.

Stack.c

#include <stdio.h>
#include <stdlib.h>
#include "Stack.h"

struct node {
    int info;
    struct node *ptr;
} *top;

JNIEXPORT void JNICALL Java_Stack_push
  (JNIEnv* env, jobject thisObject, jint val) {
	struct node* temp = (struct node*) malloc(1 * sizeof(struct node));
	if (top == NULL) {
    	top = temp;
        top->ptr = NULL;
        top->info = val;
    }
    else {
        temp->ptr = top;
        temp->info = val;
        top = temp;
    }
}

JNIEXPORT void JNICALL Java_Stack_pop
  (JNIEnv* env, jobject thisObject) {
    struct node *temp = top;
    if (temp == NULL) {
        return;
    }
    else {
    	temp = temp->ptr;
	}
    free(top);
    top = temp;
}

JNIEXPORT void JNICALL Java_Stack_display
  (JNIEnv* env, jobject thisObject) {
	struct node *temp = top;
    if (temp == NULL) {
        printf("stack is empty\n");
        return;
    }
    while (temp != NULL) {
        printf("%d -> ", temp->info);
        temp = temp->ptr;
    }
    printf("NULL\n");
}

Compiling

Now, we will build our shared library (native-stack. remember static code block in java) from the C codes. The Stack.h file generated by compiler uses jni.h file. This file comes from JDK, and we will need to include this file when build shared library.

jdkjniheader

gcc -c -I%JAVA_HOME%\include -I%JAVA_HOME%\include\win32 Stack.c -o Stack.o

gcc -shared native-stack.dll Stack.o -Wl,--add-stdcall-alias

Let’s test our Stack Java class.

Main.java

public class Main {

    public static void main(String[] args) {
        Stack stack = new Stack();

        stack.display();
        stack.push(4);
        stack.push(1);
        stack.push(6);
        stack.display();
        stack.pop();
        stack.display();
        stack.pop();
        stack.pop();
        stack.display();
        stack.push(11);
        stack.display();

        System.out.println("java program");
    }

}

Run Main class

java -cp . -Djava.library.path=C:/libs/my-dll-path-dir Main.java

javaconsoleoutput