C++中的new/delete和char *

21

有人可以帮我吗,为什么我在尝试释放分配的内存时会收到错误消息:检测到堆破坏。CTR检测到应用程序在堆缓冲区结束后写入了内存。

char *ff (char *s){
    char *s1 = new char [strlen(s)];
    strcpy(s1, s);
    return s1;
}

int _tmain(int argc, _TCHAR* argv[])
{
    char *s = new char [5];

    strcpy(s, "hello");
    char *s2 = ff(s);

    delete []s;     // This works normal
    delete []s2;    // But I get an error on that line
    return 0;
}

9
显然,这只是一个练习,在实际情况中你会使用 std::string 来代替,对吗? - Matthieu M.
@MatthieuM。完全正确。我不被允许使用std::string; - a3dsfcv
8个回答

50
char *s = new char [5];
strcpy(s, "hello");

导致未定义行为(UB)。
您正在超出分配的内存范围。您为5个字符分配了足够的内存,但您的字符串有6个字符,包括\0

一旦程序引起了这种UB,则任何行为都有可能发生。

您需要:

char *s = new char [strlen("hello") + 1];

实际上,理想的解决方案是使用 std::string 而不是 char *。这些正是 std::string 可避免的错误。在你的例子中,没有真正需要使用 char * 而不是 std::string
使用 std::string

  • 您不需要新建任何东西
  • 您不需要删除任何东西&
  • 您可以用 std::string 完成您对 char * 所做的所有操作。

1
同意使用std::string是最佳方法。在您的答案中,编写改进的new分配的另一种方法是char *s = new char[sizeof("hello")],避免了运行时对strlen()的调用(尽管聪明的编译器可能会将其优化掉)。显然,这仅适用于您拥有常量字符串的情况,这可能不是常见情况。 - Jason R

13

new char [strlen(s)];没有计算结尾的\0字符,因此您的缓冲区比实际需要的短一个字符。


9

strcpy 包括空终止符;strlen 不包括。请写出:

char *s1 = new char [strlen(s) + 1];

6

来自strcpy(3)的说明:

strcpy()函数将源指针指向的字符串(包括终止的空字符'\0')复制到目标指针指向的缓冲区中。

因此,您需要保留6个字节,其中5个用于字符串,1个用于NULL字节。

char *s = new char [6];
strcpy(s, "hello");

3

到目前为止,所有的答案都解决了第一次或第二次分配。总之,你需要进行两个更改:

char *s1 = new char [strlen(s) + 1];
...
char *s = new char [5 + 1];

在这两种情况下,您必须为字符串分配足够的空间再加上一个字节用于终止符“\0”
正如其他人已经指出的那样,使用c++更容易且更安全地使用std::string。不需要费心进行内存分配和释放,也不需要注意'\0'字节。
std::string ff (const std::string &s){
    std::string s1(s);
    // do something else with s1
    return s1;
}

int main(int argc, char* argv[])
{
    std::string s("hello");
    std::string s2 = ff(s);
    return 0;
}

如果只是复制字符串:

std::string s("hello");
std::string s2(s);

1

您需要指定char *s1 = new char [strlen(s) + 1];以为终止字符串的'\0'腾出空间。


1

您已经通过某种方式破坏了s2指针

strcpy(s, "hello");

因为s的大小为5,而您忽略了strcpy包含字符串终止符。


0

你的初始字符串 s 只有五个字符长度,所以不能有空终止符。 "hello" 会被 strcpy 包括空终止符一起复制,但是这样会超出缓冲区。而且 strlen 函数需要该字符串有空终止符,如果没有,就会导致问题。建议将此行更改为:

char *s = new char [6];

更好的方式是优先使用 std::string 来代替 C 风格的字符串函数 —— 它们同样高效,而且更加安全、易用。另外,尽量避免使用 newdelete,除非确实需要。你遇到的问题非常普遍,很容易避免。


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