为什么必须指向char数组的指针需要strcpy来为其数组赋值,双引号赋值不起作用?

时间:2009-10-16 12:29:09

标签: c++ arrays string char strcpy

当你去删除指针时,第一个例子不起作用。当我添加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时会发生什么?它们都会完美地完成字符串,调试器不会显示任何不同的内容。

9 个答案:

答案 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涵盖了这一点。