C++:如何使用new查找函数返回值的存储位置?

9
我正在阅读Bjarne Stroustrup的《C++程序设计语言》第3版,并尝试完成其中所有练习。我不确定如何解决第6.6节中的第13个练习题,所以我想求助于Stack Overflow。以下是问题描述:
编写一个函数cat(),它接受两个C风格的字符串参数,并返回一个由这些参数连接而成的单个字符串。使用new为结果找到存储空间。
以下是我的代码,其中我不确定该做什么的地方用问号表示:
? cat(char first[], char second[])
{
    char current = '';
    int i = 0;

    while (current != '\0')
    {
        current = first[i];
        // somehow append current to whatever will eventually be returned
        i++;
    }

    current = '';
    i = 0;

    while (current != '\0')
    {
        current = second[i];
        // somehow append current to whatever will eventually be returned
        i++;
    }

    return ?
}

int main(int argc, char* argv[])
{
    char first[]  = "Hello, ";
    char second[] = "World!";

    ? = cat(first, second);

    return 0;
}

以下是我的问题:

  1. 如何使用new找到存储器?我应该像std :: string * result = new std :: string;这样做,还是应该以某种方式使用new创建另一个C风格的字符串?
  2. 与前面的问题有关,在cat()中应该返回什么?如果必须使用new,那么我认为它需要是指针。但是指向什么?
  3. 虽然问题没有提到使用delete来释放内存,但我知道我应该这样做,因为我将使用new进行分配。我应该在main结束之前删除吗?
7个回答

