简体   繁体   中英

Since I can't return a local variable, what's the best way to return a string from a C or C++ function?

As a follow-up to this question :

From what I've seen, this should work as expected:

void greet(){
  char c[] = "Hello";
  greetWith(c);
  return;
}

but this will cause undefined behavior:

char *greet(){ 
  char c[] = "Hello";
  return c;
}

If I'm right, what's the best way to fix the second greet function? In an embedded environment? On a desktop?

You're absolutely right. Your c array in the second example is being allocated on the stack, and thus the memory will get reused immediately following. In particular, if you had code like

 printf("%s\n",greet());

you'd get weird results, because the call to printf would have reused some of the space of your array.

The solution is to allocate the memory somewhere else. For expample:

char c[] = "Hello";

char * greet() {
    return c;
}

Would work. Another choice would be to allocate it statically in scope:

char * greet() {
    static char c[] = "Hello";
    return c;
}

because static memory is allocated separately from the stack in data space.

Your third choice is to allocate it on the heap via malloc:

char * greet() {
   char * c = (char *) malloc(strlen("Hello")+1);  /* +1 for the null */
   strcpy(c, "Hello");
   return c;
}

but now you have to make sure that memory is freed somehow, or else you have a memory leak.

Update

One of those things that seems more confusing than I expect is what exactly a "memory leak" is. A leak is when you allocate memory dynamically, but lose the address so it can't be freed. None of these examples necessarily has a leak, but only the third one even potentially has a leak, because it's the only one that allocates memory dynamically. So, assuming the third implementation, you could write this code:

{
    /* stuff happens */
    printf("%s\n", greet());
}

This has a leak; the pointer to the malloc'ed memory is returned, the printf uses it, and then it's lost; you can't free it any longer. On the other hand,

{
    char * cp ;
    /* stuff happens */
    cp = greet();
    printf("%s\n", cp);
    free(cp);
}

doesn't leak, because the pointer is saved in an auto variable cp long enough to call free() on it. Now, even though cp disappears as soon as execution passes he end brace, since free has been called, the memory is reclaimed and didn't leak.

If you are using C++, then you may want to consider using std::string to return strings from your second function:

std::string greet() {
    char c[] = "Hello";
    return std::string(c); // note the use of the constructor call is redundant here
}

Or, in a single threaded environment you can do:

char *greet() {
    static char c[] = "Hello";
    return c;
}

The static here allocates space in the global memory area, which never disappears. This static method is fraught with peril, though.

Depends if the embedded environment has a heap or not, if so, you should malloc as follows:

char* greet()
{
  char* ret = malloc(6 * sizeof(char)); // technically " * sizeof(char)" isn't needed since it is 1 by definition
  strcpy(ret,"hello");
  return ret;
}

Note that you should later call free() to clean up. If you don't have access to dynamic allocation, you will need to make it a global, or a variable on the stack, but further up the call stack.

If you need to do this in C++, using std::string as suggested by Greg Hewgill is definitely the most simple and maintainable strategy.

If you are using C, you might consider returning a pointer to space that has been dynamically allocated with malloc() as suggested by Jesse Pepper; but another way that can avoid dynamic allocation is to have greet() take a char * parameter and write its output there:

void greet(char *buf, int size) {
    char c[] = "Hello";

    if (strlen(c) + 1 > size) {
        printf("Buffer size too small!");
        exit(1);
    }

    strcpy(buf, c);
}

The size parameter is there for safety's sake, to help prevent buffer overruns. If you knew exactly how long the string was going to be, it would not be necessary.

You can use either of these:

char const* getIt() {
    return "hello";
}

char * getIt() {
    static char thing[] = "hello";
    return thing;
}

char * getIt() {
    char str[] = "hello";
    char * thing = new char[sizeof str];
    std::strcpy(thing, str);
    return thing;
}

shared_array<char> getIt() {
    char str[] = "hello";
    shared_array<char> thing(new char[sizeof str]);
    std::strcpy(thing.get(), str);
    return thing;
}

The first requires you to not write to the returned string, but also is the simplest you can get. The last uses shared_array which automatically can clean up memory if the reference to the memory is lost (the last shared_array to it goes out of scope). The last and second last must be used if you require a new string everytime you call the function.

Returning malloc'd memory from a function as suggested by several of the other responses is just asking for a memory leak. The caller would have to know you malloc'd it and then call free. If they happened to call delete on it, the results are undefined (although probably okay).

It would be better to force the caller to provide the memory for you and then copy your string into it. This way the caller is on notice that he/she needs to clean up.

From what I've read the safest option is to make the caller responsible for allocating memory to hold the string. You must also check if the buffer is large enough to hold your string:

char *greet(char *buf, int size) {
     char *str = "Hello!"
     if (strlen(str) + 1 > size) { // Remember the terminal null!
          return NULL;
     } 
     strcpy(buf, str);
     return buf;
}

void do_greet() {
    char buf[SIZE];
    if (greet(buf, SIZE) == NULL) {
        printf("Stupid C");
     } 
     else {} // Greeted!
}

A ton of work for a simple task... but there's C for you :-) Oops! Guess I was beaten by random_hacker...

Allocate the character array in the heap?

Whether you can use malloc or not depends on just what you mean by "embedded environment".

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM