【问题标题】:How to return a string literal from a function如何从函数返回字符串文字
【发布时间】:2010-03-20 01:30:58
【问题描述】:

我总是对从函数返回字符串文字或字符串感到困惑。有人告诉我可能是内存泄漏,因为你不知道什么时候会删除内存?

例如在下面的代码中,如何实现foo(),使代码的输出为“Hello World”?

void foo (       )              // you can add parameters here.
{

}

int main ()
{
    char *c;
    foo (    );
    printf ("%s",c);
    return 0;
}

另外,如果foo()的返回类型不是void,但是可以返回char*,应该是什么?

【问题讨论】:

  • 您要求它返回char* 而不是char const* 实际上迫使我们假设纯字符串文字是不够的,因此return "hello world"; 是一个无效的解决方案。你对 constness 有什么要求?

标签: c++ string literals return-type


【解决方案1】:

我假设我们不能修改 main.为了让你的程序在不泄漏的情况下运行,你需要一些东西来拥有静态存储:

void foo(char*& pC)  // reference
{
    static char theString[] = "thingadongdong";

    pC = theString;
}

但实际上,这不是非常传统的 C++ 代码。您将使用std::stringstd::cout,因此您不必担心内存问题:

std::string foo(void)
{
    return "better thingadongdong";
}

int main(void)
{
    // memory management is done
    std::cout << foo() << std::endl;
}

如果您想知道是否需要手动解除分配,那就是做错了。

【讨论】:

  • 并非总是如此。毕竟,std::string 是手动分配的。
  • 嗯,当你不在“图书馆写作模式”时,我应该说。 :)
  • @fd,您无法更改字符串文字,它会触发 '03 中已弃用的转换,并且在 c++0x 中格式错误。 char* 表示 OP 想要更改返回的字符串。我们不能确定。
  • 说起常规的C++代码,空参数列表中的void是多余的。
  • 并不是说我想在需要之前进行优化,但是您的“传统 C++”示例是复制实际字符,还是将地址返回到静态字符串文字?为避免复制,static std::string my_string = "better thingadongdong"; return my_string,返回类型为const std::string&amp; 应该没问题,对吧?
【解决方案2】:

既然不推荐使用 char* 的旧用法,你能不能不简单地使用字符串?

const char* func1 () {return "string literal";}

string func2 () {return "another string literal";}

这两个都可以正常工作,没有编译器警告。

然而

char* func3 () {return "yet another string literal";}

根本不会编译。也不会

char* func4 () {return &amp;"a ref to a string literal?";}

Stroustrup 在《C++ 编程语言》(第三版)中说:

“字符串文字是静态分配的,因此从函数返回一个是安全的。

const char* error_message (int i)`
{
//...
return "range error";
}

在调用 error_messages() 后,持有范围错误的内存不会消失。"

因此,程序中的每个字符串字面量都分配在其自己的一小块内存中,该内存会在程序运行期间持续存在(即静态分配)。 将 const 放在 char* 前面可以让编译器知道您不打算(也不能)更改该字符串文字的一小块内存,这可能是危险的,因此尽管从字符串文字转换为 char*,他们还是让这个赋值滑动已弃用。

返回到字符串必须将字符串文字复制到字符串类型的对象中,即调用者负责的内存。

无论哪种方式都没有内存泄漏:每个字符串文字都有自己的一块内存,在程序终止时会被清理; return to const char* 返回一个指向字面量内存的指针(知道你不能改变它);并返回一个字符串,将副本复制到调用者代码中存在的字符串对象中,该对象由调用者清理。

虽然它看起来有点难看,但我猜他们留下了 const char* 以保留便宜的替代品(不涉及副本)。

【讨论】:

    【解决方案3】:

    我总是对从函数返回字符串文字或字符串感到困惑。

    不可变的文字字符串

    据我了解,如果返回类型声明为 const,则直接返回字符串文字是安全的,以声明该字符串不打算更改。这意味着您不必担心字符串的寿命/内存泄漏。

    可变的非文字字符串

    但是,如果您需要一个可以就地更改的字符串,则需要考虑该字符串的生命周期以及存储它的内存分配的大小。 这成为一个问题,因为您不能再愉快地为函数的每次调用返回包含字符串的相同内存,因为以前的使用可能已经改变了该内存的内容,和/或可能仍在使用中。因此,必须分配一块新的内存来保存返回的字符串。

    这是可能发生泄漏的地方,并且需要选择应该在哪里进行分配和取消分配。您可以让函数本身在发生这种情况的文档中分配内存和状态,并在其中规定调用者必须在不再需要内存时释放内存(防止泄漏)。这意味着该函数可以简单地返回一个字符 *。

    另一种选择是将一些内存传递给调用者分配的函数,并让函数将字符串放入该内存中。在这种情况下,调用者既分配并负责释放该内存。

    最后,我提到在使用可变字符串时需要管理内存和字符串的大小。分配需要足够大,以容纳函数最初设置的字符串,以及在函数之后、释放内存之前所做的任何更改。未能正确执行此操作可能会导致缓冲区溢出,方法是将字符串写入 long 以适合最初分配的内存;这对您的程序的健康和安全极为危险。它可能会导致极难发现的错误和安全漏洞(因为错误的来源 - 溢出 - 可以与程序失败时看到的症状相去甚远)。

    【讨论】:

      【解决方案4】:

      类似这样的:

      void foo(char ** pChar)
      {
          // Make sure the string is shorter
          // than the buffer
          *pChar = new char[256];
          strcpy(*pChar,"Hello World!");
      }
      

      然后这样称呼它:

      foo(&c);
      

      正如评论中提到的,小心你存储的字符串小于缓冲区,否则你会得到......堆栈溢出! (双关语)

      【讨论】:

      • 你不能取消引用 0 这会让你的程序崩溃。
      • 因为分配是在foo 中完成的,当foo 返回时,分配的内存将是悬空的。 (而且没有办法释放它。)
      • @George:这就是重点。不要拿指针,拿参考。这是 C++。
      • @GMan 这就像说“如果我做字符串(0)?”或“如果我执行 printf(0)?”。他禁止空指针作为前提条件,所以不允许传递0
      • @litb:但是引用强制执行此操作,而不仅仅是说“不要这样做”。
      猜你喜欢
      • 2022-10-14
      • 1970-01-01
      • 2010-12-23
      • 2016-11-17
      • 1970-01-01
      • 1970-01-01
      • 2017-05-12
      • 2011-03-15
      相关资源
      最近更新 更多