MrWolf
MrWolf

Reputation: 750

Returning string from C function

I haven't used C in over 3 years, I'm pretty rusty on a lot of things.

I know this may seem stupid but I cannot return a string from a function at the moment. Please assume that: I cannot use string.h for this.

Here is my code:

#include <ncurses.h>

char * getStr(int length) { 
    char word[length];

    for (int i = 0; i < length; i++) {
        word[i] = getch();
    }

    word[i] = '\0';
    return word;
}

int main() {
    char wordd[10];
    initscr();
    *wordd = getStr(10);
    printw("The string is:\n");
    printw("%s\n",*wordd);
    getch();
    endwin();
    return 0;
}

I can capture the string (with my getStr function) but I cannot get it to display correctly (I get garbage).

Help is appreciated.

Upvotes: 62

Views: 229329

Answers (9)

ChanOnly123
ChanOnly123

Reputation: 1034

You can use another thread as garbage collector, here after 1 second it will deallocate. Copy it before it does.

#include <iostream>
#include <thread>

const char* returnCopyCharDelete(const char* string) {
    char* copy = (char*)malloc(strlen(string) + 1);
    strcpy(copy, string);

    std::thread thread([copy]{
        std::this_thread::sleep_for(std::chrono::seconds(1));
        std::cout<<"free: "<<copy<<std::endl;
        free(copy);
    });
    thread.detach();

    return copy;
}

Use it when returning

const char* returnCString() {
    std::string string("hello ");
    string.append("world");
    return returnCopyCharDelete(string.c_str());
}

int main(int argc, const char * argv[]) {

    std::string result(returnCString());
    std::cout<<"copied: "<<result<<std::endl;

    std::this_thread::sleep_for(std::chrono::seconds(3));

    std::cout<<result<<std::endl;

    return 0;
}

Output

copied: hello world
free: hello world
hello world

Upvotes: 0

Jac Goudsmit
Jac Goudsmit

Reputation: 111

As others already said, you can't return a non-constant string in a useful way without allocating it on the heap (e.g. using strdup). But in all recent versions of the C standard (C89 and later if I'm not mistaken) you can return a struct. It won't be necessary for the caller to deallocate the result because it's not on the heap. And it's thread-safe.

#include <stdio.h>

struct stringbuf
{
  char buf[40];
};

struct stringbuf getanswer(int i)
{
  struct stringbuf result = { 0 };

  snprintf(result.buf, sizeof(result.buf), "The answer is %d", i);

  return result;
}

int main(int argc, char **argv)
{
  /*
   * Remember to pass the .buf member, not the struct, to functions
   * such as printf which expect a character pointer as argument!
   * Passing the result of getanswer in the next line without .buf
   * appended, will likely crash the program because the program
   * will put the entire struct on the stack, not a character
   * pointer, and will make printf interpret the first few bytes
   * of the string as a pointer. That would be bad.
   */
  printf("How many arguments did I get? %s\n", getanswer(argc).buf);
  return 0;
}

Note: To keep the sample code as simple and focused as possible, I simply declared a struct type without typedef. You may save yourself a lot of typing by using typedef and returning the defined type.

There are (arguably) a few disadvantages:

  • A function that returns a struct cannot return NULL.
  • The size of the buffer in the struct is fixed because the compiler has to know the size of the return type at compile time.
  • The result of a function that returns a struct is probably stored on the stack; this may be a problem in small systems (like microcontrollers) that don't have a lot of stack space.
  • Unlike character arrays, an instance of a struct is not a usable alias for the string that's stored in it. In other words, whereas you can create an array of characters and use its name as a pointer to the first character, you can't use the name of a struct as a pointer.

That last point is important because you have to keep in mind that a struct with a character array is not the same as the array itself. So if you want to call a string function, you should pass the string member variable, not a pointer to the struct. This is especially important for functions with variadic arguments such as printf and friends where a compiler may not warn you if you're doing it wrong: passing a struct will place the entire struct on the stack, not just a pointer to the first character. Printf will interpret the first few characters in the struct as a character pointer, which will certainly be invalid.

Yes, it's possible to cast a pointer to a struct to a char * and pass it to a string function (including printf) and that will work correctly, but I would argue that it's bad practice to do this: If you (or someone else) ever decides to put another member variable in the struct declaration in front of the string buffer, any use of a typecast pointer to a struct that assumes that the string buffer starts where the struct starts, would silently fail. You probably want to avoid this, so use a pointer to the string member variable even if it's somewhat inconvenient.

