ALWAYS set .argtypes and .restype for your functions to save headache. ctypes can verify the arguments are passed correctly.
Example:
test.c
#include <stdio.h>
__declspec(dllexport) // required for exporting a function on Windows
void test_print(char* test) // decays to pointer, so char test[100] is misleading.
{
printf("%s", test);
}
test.py
from ctypes import *
libcdll = CDLL("./test")
libcdll.test_print.argtypes = c_char_p, # for (char*) arguments, comma makes a tuple
libcdll.test_print.restype = None # for void return
libcdll.test_print(b"test")
output:
test
If called with "test" as in the OP question, now it will tell you the parameter is wrong:
Traceback (most recent call last):
File "C:\test.py", line 5, in <module>
libcdll.test_print("test")
ctypes.ArgumentError: argument 1: <class 'TypeError'>: wrong type
Without .argtypes, ctypes defaults to converting "test" from a Python str to a wchar_t* encoded as UTF-16LE on Windows, so it would look like the following, and printf will stop at the first null byte (\x00), explaining t as the output.
>>> 'test'.encode('utf-16le')
b't\x00e\x00s\x00t\x00'
Note that if you want to pass Python str instead of bytes, declare the C function as the following and use .argtypes = c_wchar_p, instead:
void test_print(wchar_t* test) {
wprintf(L"%s", test); // wide version of printf and format string
}