10

当您去删除指针时,第一个示例不起作用。当我添加空终止符或没有它时,程序要么挂起,要么我得到:

Debug Assertion Failed Expression: _BLOCK_TYPE_IS_VALID(pHead->nBlockUse)来自 Visual Studio 2008

//Won't work when deleting pointer:
    char *at = new char [3];
    at = "tw"; //   <-- not sure what's going on here that strcpy does differently
    at[2] = '\0'; // <-- causes program to hang
    delete at;

//Works fine when deleting pointer:
    char *at = new char [3];
    strcpy(at,"t");
    at[1] = 'w';
    at[2] = '\0';
    delete at;

那么当我使用双引号而不是 strcpy 时会发生什么?它们都将完美地输出字符串,并且调试器不会显示任何不同。

4

9 回答 9

14

当你这样做

char *at = ...;

at = "hello";

您基本上是new[]用静态常量字符串的地址覆盖指针值(即,分配给您的内存地址)。这意味着当您稍后删除内存时,您将传递delete一个以前未返回的指针new

这是一件坏事。

在 C 和 C++ 中,对指针的赋值通常不会对指向的内存做任何事情,它们会改变指针本身。如果您习惯于字符串更多是“一等公民”的语言,这可能会令人困惑。

此外,delete[]如果您使用过new[].

于 2009-10-16T12:34:40.563 回答
12

因为 achar*不是字符串。它只是一个指向某个字符的指针,约定可能有更多字符要跟随,并且在最后一个字符之后有一个'\0'.

C(因此在 C++ 中)中的字符串文字"abc"只是一个字符数组,编译器会默默地添加一个'\0'. 当您将数组分配给指针时,数组会静默地将指针转换为第一个元素。结果是

at = "tw";

意味着,指针at被分配了字符串字面量中第一个字符的地址"tw"。这样一来,它就会失去原来的价值。由于这是动态分配的字符数组的地址,因此您正在泄漏该数组。

当您稍后分配给at现在指向的数组中的一个字符时,您正在为字符串文字中的某个字符分配一个新值。这会调用未定义的行为,并且程序立即挂起或崩溃可能是您执行此操作时可能发生的最好情况。(在许多平台上,您正在这样做写入只读内存。)

后来你传给atdelete[]不是delete,因为你叫了new[],不是new)。这样做时,您将字符串文字的地址传递给它,而不是分配的字符数组。当然,这会弄乱堆管理器。(VC 的运行时库在调试模式下捕捉到了这一点。)

std::strcpy另一方面,将字符串从一个数组逐个字符复制到另一个数组。不会更改指针,只会复制内存片段。之后指向目标数组的指针仍然指向目标数组,只是该数组中的数据发生了变化。

让我补充一下:作为 C++ 的初学者,您应该使用std::string, 而不是 C 字符串。这为您完成了所有肮脏的工作并且具有理智的语义。

于 2009-10-16T12:46:01.283 回答
9

有3点需要理解:

1)char *at;只是一个指针变量。
指针变量仅仅意味着它拥有一个内存地址。

2)new char[3]返回堆上分配的内存的起始地址。

3)"hello"返回字符串文字的地址。

char *at = new char [3];
//at now contains the address of the memory allocated on the heap


at = "hello";
//at now contains the address of the static string. 
// (and by the way you just created a 3 byte memory leak)


delete[] at; 
//WOOPS!!!! you can't do that because you aren't deleting 
// the original 3 chars anymore which were allocated on the heap!
//Since at contains the string literal's memory address you're 
// trying to delete the string literal.

关于修改只读内存的注意事项:

此外,您永远不应该修改字符串文字。即永远不应该这样做:

char *at = "hello";
at[2] = '\0'; 

字符串文字的内存必须是只读的,如果更改它,C++ 语言未定义结果。

由于您使用的是 C++:

由于您使用的是 C++,请考虑改用该std::string类型。

#include <string>

using namespace std;

int main(int argc, char **argv)
{
  string s = "hello";
  s += " world!";

  //s now contains "hello world!"

  s = "goodbye!";

  //Everything is still valid, and s contains "goodbye!"


  //No need to cleanup s. 

  return 0;
}
于 2009-10-16T12:33:50.263 回答
5

不要忘记使用

delete []

每当你用 [] 分配东西时。

于 2009-10-16T12:34:14.787 回答
4

一个指针保存一个地址。指针的 = 运算符更改所保存的地址。

at = "tw";

指向数组“tw”(由编译器创建的用于保存字符 tw 的数组),它不再指向您使用 new 创建的数组。在文件中创建。

at[2] = '\0';

将 NULL 添加到编译器数组的末尾。

于 2009-10-16T12:35:21.960 回答
0

在第一个示例中,您正在更改 at 的值,在第二个示例中,您正在更改 at 指向的值。将 char * 分配给双引号字符串会将其分配给静态 const 指针。

特别是,在第一个示例中,现在指向内存中的不同位置。

于 2009-10-16T12:33:42.700 回答
0

你犯了两件事:让指针指向不同的东西(这是赋值所做的)并将一些数据复制到指针指向的位置。

at = "tw";

此代码at指向在只读内存中某处创建的文字“tw”。尝试写入它是一种未定义的行为。

char *at = new char [3];
strcpy(at,"t");

此代码为三个字符分配内存并at指向这部分内存(第 1 行),然后将一些数据复制到at.

请记住,分配的内存new[]应该用 释放delete[],而不是delete

我建议您了解有关指针的更多信息。本讨论涵盖了这一点。

于 2009-10-16T12:49:12.443 回答
0

在您的第一个示例中,您正在分配一些内存并使用“at”变量指向它。当你这样做

at = "tw"

您实际上是在将 char * 重新指向一个常量字符串。这会导致您泄漏内存。当您继续删除“at”时,您正在尝试删除堆栈内存。

strcpy 遍历每个字符并将它们的值复制到您分配的新内存中。这也称为深拷贝。

于 2009-10-16T12:36:41.027 回答
0

在第一个示例中,您导致了内存泄漏。

您的变量at是指向内存地址的指针,而不是字符串本身。当您将 的地址分配给"tw"指针时,您丢失了使用 获得的原始地址newat现在指向一个你没有分配的地址new,所以你不能delete

如果您将指针视为整数,它可能会更有意义。为了讨论,我已经指定了任意数字作为地址。

char *at = new char[3];    // 0x1000
at = "tw";                 // 0x2000
at[2] = '\0';              // set char at 0x2002 to 0
delete at;                 // delete 0x2000 (whoops, didn't allocate that!)
于 2009-10-16T12:43:01.517 回答