===Jac

Upvotes: 7

John 9631
John 9631

Reputation: 577

I came across this thread while working on my understanding of Cython. My extension to the original question might be of use to others working at the C / Cython interface. So this is the extension of the original question: how do I return a string from a C function, making it available to Cython & thus to Python?

For those not familiar with it, Cython allows you to statically type Python code that you need to speed up. So the process is, enjoy writing Python :), find its a bit slow somewhere, profile it, calve off a function or two and cythonize them. Wow. Close to C speed (it compiles to C) Fixed. Yay. The other use is importing C functions or libraries into Python as done here.

This will print a string and return the same or another string to Python. There are 3 files, the c file c_hello.c, the cython file sayhello.pyx, and the cython setup file sayhello.pyx. When they are compiled using python setup.py build_ext --inplace they generate a shared library file that can be imported into python or ipython and the function sayhello.hello run.

c_hello.c

#include <stdio.h>

char *c_hello() {
  char *mystr = "Hello World!\n";
  return mystr;
  // return "this string";  // alterative
}

sayhello.pyx

cdef extern from "c_hello.c":
    cdef char* c_hello()

def hello():
    return c_hello()

setup.py

from setuptools import setup
from setuptools.extension import Extension
from Cython.Distutils import build_ext
from Cython.Build import cythonize


ext_modules = cythonize([Extension("sayhello", ["sayhello.pyx"])])


setup(
name = 'Hello world app',
cmdclass = {'build_ext': build_ext},
ext_modules = ext_modules
)

Upvotes: 2

Colin Weinshenker
Colin Weinshenker

Reputation: 26

Easier still: return a pointer to a string that's been malloc'd with strdup.

#include <ncurses.h>

char * getStr(int length)
{   
    char word[length];

    for (int i = 0; i < length; i++)
    {
        word[i] = getch();
    }

    word[i] = '\0';
    return strdup(&word[0]);
}

int main()
{
    char wordd[10];
    initscr();
    *wordd = getStr(10);
    printw("The string is:\n");
    printw("%s\n",*wordd);
    getch();
    endwin();
    return 0;
}

Upvotes: 1

michaelmeyer
michaelmeyer

Reputation: 8215

Either allocate the string on the stack on the caller side and pass it to your function:

void getStr(char *wordd, int length) {
    ...
}

int main(void) {
    char wordd[10 + 1];
    getStr(wordd, sizeof(wordd) - 1);
    ...
}

Or make the string static in getStr:

char *getStr(void) {
    static char wordd[10 + 1];
    ...
    return wordd;
}

Or allocate the string on the heap:

char *getStr(int length) {
    char *wordd = malloc(length + 1);
    ...
    return wordd;
}

Upvotes: 95

Arpit
Arpit

Reputation: 775

Your pointer is pointing to local variable of the function. So as soon as you return from the function, memory gets deallocated. You have to assign memory on heap in order to use it in other functions.

Instead char *rtnPtr = word;

do this char *rtnPtr = malloc(length);

So that it is available in the main function. After it is used free the memory.

Upvotes: 5

Brian Campbell
Brian Campbell

Reputation: 333314

You are allocating your string on the stack, and then returning a pointer to it. When your function returns, any stack allocations become invalid; the pointer now points to a region on the stack that is likely to be overwritten the next time a function is called.

In order to do what you're trying to do, you need to do one of the following:

  1. Allocate memory on the heap using malloc or similar, then return that pointer. The caller will then need to call free when it is done with the memory.
  2. Allocate the string on the stack in the calling function (the one that will be using the string), and pass a pointer in to the function to put the string into. During the entire call to the calling function, data on its stack is valid; its only once you return that stack allocated space becomes used by something else.

Upvotes: 7

John3136
John3136

Reputation: 29266

word is on the stack and goes out of scope as soon as getStr() returns. You are invoking undefined behavior.

Upvotes: 2

nneonneo
nneonneo

Reputation: 179717

char word[length];
char *rtnPtr = word;
...
return rtnPtr;

This is not good. You are returning a pointer to an automatic (scoped) variable, which will be destroyed when the function returns. The pointer will be left pointing at a destroyed variable, which will almost certainly produce "strange" results (undefined behaviour).

You should be allocating the string with malloc (e.g. char *rtnPtr = malloc(length)), then freeing it later in main.

Upvotes: 19

Related Questions