25

Is it possible to return string from a function without calling malloc? I have a function as below:

char* getString(){
      char tempStr[20]

      // open file, read char by char and assign to tempStr
      // ....

      char* str = (char*)malloc(sizeof(char)*20);
      strcpy(str, tempStr); // assume this copy the null terminating char as well
      return str;
}

And then when I call the getString(), I assign the return value to a char*, and then free it when I'm done, just like below:

void randomFunction(){
      char* line = NULL;
      int i = 0;
      while (i < 1000) {
           line = getString();
           // do stuff with line
           free (line);  
           line = NULL;
      }
}

However, I am wondering if there is any way to do this without malloc? And, is this the proper way of returning string from a C function? I tried to do some research regarding how to return without malloc, but didn't find clear answers. I am new to C and still learning.

Rubens Mariuzzo
  • 28,358
  • 27
  • 121
  • 148
blenzcoffee
  • 851
  • 1
  • 11
  • 35
  • 5
    Your `malloc` call is better written as `char *str = malloc(20);` `sizeof (char)` is 1 by definition, and casting the result of `malloc` is unnecessary and [not recommended](http://c-faq.com/malloc/mallocnocast.html). – Keith Thompson Mar 16 '13 at 20:09
  • @KeithThompson I never knew casting the result of malloc can bring undesired behaviour. Thanks for the info! :) – blenzcoffee Mar 16 '13 at 20:10
  • @KeithThompson I wouldn't recommend omitting the sizeof(char); it's good practice to keep it there and it'd be a terrible compiler that wouldn't optimise it out. Also the arguments against casting malloc have always seemed silly to me; it's required in C++ and to my mind you might as well write language-portable code when you can. – Dave Mar 16 '13 at 20:14
  • @Dave: (splitting my response into two comments) Depending on the fact that `sizeof (char) == 1` isn't a problem if you're allocating a string, which is by definition a sequence of `char`s. But if you want to make the code robust if the type might change, the best idiom is `char *str = malloc(20 * sizeof *str);`. – Keith Thompson Mar 16 '13 at 20:21
  • 3
    @Dave: As for casting the result of `malloc`, a cast serves no real purpose in that context. Casts in general should be viewed with suspicion. And I disagree about the virtue of writing language-portable code. It's easy enough to call C code from C++, or vice versa; there's rarely any real advantage in writing code that can be compiled as either C or C++. Decide what language you're writing in, and write in that language. – Keith Thompson Mar 16 '13 at 20:23
  • 1
    @Dave In C++ you use `new` and so arguments about `malloc` are moot. – David Heffernan Mar 16 '13 at 20:26
  • @KeithThompson by good practice I simply mean that there's less chance of getting into the habit of missing out `sizeof`, so ending up with `double *x = malloc( 3 );` and wondering why it fails. It's about as likely as the argument about forgetting `stdlib` at least. – Dave Mar 16 '13 at 20:28

8 Answers8

16

You can not return temporary from a function and unless you use a malloc your character array defined in the function will be a temporary. An alterantive solution is to pass a character array as parameter to the function and use it as output parameter.

Ivaylo Strandjev
  • 69,226
  • 18
  • 123
  • 176
  • I will try your suggestion to pass a char array as parameter. If I get your solution correctly, the function will modify and return that same char array passed as input right? And this way we can avoid malloc, am I right? Thanks – blenzcoffee Mar 16 '13 at 20:09
  • @blenzcoffee if I understand correctly what you want to do, then yes that should do it. – Ivaylo Strandjev Mar 16 '13 at 20:10
  • 2
    You can avoid malloc if the array was declared by the caller with a certain size. – OregonTrail Mar 16 '13 at 20:23
16

There are three common ways to return a string from a function. (Well, actually there are none, but there are three common ways to return a pointer to a string, which the caller can then use to access the string.)

  1. Allocate space for the string using malloc() inside the function. This is the most flexible method, but it makes the caller responsible for free()ing the allocated array. It can also impose some performance overhead.

  2. Require the caller to allocate space for the string and pass in a pointer to that space. This imposes some inconvenience on the caller. In particular, the caller has to decide how big the string can be.

  3. Return a pointer to (the first element of) a static array defined inside the function. The array will continue to exist after the function returns, but there's only one copy, which means that successive calls will clobber the result returned by previous calls. It also means the array has to be of some fixed size, chosen when you write the code.

