如何从函数中返回一个字符串常量

9
我总是困惑于返回字符串常量还是函数中的字符串。有人告诉我可能会出现内存泄漏,因为你不知道内存何时会被删除?
例如,在下面的代码中,如何实现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"; 是无效的解决方案。您对常量性有什么要求? - Johannes Schaub - litb
4个回答

11

我假设我们不能修改主函数。为了让你的程序正常工作且不泄漏内存,你需要使用具有静态存储的变量:

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需要手动分配内存。 - Nathan Osman
1
哦,我应该说的是,当你不处于“图书馆写作模式”时。 :) - GManNickG
1
@fd,您无法更改字符串字面量,并且它会在'03中触发不推荐的转换,在c++0x中则是不合法的。 char * 表示 OP 想要更改返回的字符串。我们不能确定。 - Johannes Schaub - litb
3
说到传统的C++代码,空参数列表中的void是多余的。 - Paul Manta
1
我并不想在需要之前进行优化,但是你的“传统C ++”示例是否复制了实际字符,还是返回静态字符串文字的地址?为了避免复制,使用static std :: string my_string =“better thingadongdong”,并以const std :: string&作为返回类型,这样做应该没问题,对吧? - Gauthier
显示剩余4条评论

8

由于旧的char*用法已被弃用,你不能只使用string吗?

const char* func1 () {return "字符串文字";}

string func2 () {return "另一个字符串文字";}

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

然而

char* func3 () {return "另一个字符串文字";}

根本无法编译。也不会

char* func4 () {return &"对字符串文字的引用?";}

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

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

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

调用error_messages()后,持有“范围错误”的内存不会消失。
所以程序中的每个字符串字面值都分配在其自己的小内存中,该内存持续整个程序(即静态分配)。在char*前放置const可以让编译器知道你不打算(也无法)更改该字符串字面值的小内存,这可能是危险的,因此尽管从字符串字面值转换为char*已被弃用,但编译器仍然允许此赋值操作。
返回一个字符串必须将字符串字面值复制到一种字符串对象中,调用者负责该内存。无论哪种方式,都不会发生内存泄漏:每个字符串字面值都拥有自己的内存块,在程序终止时进行清理;返回const char*会返回指向字面值内存块的指针(知道您不能更改它);而返回字符串则将其复制到调用方代码中存在的字符串对象中,由调用方清理。
虽然符号表示方式看起来有点丑陋,但我猜他们保留了const char*以保持廉价的替代方案(不涉及任何复制)。

4
我经常困惑于返回字符串字面值或函数中的字符串。据我所知,如果声明了字符串不打算被更改,那么可以直接返回字符串字面值,这样是安全的,只要返回类型已声明为const。这意味着你不必担心字符串的寿命或内存泄漏。
然而,如果你需要一个可以就地更改的字符串,则需要考虑字符串的寿命和存储它的内存分配的大小。这变成了一个问题,因为你不能再轻率地为每次函数调用返回相同的包含字符串的内存,因为先前的使用可能已更改该内存的内容,或者仍在使用中。因此,必须分配新的内存来保存返回的字符串。
这就是潜在泄漏发生的地方,也是需要选择在哪里进行分配和释放的地方。你可以让函数自己分配内存,并在文档中说明这一点,并规定调用者在不再需要内存时释放它(以防止泄漏)。这意味着函数可以简单地返回char *。
另一个选择是将由调用者分配的一些内存传递给函数,并让函数将字符串放在其中。在这种情况下,调用者既分配内存,也负责释放该内存。
最后,我提到了当使用可变字符串时需要管理内存和字符串的大小。分配需要足够大,以容纳函数最初设置的字符串以及在函数之后进行的任何更改,然后才释放内存。如果没有正确地执行此操作,则可能会导致缓冲区溢出,从而将字符串写入到最初分配的内存中无法容纳的长度;这对程序的健康和安全非常危险。它可能会导致极难发现的错误和安全漏洞(因为错误的源头——溢出——可能与程序失败时看到的症状相去甚远)。

1

类似这样:

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

然后像这样调用它:

foo(&c);

如评论中所提到的,要小心存储的字符串比缓冲区小,否则会出现堆栈溢出!(双关语意在其中)


1
你不能对 0 进行解引用,否则会导致程序崩溃。 - Nathan Osman
因为内存分配是在 foo 中进行的,当 foo 返回时,所分配的内存将会挂起。 (而且没有办法释放它。) - Nathan Osman
@GMan 这就像是说“如果我做 string(0)?”或者“如果我做 printf(0)?”他禁止使用空指针作为前提条件,因此传递 0 是不允许的。 - Johannes Schaub - litb
@litb:但是引用可以强制执行这一点,而不仅仅是说“不要这样做”。 - GManNickG
@GMan 哦,我明白了。是的,在这里引用似乎是更好的工具。 - Johannes Schaub - litb
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接