56

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.

M.M
  • 138,810
  • 21
  • 208
  • 365
MrWolf
  • 690
  • 1
  • 6
  • 6
  • 4
    You can use `ncurses.h` but not `string.h`? What a strange environment... – nneonneo Sep 12 '14 at 00:35
  • 2
    You create a variable length array `char word[length];` (**security problem**), then assign its address to `rtnPtr` -- the function ends destroying the local char array `word[length]`. You are returning a pointer to something that ceases to exist. – David C. Rankin Sep 12 '14 at 00:41
  • @nneonneo: It's more of a pre-requisite for this assignment. I'm just not supposed to use it for this. – MrWolf Sep 12 '14 at 01:11
  • Why can't you use `string.h`? Is there a problem with `string.h`? – Box Box Box Box Mar 14 '16 at 10:43
  • 1
    Possible duplicate of [Returning C string from a function](http://stackoverflow.com/questions/1496313/returning-c-string-from-a-function) – Box Box Box Box Mar 14 '16 at 10:44
  • 1
    @AshishAhujaツ: It was one of the requirements for an assignment in a course back in 2014. It was meant to be challenging and the professor thought it would be too easy to simply use string.h. I hope this clears it up for people asking the same question. Cheers. – MrWolf Mar 15 '16 at 16:08
  • @MrWolf, got it. just for next time, mention why you can't use something in the question itself. That will make it clearer. – Box Box Box Box Mar 16 '16 at 01:09
  • 1
    @AshishAhujaツ: That information is irrelevant. The requirement is there and that is absolutely all that matters. ;) – MrWolf Mar 17 '16 at 16:03
  • @MrWolf, okay, I agree it is not relevant. But actually it is a matter of opinion. I always make sure to give reasons in my questions, which tell why I want to do what, and why I don't want to do what. It makes the question clearer. – Box Box Box Box Mar 18 '16 at 01:27

8 Answers8

89

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;
}
michaelmeyer
  • 7,985
  • 7
  • 30
  • 36
  • 12
    if you do malloc the caller should call free on the pointer to release memory – Ahmed Aug 11 '16 at 23:30
  • 2
    This is a dumb question, but do you need to return the string the caller passed in? I'm modifying it directly, so I don't see why I have to return it, right? – MarcusJ Aug 25 '16 at 01:33
  • 9
    You should point out the downside of the second approach, which is that the static buffer will be overwritten on the next call, so the method is not thread safe, and also produces unexpected results even in single-threaded code if the user ends up calling the method again while still having a reference to the first string. – BeeOnRope Jan 01 '17 at 22:48
  • 4
    ... I think you should also point out that the third method requires the caller to deallocated the string via `free` as part of the API. – BeeOnRope Jan 01 '17 at 22:48
  • 2
    @michaelmeyer How to free the memory when you allocate on heap? – J...S Sep 28 '17 at 01:42
  • @J...S: For the third approach (allocate the string on the heap), you would later have to use `free()` to deallocate the string's memory from the heap, perhaps in the function that called `getStr()`. (This is addressed in the answers to this question by @nneonneo and @BrianCampbell, below.) – jvriesem Mar 04 '20 at 15:38
17
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.

nneonneo
  • 171,345
  • 36
  • 312
  • 383
7

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.
Brian Campbell
  • 322,767
  • 57
  • 360
  • 340
5

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.

Arpit
  • 767
  • 7
  • 20
5

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

Jac Goudsmit
  • 91
  • 2
  • 5
  • error C2371: “stime”: 重定义;不同的基类型 – CS QGB Jul 24 '22 at 03:46
  • 1
    @csqgb I don't understand your language but that compiler error means that you defined a variable or function (our something else) called "stime" and that identifier is already declared elsewhere. Simply rename your variable to something else. – Jac Goudsmit Jul 25 '22 at 07:28
2

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

John3136
  • 28,809
  • 4
  • 51
  • 69
2

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
)
John 9631
  • 527
  • 5
  • 13
1

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;
}
  • Normally, this would work well but this user can't use `string.h` and `strdup` is a part of that header file. – Peter G Mar 30 '17 at 00:38
  • strdup( ) allocates memory on the heap to duplicate the string. A well-behaved program should check that strdup doesn't return NULL, and should free( ) the result when it's no longer needed. – Jac Goudsmit Oct 20 '21 at 17:51