I don't know anything about Kotlin, but given the C structure described here's a C DLL with that structure and the Python code to call it. The structures need to be derived from ctypes.Structure and the return type of the function needs to be declared via .restype and not left to the default of (usually 32-bit) integer. Casting the return value after-the-fact is too late if it was meant to be a 64-bit pointer.

test.c

#ifdef _WIN32
#   define API __declspec(dllexport)
#else
#   define API
#endif

typedef struct {
  struct {
    struct {
        int a;
    } root;
  } kotlin;
} libnative_ExportedSymbols;

libnative_ExportedSymbols g_lib = {7};

API libnative_ExportedSymbols* libnative_symbols(void) {
    return &g_lib;
}

test.py

import ctypes as ct

class Root(ct.Structure):
    _fields_ = ('a', ct.c_int),

class Kotlin(ct.Structure):
    _fields_ = ('root', Root),

class Symbols(ct.Structure):
    _fields_ = ('kotlin', Kotlin),

dll = ct.CDLL('./test')
dll.libnative_symbols.argtypes = ()
dll.libnative_symbols.restype = ct.POINTER(Symbols)

lib = dll.libnative_symbols()
print(lib.contents.kotlin.root.a)

Output:

7
Answer from Mark Tolonen on Stack Overflow
🌐
University of Chicago
people.cs.uchicago.edu › ~amr › 122 › labs › cstructs.html
CMSC 12200 - Structs in C and bridging with Python using CTypes - Februrary 2, 2011
Return a pointer of type "struct Rect *" myrect->width = 5; //use -> to access and set fields myrect->height = 3; We defined a pointer to a struct Rect. Allocated enough space to store one. And then set the fields of the structure using the arrow "->" syntax. This process is identical to what happens in Java when we say · //JAVA CODE Rect myrect; //Define a pointer myrect = new Rect(); //Allocate space with "new" instead of "malloc" myrect.width = 5; myrect.height = 3; It is often advisable to make a constructor function to do the allocating for us.
Top answer
1 of 2
2

I don't know anything about Kotlin, but given the C structure described here's a C DLL with that structure and the Python code to call it. The structures need to be derived from ctypes.Structure and the return type of the function needs to be declared via .restype and not left to the default of (usually 32-bit) integer. Casting the return value after-the-fact is too late if it was meant to be a 64-bit pointer.

test.c

#ifdef _WIN32
#   define API __declspec(dllexport)
#else
#   define API
#endif

typedef struct {
  struct {
    struct {
        int a;
    } root;
  } kotlin;
} libnative_ExportedSymbols;

libnative_ExportedSymbols g_lib = {7};

API libnative_ExportedSymbols* libnative_symbols(void) {
    return &g_lib;
}

test.py

import ctypes as ct

class Root(ct.Structure):
    _fields_ = ('a', ct.c_int),

class Kotlin(ct.Structure):
    _fields_ = ('root', Root),

class Symbols(ct.Structure):
    _fields_ = ('kotlin', Kotlin),

dll = ct.CDLL('./test')
dll.libnative_symbols.argtypes = ()
dll.libnative_symbols.restype = ct.POINTER(Symbols)

lib = dll.libnative_symbols()
print(lib.contents.kotlin.root.a)

Output:

7
2 of 2
0

Basically this other answer, but closer to the question:

import ctypes

class Root(ctypes.Structure):
    _fields_ = [("foo", ctypes.c_bool)]

class Kotlin(ctypes.Structure):
    _fields_ = [("root", Root)]

class Libnative_ExportedSymbols(ctypes.Structure):
    _fields_ = [("kotlin", Kotlin)]

libPath = "path/to/libnative.dll"
dll = ctypes.CDLL(libPath)
dll.libnative_symbols.restype = ctypes.POINTER(Libnative_ExportedSymbols)
libnative = dll.libnative_symbols().contents
print(type(libnative.kotlin.root)) # <class '__main__.Root'>
print(libnative.kotlin.root) # <__main__.Root object at ...

Note that the following approach without POINTER would also work, but may cause errors down the road:

dll.libnative_symbols.restype = Libnative_ExportedSymbols
libnative = dll.libnative_symbols()
print(type(libnative.kotlin.root)) # <class '__main__.Root'>
print(libnative.kotlin.root) # <__main__.Root object at ...
Discussions

