2

我在处理 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:正如预期的那样,系统崩溃。

案例二的输出:

A B C D

案例 III 的输出:

A B C D

所以,我不确定为什么案例 II 和 III 会产生相同的输出!!!!..... 我应该如何在一个类中初始化一个 char 指针?

4

7 回答 7

11

第三种情况会调用未定义的行为,因此在这种情况下可能会发生任何事情。
在这种情况下,您正在写入超出分配内存的范围,这可能会或可能不会崩溃,但它是一个 UB。

如何在 C++ 中以正确的方式做到这一点?
根本不使用char *
只需简单地使用std::string

请注意,它std::string为您提供了c_str()函数,该函数可以为您提供基础字符串。除非,您担心将 a 的所有权传递char *给您应该始终std::string在 c++ 中使用的 c 样式 api。

于 2012-04-23T15:07:54.687 回答
5

第三个选项也是错误的,因为您没有为它分配足够的内存。您正在尝试将大小为 5 的字符串复制到大小为 1 的缓冲区,这意味着之后的数据pname[1]被错误地覆盖并消失了..

如果幸运的话,您可能会看到诸如内存访问冲突之类的运行时错误,或者除了其背后的数据已损坏(例如您的银行帐户)之外,您什么也看不到,直到......

正确的方法是始终分配足够的内存进行复制。std::string正如 Als 所指出的,C++ 中更好的方法是使用,因为它可以让您从手动管理内存(分配、增长、释放等)中解脱出来。

例如,

class Person
{
    std::string pname;
public:
    Person(char* name)
    {
        pname = name;
    }

    void display()
    {
        cout << pname << endl;
    }
};

void main()
{
    Person obj("ABCD");
    obj.display(); 
}
于 2012-04-23T15:09:12.907 回答
4

你必须为你的成员变量分配内存pname,但是,我不知道你为什么要使用 achar*而你只能使用 a string

std::string pname;

//...

pname = std::string(name);

如果有充分的理由必须使用 a char*,那么请执行以下操作:

// initialize the pname
pname = new char[strlen(name)];

// copy the pname
strcpy(pname, name);

您不需要在字符串末尾为null-termination 分配额外空间的原因是因为使用双引号"blah"会自动生成null-terminated 字符串。

于 2012-04-23T15:07:19.747 回答
3

如果您从事 C++ 业务,那么是时候代表 STL 字符串转储 char 指针了:

#include <string>

class Person
{
    std::string the_name;
public:
    Person(std::string name) : the_name(name)
    { ...

cout 也同样使用。

于 2012-04-23T15:10:35.300 回答
2

在您的案例 III 中,您这样做了,它为单个 charpname = (char*) malloc(sizeof(char));分配了足够的内存。但是,strcpy 无法知道这一点,它会直接写入该字节之后的任何内存,直到它完成对您传递给函数的所有 char* 的复制。这被称为缓冲区溢出,虽然这可能会立即起作用,但它可能会在未来破坏某些东西。如果您只想复制 char* 的一小部分,您可以查看,它复制到一定长度(API 参考此处)。如果您使用它,请务必自己添加以空值结尾的字符,因为如果您只复制字符串的一部分,strncpy 将不会包含它。strncpy

于 2012-04-23T15:09:48.707 回答
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类来避免,正如大家所说的。

于 2012-04-23T15:10:14.193 回答
2

正确的是案例二!

是的,如果我错了,它会崩溃,因为您将数据复制到未初始化的指针。

案例 III 也是错误的,但它现在可以工作,因为您的测试字符串很小!如果您尝试使用更大的字符串,则会损坏内存,因为您将大字符串复制到分配的小空间。

在某些系统中,malloc 与集群一起工作,因此它通过分配内存块而不是逐字节分配来工作。这意味着当您使用 malloc 分配单个字节时(就像您在案例 III 中所做的那样),它会分配更多的内存以达到它可以处理的最小内存块,这就是为什么您可以移动超过 1 个字节而不会崩溃的原因系统。

于 2012-04-23T15:17:35.020 回答