The C Programming Language Usage Guide
Some basic concepts about the programming language C
31. Mar 2024 toc: disabled view: slimTips
- Use
void
in functions without parameters. For example:int main(void) { ... }
- Use
#pragma once
at the beginning of a header file to prevent multiple inclusion. - Use the compiler flags
-Wall
,-Wextra
and-Werror
for additional hints. - Use the compiler flag
-g
to be able to debug your program with gdb. - Use the compiler flag
-rdynamic
the keep function names in binary exectuables.
Error Handling
Signal Handling
Create a function which handles the signal.
void handle_sigint() {
// do nothing
}
Register the signal handler with sigaction()
.
struct sigaction sigint_action;
sigint_action.sa_handler = handle_sigint;
sigint_action.sa_flags = 0;
sigemptyset(&sigint_action.sa_mask);
sigaction(SIGINT, &sigint_action, NULL);
Segfault Backtracing
Create a function which handles the signal.
Register the signal handler with sigaction()
to the SIGSEGV
signal.
Specify a list of a certain length which will later correspond to the levels of backtracing.
Print the backtrace with backtrace_symbols_fd()
and call exit
to stop the program.
void handle_segfault(int signal) {
void *list[10];
size_t size = backtrace(list, 10);
fprintf(stderr, "ERROR: signal %d:\n", signal);
backtrace_symbols_fd(list, size, STDERR_FILENO);
exit(1);
}
The compiler flag -rdynamic
is required in order to view the function names in the backtrace.
Localization
setlocale(LC_ALL, "de_DE.UTF-8");
Make sure you have the desired language installed on your system.
Use a custom shared library
The shared library gets linked during runtime.
A shared library should start with the prefix lib
.
- Copy the header file to
/usr/local/include/
. - Compile the shared library with
cc -o libtest -shared -fPIC libtest.c
. - Copy the shared library to
/usr/local/lib/
. - Add the folder to
/etc/ld.so.conf
if the entry doesn't already exist. - Run
ldconfig
to load the library. Check withldconfig -p | grep libterm
if the library was loaded correctly. - Include the library with
#include <libtest.h>
. - Compile your application with
cc -o main -ltest main.c
. Runldd main
to see if the library is listed as dependency.
The following Makefile show how to create simple install/uninstall options:
Makefile
default: build
build:
cc -o libtest -shared -fPIC libtest.c
install: build
sudo cp libtest.h /usr/local/include/
sudo cp libtest.so /usr/local/lib/
sudo ldconfig
rm libtest.so
uninstall:
sudo rm /usr/local/include/libtest.h
sudo rm /usr/local/lib/libtest.so
clean:
rm libtest.so
Java Native Interface
The JNI (Java™ Native Interface) allows us to implement native methods in the programming language C.
Install the JNI header files by symlinking jni.h
and linux/jni_md.h
to /usr/local/include
.
ln -s /usr/lib/jvm/java-<version>-openjdk/include/jni.h jni.h
ln -s /usr/lib/jvm/java-<version>-openjdk/include/linux/jni_md.h jni_md.h
The following code snippet serves as an example to demonstrate the implementation of native methods in the programming language C.
public class Example {
static {
System.loadLibrary("native");
}
public static void main(String[] args) {
final Example example = new Example();
final int sum = example.sum(3, 5);
System.out.println("sum: " + sum);
}
private native int sum(int a, int b);
}
There are two important things to notice in this snippet. First, we have a static block which loads the library native
. This will be the name of our shared library we will later create. Keep in mind the name should be the same as the filename (libnative.so
). And second, the method sum
has the modifier native
which tells the JVM this method is a native implementation.
The next step is to create a header file we can later use to implement out our native method in C. In order to create a header file from our Java files we can use the following command.
javac -h . Example.java
After that, we have to new files, an Example.class
file and an Example.h
file. If we look into the the header file we can see the following method definition.
/*
* Class: Example
* Method: sum
* Signature: (II)I
*/
JNIEXPORT jint JNICALL Java_Example_sum
(JNIEnv *, jobject, jint, jint);
Now we can implement our sum
method by creating a new Example.c
file, including the header file and writing the following implementation.
#include "Example.h"
JNIEXPORT jint JNICALL Java_Example_sum(JNIEnv *env, jobject this, jint a, jint b) {
return a + b;
}
We are all set and can now create our shared library with the following command which will generate our libnative.so
file.
cc -o libnative.so -shared -fPIC Example.c
After that, we can execute our Java program by passing the directory location of our shared library to the java
command.
java -Djava.library.path=./ Example.java
Now you should see the output sum: 8
on your screen. Congrats, you implemented a Java method in the programming language C!
You can find more information about implementing native methods with C and JNI here.
Libraries
- curl: HTTP Client library
- libpq-fe: Postgres Client library
- openssl: SSL library
- cmark: Markdown library
- toml: TOML Parser libarry
Tools
- man: View manuals for a specific topic
- xxd: View binary files as hex
- ldd: View binary dependecies
- file: View file types