Ctypes returns string from c function

I am a Python veteran, but did not type much in C. In the afternoon, when I did not find anything on the Internet that worked for me, I thought I would ask here and get the help that I need.

What I want to do is write a simple C function that takes a string and returns another string. I plan to link this function in several languages ​​(Java, Obj-C, Python, etc.), so I think it should be pure C?

Here is what I still have. Notice that I get segfault when I try to get the value in Python.

hello.c

#include <stdlib.h> #include <stdio.h> #include <string.h> const char* hello(char* name) { static char greeting[100] = "Hello, "; strcat(greeting, name); strcat(greeting, "!\n"); printf("%s\n", greeting); return greeting; } 

main.py

 import ctypes hello = ctypes.cdll.LoadLibrary('./hello.so') name = "Frank" c_name = ctypes.c_char_p(name) foo = hello.hello(c_name) print c_name.value # this comes back fine print ctypes.c_char_p(foo).value # segfault 

I read that segfault is caused by C freeing up the memory that was originally allocated for the returned string. Maybe I'm just barking the wrong tree?

What is the right way to accomplish what I want?

+11
c python ctypes
source share
4 answers

n hello.c you are returning a local array. You should return a pointer to an array that should be dynamically declared using malloc.

 char* hello(char* name) { char hello[] = "Hello "; char excla[] = "!\n"; char *greeting = malloc ( sizeof(char) * ( strlen(name) + strlen(hello) + strlen(excla) + 1 ) ); if( greeting == NULL) exit(1); strcpy( greeting , hello); strcat(greeting, name); strcat(greeting, excla); return greeting; } 
+6
source share

Your problem is that the greeting was placed on the stack, but the stack is destroyed when the function returns. You can allocate memory dynamically:

 #include <stdlib.h> #include <stdio.h> #include <string.h> const char* hello(char* name) { char* greeting = malloc(100); snprintf("Hello, %s!\n", 100, name) printf("%s\n", greeting); return greeting; } 

But this is only part of the battle, because now you have a memory leak. You can hook this up with another ctypes call for free ().

... or a much better approach is to read the official C binding to python (python 2.x at http://docs.python.org/2/c-api/ and python 3.x at http: //docs.python. org / 3 / c-api / ). Have your C function create a python string object and pass it back. It will be a Python garbage collector automatically. Since you write on the C side, you do not need to play the ctypes game.

...edit..

I have not compiled or tested, but I think .py will work:

 import ctypes # define the interface hello = ctypes.cdll.LoadLibrary('./hello.so') # find lib on linux or windows libc = ctypes.CDLL(ctypes.util.find_library('c')) # declare the functions we use hello.hello.argtypes = (ctypes.c_char_p,) hello.hello.restype = ctypes.c_char_p libc.free.argtypes = (ctypes.c_void_p,) # wrap hello to make sure the free is done def hello(name): _result = hello.hello(name) result = _result.value libc.free(_result) return result # do the deed print hello("Frank") 
+15
source share

This is what happens. And why does it break. When hello () is called, the stack pointer C moves up, freeing up space for any memory your function needs. Along with some overhead function calls, all of your local function functions are managed there. Thus, static char greeting[100] means that 100 bytes of the increased stack are used for this line. You, than to use some functions which manage this memory. Place a pointer to the stack in the welcome memory. And then you go back from the call, after which the stack pointer goes back to the original to the call position. Thus, the 100 bytes that were on the stack at the time of your call are essentially used again for capture since the stack is manipulated even more. Including an address field indicating this value and returned by you. At this moment, who knows what is happening to him, but he is probably set to zero or some other value. And when you try to access it as if it is still a viable memory, you get segfault.

To get around, you need to somehow manage this memory. You can use your alloc function in heap memory, but you will need to make sure it gets free() 'ed later, according to your binding. OR, you can write your function so that the binding language gives it the memory verb that will be used.

+2
source share

I ran into the same problem today and found that you should override the default return type ( int ) by setting restype for the method. See Return Types in the ctype document here .

 import ctypes hello = ctypes.cdll.LoadLibrary('./hello.so') name = "Frank" c_name = ctypes.c_char_p(name) hello.hello.restype = ctypes.c_char_p # override the default return type (int) foo = hello.hello(c_name) print c_name.value print ctypes.c_char_p(foo).value 
0
source share

All Articles