在处理char指针时我感到困惑。请看下面的代码:
class Person
{
char* pname;
public:
Person(char* name)
{
//I want to initialize 'pname' with the person's name. So, I am trying to
//achieve the same with different scenario's
//Case I:
strcpy(pname, name); // As expected, system crash.
//Case II:
// suppose the input is "ABCD", so trying to create 4+1 char space
// 1st 4 for holding ABCD and 1 for '\0'.
pname = (char*) malloc(sizeof(char) * (strlen(name)+1) );
strcpy(pname, name);
// Case III:
pname = (char*) malloc(sizeof(char));
strcpy(pname, name);
}
void display()
{
cout<<pname<<endl;
}
};
void main()
{
Person obj("ABCD");
obj.display();
}
对于案例I: 正如所料,系统崩溃。
案例II的输出:
ABCD
案例III的输出:
ABCD
所以,我不确定Case II&amp;三是产生相同的输出!!!! ..... 我应该如何在类中初始化char指针?
答案 0 :(得分:10)
第三种情况调用未定义行为,因此在这种情况下可能会发生任何事情 在这种情况下,您正在写超出已分配内存的范围,这可能会或可能不会崩溃但是是UB。
如何在C ++中以正确的方式执行此操作?
完全不使用char *
!
只需使用 std::string 即可。
请注意,std::string
为您提供了 c_str()函数,该函数可以获取基础字符串。除非,您对将char *
的所有权传递给c风格的api感到困扰,否则您应该始终在c ++中使用std::string
。
答案 1 :(得分:5)
第三种选择也是错误的,因为你没有为它分配足够的内存。您正在尝试将大小为5的字符串复制到大小为1的缓冲区,这意味着pname[1]
之后的数据被错误地覆盖并消失了。
如果您很幸运,您可能会看到运行时错误,例如内存访问违规,或者您不会看到任何内容,但背后的数据已损坏,例如您的银行帐户,而您永远不会知道直到...
正确的方法是始终分配足够的内存来复制到。正如Als所指出的,在C ++中更好的方法是使用std::string
,因为它可以让你免于手动管理内存(分配,增长,释放等)。
如,
class Person
{
std::string pname;
public:
Person(char* name)
{
pname = name;
}
void display()
{
cout << pname << endl;
}
};
void main()
{
Person obj("ABCD");
obj.display();
}
答案 2 :(得分:4)
您必须为成员变量pname
分配内存,但是,当您只使用char*
时,我不知道您为何要使用string
:
std::string pname;
//...
pname = std::string(name);
如果您有充分的理由必须使用char*
,那么请执行以下操作:
// initialize the pname
pname = new char[strlen(name)];
// copy the pname
strcpy(pname, name);
您不需要在字符串末尾为null
分配额外空格的原因 - 终止是因为使用双引号"blah"
会自动生成null
- 终止字符串。
答案 3 :(得分:3)
如果您从事C ++业务,那么是时候代表STL字符串转储char指针了:
#include <string>
class Person
{
std::string the_name;
public:
Person(std::string name) : the_name(name)
{ ...
同样使用cout。
答案 4 :(得分:2)
在你的案例III中,你做了pname = (char*) malloc(sizeof(char));
,它为单个字符分配了足够的内存。但是,strcpy无法知道这一点,并且在该字节之后直接写入任何内存,直到它完成对传递给函数的所有char *的复制。这被称为缓冲区溢出,虽然这可能会立即起作用,但它可能会破坏一些东西。如果您只想复制char *的一个子部分,可以查看strncpy
,其复制长度为(API reference here)。如果你使用它,请务必自己添加空终止字符,因为如果只复制部分字符串,strncpy将不包括它。
答案 5 :(得分:2)
pname = (char*) malloc(sizeof(char));
工作很巧合,对strcpy
的调用会写入尚未分配的内存中,因此可能会导致程序崩溃。
初始化缓冲区的一种更简单的方法是:
pname = strdup(name);
或
pname = strndup(name, strlen(name));
请参阅http://linux.die.net/man/3/strdup。
此外,您必须考虑通过在类析构函数中调用free(pname);
来释放分配的内存。
总而言之,所有这些都可以通过使用C ++ std :: string类来避免,正如每个人所提到的那样。
答案 6 :(得分:2)
正确的是案例II!
是的,如果我错了,它会崩溃,因为你要将数据复制到一个非初始化的指针。
案例III也是错误的,但它现在有效,因为你的测试字符串很小!如果你尝试使用更大的字符串,它会破坏内存,因为你正在将一个大字符串复制到一个小的分配空间。
在某些系统中,malloc使用集群,因此它通过分配内存块而不是逐字节分配来工作。这意味着当你使用malloc来分配单个字节时(就像你在案例III中所做的那样),它会分配一些以达到它可以处理的最小内存块,这就是为什么你可以移动超过1个字节而不会崩溃的原因系统。