由于我无法返回局部变量,从C或C ++函数返回字符串的最佳方法是什么?

时间:2009-01-08 03:44:27

标签: c++ c memory

作为this question的后续行动:

据我所见,这应该按预期工作:

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

但这会导致未定义的行为:

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

如果我是对的,那么修复第二个问候语功能的最佳方法是什么?在嵌入式环境中?在桌面上?

8 个答案:

答案 0 :(得分:33)

你是对的。第二个示例中的c数组正在堆栈中分配,因此内存将立即重用。特别是,如果您有像

这样的代码
 printf("%s\n",greet());

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

解决方案是在其他地方分配内存。例如:

char c[] = "Hello";

char * greet() {
    return c;
}

会工作吗?另一种选择是在范围内静态分配:

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

因为静态内存是在数据空间中与堆栈分开分配的。

你的第三个选择是通过malloc:

在堆上分配它
char * greet() {
   char * c = (char *) malloc(strlen("Hello")+1);  /* +1 for the null */
   strcpy(c, "Hello");
   return c;
}

但是现在你必须确保以某种方式释放内存,否则你会有内存泄漏。

更新

其中一件似乎比我预期的更令人困惑的事情是“内存泄漏”到底是什么。泄漏是指您动态分配内存,但丢失地址以便无法释放。这些示例都不一定有漏洞,但只有第三个漏洞甚至可能有漏洞,因为它是唯一一个动态分配内存的漏洞。因此,假设第三个实现,您可以编写此代码:

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

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

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

没有泄漏,因为指针保存在自动变量cp中足够长,可以在其上调用free()。现在,即使cp在执行完成后立即消失,但由于已经调用了free,因此内存被回收并且没有泄漏。

答案 1 :(得分:12)

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

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

或者,在单线程环境中,您可以执行以下操作:

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

这里static在全局内存区域中分配空间,永远不会消失。不过,这种static方法充满了危险。

答案 2 :(得分:10)

取决于嵌入式环境是否有堆,如果是这样,你应该按如下方式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;
}

请注意,您应该稍后调用free()进行清理。如果您无权访问动态分配,则需要将其设置为堆栈中的全局变量或变量,但需要进一步调用堆栈。

答案 3 :(得分:7)

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

如果您正在使用C,您可以考虑返回一个指向空间的指针,该指针已经按照Jesse Pepper的建议动态分配了malloc();但另一种可以避免动态分配的方法是让greet()获取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);
}

size参数是为了安全起见,以帮助防止缓冲区溢出。如果您确切知道字符串的长度,则没有必要。

答案 4 :(得分:5)

您可以使用以下任何一种:

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;
}

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

答案 5 :(得分:4)

从其他几个响应建议的函数返回malloc内存只是要求内存泄漏。调用者必须知道你malloc'd它然后免费电话。如果他们碰巧在其上调用了删除,则结果未定义(尽管可能没问题)。

最好强制调用者为您提供内存,然后将字符串复制到其中。通过这种方式,来电者注意到他/她需要清理。

答案 6 :(得分:1)

从我读过的最安全的选项是让调用者负责分配内存来保存字符串。您还必须检查缓冲区是否足够大以容纳字符串:

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!
}

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

答案 7 :(得分:0)

在堆中分配字符数组吗?

是否可以使用malloc取决于“嵌入式环境”的含义。