当你去删除指针时,第一个例子不起作用。当我添加null终止符时,程序要么挂起,要么在没有它的情况下挂起:
来自Visual Studio 2008的 Debug Assertion Failed Expression: _BLOCK_TYPE_IS_VALID(pHead->nBlockUse)
//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时会发生什么?它们都会完美地完成字符串,调试器不会显示任何不同的内容。
答案 0 :(得分:14)
当你这样做时
char *at = ...;
at = "hello";
你基本上用静态常量字符串的地址覆盖指针值(即new[]
为你分配的内存地址)。这意味着当您稍后删除那个内存时,您正在向delete
传递new
之前未返回的指针。
这是一件坏事。
在C和C ++中,对指针的赋值通常不会对指向的内存执行任何操作,它们会更改指针本身。如果您习惯于字符串更多是“一等公民”的语言,这可能会令人困惑。
另外,如果您使用delete[]
,则应使用new[]
。
答案 1 :(得分:12)
因为char*
不是字符串。它只是一个指向某个角色的指针,其惯例是可能有更多的角色要遵循,而在最后一个角色之后有一个'\0'
。
C中的字符串文字(因此在C ++中),如"abc"
,只是一个字符数组,编译器默默地添加'\0'
。将数组分配给指针时,该数组会以静默方式将指针转换为第一个元素。结果是
at = "tw";
表示,指针at
被分配了字符串文字"tw"
中第一个字符的地址。这样,它就会失去旧的价值。由于这是动态分配的字符数组的地址,因此您正在泄漏此数组。
当您稍后分配给数组at
中现在指向的字符时,您将为字符串文字中的某个字符分配新值。这是调用未定义的行为,程序立即挂起或崩溃可能是您执行此操作时可能发生的最佳情况。 (在许多平台上,你正在写入只读内存。)
稍后您将at
传递给delete[]
(和not delete
, since you called new[]
, not new
)。在这样做时,您传递字符串文字的地址,而不是分配的字符数组。当然,这会弄乱堆管理器。 (VC的运行时库在调试模式下捕获它。)
std::strcpy
将字符串中的字符串从一个数组复制到另一个数组。不会更改任何指针,只会复制内存块。之后指向目标数组的指针仍然指向目标数组,只有该数组中的数据已更改。
让我添加一下: 作为C ++的初学者,你应该使用std::string
而不是C字符串。这为你做了所有肮脏的工作,并且具有理智的语义。
答案 2 :(得分:9)
有三件事需要理解:
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;
}
答案 3 :(得分:5)
别忘了使用
delete []
每当你用[]分配东西时。
答案 4 :(得分:4)
指针包含一个地址。指针的=运算符会更改保持的地址。
at = "tw";
指向数组“tw”(由编译器创建的用于保存字符tw的数组),它不再指向您使用new创建的数组。在文件中创建。
at[2] = '\0';
在编译器数组的末尾添加一个NULL。
答案 5 :(得分:0)
在第一个示例中,您正在处理值,在第二个示例中,您将更改点的值。将char *分配给双引号字符串会将其分配给静态const指针。
特别是,在第一个示例中,现在指向内存中的不同位置。
答案 6 :(得分:0)
在你的第一个例子中,你正在分配一些内存并用“at”变量指向它。当你这样做
at = "tw"
你实际上是将char *重新指向一个常量字符串。这会导致内存泄漏。当你继续删除“at”时,你试图删除堆栈内存。
strcpy遍历每个字符并将其值复制到您分配的新内存中。这也称为深层复制。
答案 7 :(得分:0)
在第一个示例中,您导致内存泄漏。
您的变量at
是指向内存地址的指针,而不是字符串本身。当您将"tw"
的地址分配给指针时,您丢失了new
所获得的原始地址。 at
现在指向您未使用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!)
答案 8 :(得分:0)
你弄错了两件事:使指针指向不同的东西(这就是赋值所做的)并将一些数据复制到指针所指向的位置。
at = "tw";
此代码使at
指向在只读内存中某处创建的文字“tw”。试图写入它是一种未定义的行为。
char *at = new char [3];
strcpy(at,"t");
此代码为三个字符分配内存,并使at
指向内存的这一部分(第1行),然后将一些数据复制到at
指向的内存中。
请记住,分配有new[]
的内存应该使用delete[]
而不是delete
我建议你学习更多关于指针的知识。 This discussion涵盖了这一点。