Keith Thompson
  • 254,901
  • 44
  • 429
  • 631
8

It depends.

You could decide and document that the returned string is a pointer to some static internal buffer. Then your routine is not re-entrant (nor thread-safe). For instance ctime or getpwent does that.

A better thing would be to pass the result string and size as arguments, and to fill that string and possibly return that. getcwd (or snprintf or strftime which returns a size, not a pointer) works that way.

But usually, you decide and document that the returned string is heap allocated, and it is the responsability of the caller to free it. You might want to use strdup or asprintf in that case.

And you might use in your entire program Boehm's conservative garbage collector (e.g. use its GC_STRDUP or GC_MALLOC_ATOMIC for strings, and GC_MALLOC for heap values containing some pointers.)

If you feel that standard malloc or strdup is too slow (but please measure that first), you could have your own pool allocators, etc.

You could also have alternate schemes (but it is important to document them). For example, you could return some interned string, or even a canonical interned string (sometimes called "quark" or "symbol") - then be able to use pointer equality instead of string equality. You could also have some reference counter scheme. Look for example at what Glib (from GTK, but usable outside of GUI programs!) provides: GString-s, GQuark-s, string utilities

It is however important to decide if the result is heap allocated or not, and to define clearly who has the responsibility to free (and how should it be freed) that heap-allocated result.

You may want to use valgrind to chase memory leaks. Don't forget to pass -Wall -g to your gcc compiler!

PS. I would consider using Boehm's GC. And I don't think that malloc (or strdup, asprintf ....) should be rejected for performance reasons (you could chose some other & faster malloc implementation, or use your own memory pools). However, memory leaks could be an issue.

Basile Starynkevitch
  • 223,805
  • 18
  • 296
  • 547
2

normal way to move the memory allocation out of a function is by passing in pointers. Though in practice you'd want to make sure you aren't going over buffer bounds also.

char* getString(char* tempStr){
          // open file, read char by char and assign to tempStr
          // ....

          return tempStr;
    }


void randomFunction(){
        char line[20];
        int i = 0;
        while (i < 1000) {
             getString(line);
             // do stuff with line

        }
  }
Keith Nicholas
  • 43,549
  • 15
  • 93
  • 156
2

Since your string is (apparently) always 20 characters, you can simply do this:

void getString( char *outputString ) {
    // do stuff to outputString instead of mallocing, or use local memory and copy it at the end
}

char line[20];
for( ... ) {
    getString( line );
    // do things with line
}

Because this avoids many small mallocs, it is faster.

Dave
  • 44,275
  • 12
  • 65
  • 105
2

A common way to do this in C is to have the string passed in as an argument to the function.

char *getString(char *str, int size){
      // open file, read char by char and assign to tempStr
      // ....

      strncpy(str, tempStr, size); // assume this copies the null terminator
      return str;
}

Alternatively, you could have the string declared static, or at a file or global scope, and copy into it. However, make sure that if you store a pointer to a heap allocated buffer in this symbol that you free it before allocating again.

OregonTrail
  • 8,594
  • 7
  • 43
  • 58
1

I am not a C-guru, but I think this is possible in C. (This is based in a C++ solution.) Of course you need to know a bound for the string size (99 here), but you can return a string without allocating, no need for output parameters either.

https://onlinegdb.com/r1akUBiHB

#include <stdio.h>
#include <string.h>

typedef struct str_{
    char c[99];
} str;

str fun(int i){
    str ret;
    if(i > 5) strcpy(ret.c, "big");
    else      strcpy(ret.c, "small");
    return ret;
}

int main(int argc, char* argv[]){
    str ret = fun(argc);
    printf("%s", ret.c);
    return 0;
}

I am not sure if this depends on C enforcing something called Return Value Optimization.

Also I don't know if you want no allocations because you can't at all or for performances only. If it is the second you can implement a struct that conditionally allocates if the string doesn't fit in the predefined size (99 here).

This is basically what std::string does in C++, for short strings it will not allocate in practice.

Note that if this works, it is going to be thread-safe as well. (Other solutions here relying on global variables are not thread safe.)

alfC
  • 14,261
  • 4
  • 67
  • 118
0

Just declare the string as static in the function and return it.

salsaman
  • 956
  • 6
  • 3