由于我不能返回局部变量,那么从C或c++函数返回字符串的最佳方式是什么?

时间:2022-06-02 16:52:48

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?

如果我是对的,修复第二个问候函数的最好方法是什么?在嵌入式环境中?在桌面吗?

8 个解决方案

#1


33  

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

你是绝对正确的。第二个示例中的c数组正在堆栈上分配,因此内存将会立即被重用。特别是,如果你有代码。

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

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

你会得到奇怪的结果,因为对printf的调用会重用数组的一些空间。

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

解决方案是将内存分配到其他地方。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:

您的第三个选择是通过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,

这有一个泄漏;返回malloc'ed内存的指针,printf使用它,然后它丢失;你不能再释放它了。另一方面,

{
    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.

不会泄漏,因为指针被保存在自动变量cp中足够长的时间来调用free()。现在,即使cp在执行通过时就消失了,但是由于free被调用,内存被回收并且没有泄漏。

#2


12  

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

如果您使用的是c++,那么您可能需要考虑使用std::string来从第二个函数返回字符串:

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.

这里的静态分配了全局内存区域的空间,它永远不会消失。然而,这种静态方法充满了危险。

#3


10  

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

取决于嵌入式环境是否有堆,如果有,应该如下malloc:

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.

注意,稍后您应该调用free()来清理。如果您没有访问动态分配的权限,您将需要使它成为全局的,或者堆栈上的一个变量,但是在调用堆栈的后面。

#4


7  

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

如果您需要在c++中进行此操作,那么使用Greg Hewgill建议的std::string绝对是最简单且可维护的策略。

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:

如果您正在使用C,您可以考虑返回一个指针,该指针指向由Jesse Pepper所建议的使用malloc()动态分配的空间;但是另一种避免动态分配的方法是使用char *参数,并将其输出写到那里:

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.

为了安全起见,这里有size参数,以帮助防止缓冲区溢出。如果你知道绳子的确切长度,那就没有必要了。

#5


5  

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.

第一个要求您不写返回的字符串,但也是最简单的。最后一个使用shared_array,它可以在内存引用丢失时自动清理内存(它的最后一个shared_array超出范围)。如果您每次调用该函数时都需要一个新字符串,则最后一个和第二个必须使用。

#6


4  

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).

根据其他几个响应的建议,从函数中返回malloc的内存只是请求内存泄漏。打电话的人必须知道你打错了电话,然后再打免费电话。如果他们碰巧在上面调用delete,那么结果是未定义的(尽管可能还可以)。

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.

最好强制调用者为您提供内存,然后将您的字符串复制到其中。这样打电话的人就会注意到他/她需要清理。

#7


1  

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...

为一项简单的任务做大量的工作……但是你有C:-)哎呀!我猜我被random_hacker打败了……

#8


0  

Allocate the character array in the heap?

分配堆中的字符数组?

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

你是否可以使用malloc取决于你所说的“嵌入式环境”。

#1


33  

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

你是绝对正确的。第二个示例中的c数组正在堆栈上分配,因此内存将会立即被重用。特别是,如果你有代码。

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

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

你会得到奇怪的结果,因为对printf的调用会重用数组的一些空间。

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

解决方案是将内存分配到其他地方。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:

您的第三个选择是通过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,

这有一个泄漏;返回malloc'ed内存的指针,printf使用它,然后它丢失;你不能再释放它了。另一方面,

{
    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.

不会泄漏,因为指针被保存在自动变量cp中足够长的时间来调用free()。现在,即使cp在执行通过时就消失了,但是由于free被调用,内存被回收并且没有泄漏。

#2


12  

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

如果您使用的是c++,那么您可能需要考虑使用std::string来从第二个函数返回字符串:

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.

这里的静态分配了全局内存区域的空间,它永远不会消失。然而,这种静态方法充满了危险。

#3


10  

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

取决于嵌入式环境是否有堆,如果有,应该如下malloc:

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.

注意,稍后您应该调用free()来清理。如果您没有访问动态分配的权限,您将需要使它成为全局的,或者堆栈上的一个变量,但是在调用堆栈的后面。

#4


7  

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

如果您需要在c++中进行此操作,那么使用Greg Hewgill建议的std::string绝对是最简单且可维护的策略。

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:

如果您正在使用C,您可以考虑返回一个指针,该指针指向由Jesse Pepper所建议的使用malloc()动态分配的空间;但是另一种避免动态分配的方法是使用char *参数,并将其输出写到那里:

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.

为了安全起见,这里有size参数,以帮助防止缓冲区溢出。如果你知道绳子的确切长度,那就没有必要了。

#5


5  

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.

第一个要求您不写返回的字符串,但也是最简单的。最后一个使用shared_array,它可以在内存引用丢失时自动清理内存(它的最后一个shared_array超出范围)。如果您每次调用该函数时都需要一个新字符串,则最后一个和第二个必须使用。

#6


4  

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).

根据其他几个响应的建议,从函数中返回malloc的内存只是请求内存泄漏。打电话的人必须知道你打错了电话,然后再打免费电话。如果他们碰巧在上面调用delete,那么结果是未定义的(尽管可能还可以)。

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.

最好强制调用者为您提供内存,然后将您的字符串复制到其中。这样打电话的人就会注意到他/她需要清理。

#7


1  

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...

为一项简单的任务做大量的工作……但是你有C:-)哎呀!我猜我被random_hacker打败了……

#8


0  

Allocate the character array in the heap?

分配堆中的字符数组?

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

你是否可以使用malloc取决于你所说的“嵌入式环境”。