4
在一个“真正”的程序中,是的,你会使用std::string。但听起来这个例子想让你使用 C 字符串代替。
所以可能像这样:
char * cat(char first[], char second[])
{
    char *result = new char[strlen(first) + strlen(second) + 1];

问:如何“追加”?

答:只需将“first”中的所有内容写入“result”。

完成后,继续将“second”中的所有内容写入结果(从上次结束的地方开始)。完成后,请确保在末尾添加'\0'。


在C++编程中,使用strlen与什么有关系吗? - Antonio
@Antonio:是的,正如问题中所述,当处理C风格的字符串时。 - Blastfurnace
@Blastfurnace那么为什么不用strcpy和strcat呢?我觉得如果你真的想练习关于字符串和内存管理方面的知识,你应该知道这些函数的存在,但是要练习实现自己的函数。 - Antonio

4
我如何使用 new 寻找某个存储空间?我是否需要做类似于 std::string* result = new std::string; 这样的事情,还是应该使用 new 创建另一个 C 风格的字符串?
后者。该方法接受 C 风格的字符串,并且文本中没有任何迹象表明它应该返回其他内容。因此函数的原型应该是 char* cat(char const*, char const*)。当然,这不是您通常编写函数的方式;手动管理内存在现代 C++ 中完全是禁忌,因为容易出错。
虽然该问题没有提及使用 delete 释放内存,但我知道我应该这样做,因为我已经使用 new 分配了内存。我应该在 main 结束前删除它吗?
在这个练习中,是的。在实际情况中,不应该这样做:就像我上面所说的,这完全是禁忌。实际上,你应该返回一个std::string而不是使用new分配内存。如果你发现自己手动分配内存(并且假设有充分的理由),你应该把那块内存放在智能指针 std::unique_ptrstd::shared_ptr 中,而不是一个裸指针。

3

这是我从一段时间以前的一个项目中挖出来的一些旧代码:

char* mergeChar(char* text1, char* text2){
    //Find the length of the first text
    int alen = 0;
    while(text1[alen] != '\0')
        alen++;

    //Find the length of the second text
    int blen = 0;
    while(text2[blen] != '\0')
        blen++;

    //Copy the first text
    char* newchar = new char[alen + blen + 1];
    for(int a = 0; a < alen; a++){
            newchar[a] = text1[a];
    }

    //Copy the second text
    for(int b = 0; b < blen; b++)
        newchar[alen + b] = text2[b];

    //Null terminate!
    newchar[alen + blen] = '\0';
    return newchar;
}

一般来说,在“真正”的程序中,你会使用std::string。确保稍后delete[] newchar


1
手动编写这些部分而不使用适当的函数(std::strlen, std::strcpy)有什么特殊原因吗?这样做更冗长,可读性更差,而且容易出错。不要这样做。 - Konrad Rudolph
1
一个试图学习的人不应该被给予完整的代码集,以解决完全相同的问题 - 这不会教授学习者任何东西,除了CTRL-C + CTRL-V之外,这是使用SO的人可能(尽管有时仅仅是勉强)知道的。 - Mats Petersson
2
他在询问如何操作C风格的字符串。我正在向他展示发生了什么。如果他不想学习,他一开始就不会读这本书。他说他不是为了实际应用而使用代码,而是在复习书中的材料。 - BrainSteel
@Konrad:既然你已经有了strlen的长度,为什么还要使用strcpy而不是memcpy呢?我只是好奇...我认为strcpy会更慢,因为它需要检查空终止符...这可能涉及到像strlen一样的代码。 - user539810
1
@Chrono 我不明白为什么strcpy会更慢。在任何情况下都有检查。当复制以空字符结尾的字符串时,使用strcpy更具描述性,而描述性胜过(几乎)所有其他问题。个人而言,我从不使用std::memcpy - 我改用std::copy。它同样高效,并且更符合C++迭代器范围的惯用法。 - Konrad Rudolph
显示剩余4条评论

3
  1. 您需要返回一个C语言风格的字符串,因此不能使用std::string(或至少不符合问题的“精神”)。 是的,您应该使用new来创建C语言风格的字符串。
  2. 您应该返回您生成的C语言风格的字符串... 因此,是指向您新创建的字符串的第一个字符的指针。
  3. 是的,您应该在最后删除结果。我预计这可能会被忽略,在这种特殊情况下,这可能并不重要 - 但为了完整性/正确性,您应该这样做。

2
运用new来分配内存是这个练习的意义。"Find store"这个短语说得有点奇怪,但实际上它就是这么做的。你告诉它你需要多少存储空间,它会找到可用的内存块供你使用,并返回其地址。
看起来这个练习不想让你使用std::string。听起来你需要返回一个char*。所以函数原型应该是:
char* cat(const char first[], const char second[]);
注意const修饰符。这很重要,因为你将能够将字符串字面值作为参数传递。
因此,在不直接给出代码的情况下,你需要确定结果char*字符串的大小,使用new分配所需的空间,将两个源字符串复制到新分配的空间中,并返回它。
请注意,通常在C++中不手动执行这种类型的内存管理(而是使用std::string),但了解它仍然很重要,这就是这个练习的原因。

0

看起来你需要使用new为字符串分配内存,然后返回指针。因此catreturn类型将是`char*

你可以像这样做:

int n = 0;
int k = 0;

//also can use strlen
while( first[n] != '\0' )
     n ++ ;
while( second[k] != '\0' )
     k ++ ;

//now, the allocation

char* joint = new char[n+k+1]; //+1 for a '\0'

//and for example memcpy for joining

memcpy(joint, first, n );
memcpy(joint+n, second, k+1); //also copying the null


return joint;

0

它基本上告诉你要按照C的方式来做:

#include <cstring>

char *cat (const char *s1, const char *s2)
{
    // Learn to explore your library a bit, and
    // you'll see that there is no need for a loop
    // to determine the lengths. Anything C string
    // related is in <cstring>.
    //
    size_t len_s1 = std::strlen(s1);
    size_t len_s2 = std::strlen(s2);
    char *dst;

    // You have the lengths.
    // Now use `new` to allocate storage for dst.


    /*
     * There's a faster way to copy C strings
     * than looping, especially when you
     * know the lengths...
     *
     * Use a reference to determine what functions
     * in <cstring> COPY values.
     * Add code before the return statement to
     * do this, and you will have your answer.
     *
     * Note: remember that C strings are zero
     * terminated!
     */

    return dst;
}

在释放已分配的内存时,请不要忘记使用正确的运算符。否则,您将会出现内存泄漏。

编程愉快!:-)


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