python - How to return a pointer to a structure in ctypes? - Stack Overflow
I assume you want to pass a parameter of your python-declared foo, which is an instance of a struct FOO_, into the C bar(int) and get back a pointer to struct FOO_. More on stackoverflow.com
🌐 stackoverflow.com
How to access data from pointer in struct from Python with ctypes? - Stack Overflow
I knew about this, but for some ... into a pointer to a different ctypes data type"). Thank you! 2014-07-24T06:57:08.693Z+00:00 ... Find the answer to your question by asking. Ask question ... See similar questions with these tags. ... New site design and philosophy for Stack Overflow: Starting February 24, 2026... I’m Jody, the Chief Product and Technology Officer at Stack Overflow. Let’s... 2 How does ctypes map C struct members to Python class _fields_ ... More on stackoverflow.com
🌐 stackoverflow.com
struct - C-like structures in Python - Stack Overflow
Is there a way to conveniently define a C-like structure in Python? I'm tired of writing stuff like: class MyStruct(): def __init__(self, field1, field2, field3): self.field1 = field1 ... More on stackoverflow.com
🌐 stackoverflow.com
Accessing struct data through a pointer in a Python C extension - Stack Overflow
So I'm trying to create a C extension and am having a bit of trouble accessing the struct data for one of my arguments. Here's my current code: #define PY_SSIZE_T_CLEAN #include #i... More on stackoverflow.com
🌐 stackoverflow.com
🌐
SageMath
doc.sagemath.org › html › en › thematic_tutorials › numerical_sage › ctypes_examples.html
More complicated ctypes example - Thematic Tutorials
Note that in case you need to refer to a struct before it is completely defined (as in the linked list) you can first declare it with “Pass”, and then specify the field contents as above. Also note the POINTER operator which creates a pointer out of any ctypes type.
🌐
Medium
medium.com › @datasciencefilmmaker › my-god-its-full-of-stars-2-d75d01d2cb94
My God, It’s Full of Stars (2/7) — Accessing C Structures in Python Using Ctypes | by Data Science Filmmaker | Medium
January 18, 2024 - You can now access the attributes of that structure the same way you would any other Python class. For instance, to get the M_wd_up: ... We need “.contents” because “pCluster” is a pointer to a cluster structure, not the structure itself. This pointer can now be treated in our Python code just like any other Python object.
🌐
CodeRivers
coderivers.org › blog › python-c-struct
Python C Struct: Bridging Python and C Data Representations - CodeRivers
January 29, 2025 - When your C code expects a struct pointer, ctypes.Structure maps directly to the C memory layout without needing struct.pack:
Find elsewhere
🌐
Ifnamemain
ifnamemain.com › posts › 2013 › Dec › 10 › c_structs_python
C Structs and Python - ifnamemain
We act on the data in the struct using the -> syntax as we are dealing with pointers. The shared library test_struct.so is compiled in the same manner as before, without the need to explicitly point to the local header file. To declare the struct in Python requires the creation of new class based on the ctypes.Structure object :
🌐
Stack Overflow
stackoverflow.com › questions › 35988 › c-like-structures-in-python
struct - C-like structures in Python - Stack Overflow
Is there a way to conveniently define a C-like structure in Python? I'm tired of writing stuff like: class MyStruct(): def __init__(self, field1, field2, field3): self.field1 = field1 ...
🌐
Stack Overflow
stackoverflow.com › questions › 73448553 › accessing-struct-data-through-a-pointer-in-a-python-c-extension
Accessing struct data through a pointer in a Python C extension - Stack Overflow
You're clear, it was my own mistake. ... You expect too much from PyArg_ParseTuple: its implementation knows nothing about the Point-struct which is defined by you - it has no idea how a tuple can be converted to a Point struct.
🌐
Samuelstevens
samuelstevens.me › writing › optimizing-python-code-with-ctypes
Optimizing Python Code with ctypes
The only field is _fields_, which is a list of tuples. Each tuple is (<variable-name>, <ctypes.TYPE>). ctypes has types like c_char (char), and c_char_p (*char). ctypes also includes POINTER() which creates a pointer type from any type passed to it. If you have a recursive definition like in ...
🌐
Python
docs.python.org › 3 › library › ctypes.html
ctypes — A foreign function library for Python
This means, if you have POINTER(c_int) in the argtypes list of a function or as the type of a member field in a structure definition, only instances of exactly the same type are accepted. There are some exceptions to this rule, where ctypes accepts other objects.
🌐
W3Schools
w3schools.com › c › c_structs_pointers.php
C Structs and Pointers
C Examples C Real-Life Examples ... Q&A ... You can use pointers with structs to make your code more efficient, especially when passing structs to functions or changing their values....
🌐
TutorialsPoint
tutorialspoint.com › cprogramming › c_pointers_to_structures.htm
Pointers to Structures in C
If you have defined a derived data type using the keyword struct, then you can declare a variable of this type. Hence, you can also declare a pointer variable to store its address.
🌐
NumPy
numpy.org › doc › stable › reference › c-api › types-and-structures.html
Python types and C-structures — NumPy v2.3 Manual
June 29, 2020 - When you receive a Python object into C code, you always get a pointer to a PyObject structure. Because a PyObject structure is very generic and defines only PyObject_HEAD, by itself it is not very interesting.
🌐
dbader.org
dbader.org › blog › python-ctypes-tutorial-part-2
Interfacing Python and C: Advanced “ctypes” Features – dbader.org
November 7, 2017 - Notice that we can access the x and y attributes of the Point class in Python in the __repr__ function. We can also pass the Point directly to the show_point function in the C library. Ctypes uses the _fields_ map to manage the conversions automatically for you. Care should be taken with using the _fields_ attribute, however. We’ll look at this in a little more detail in the nested structures section below.
🌐
Programiz
programiz.com › c-programming › c-structures-pointers
C structs and Pointers (With Examples)
#include <stdio.h> struct person { int age; float weight; }; int main() { struct person *personPtr, person1; personPtr = &person1; printf("Enter age: "); scanf("%d", &personPtr->age); printf("Enter weight: "); scanf("%f", &personPtr->weight); printf("Displaying:\n"); printf("Age: %d\n", personPtr->age); printf("weight: %f", personPtr->weight); return 0; } In this example, the address of person1 is stored in the personPtr pointer using personPtr = &person1;. Now, you can access the members of person1 using the personPtr pointer.
🌐
NumPy
numpy.org › doc › 1.16 › reference › c-api.types-and-structures.html
Python Types and C-Structures — NumPy v1.16 Manual
February 18, 2020 - When you receive a Python object into C code, you always get a pointer to a PyObject structure. Because a PyObject structure is very generic and defines only PyObject_HEAD, by itself it is not very interesting.
🌐
tentacles666
tentacles666.wordpress.com › 2012 › 01 › 21 › python-ctypes-dereferencing-a-pointer-to-a-c
Python ctypes: dereferencing a pointer to a C structure | tentacles666
March 19, 2013 - First, you need to define a Python class that represents struct foo: class Foo (ctypes.Structure): _fields_ = [ ("bar", c_int) ] Next, you need to tell ctypes that newfoo() returns a pointer to such a thing: