为什么你需要在新字符(str.length())上添加一个



代码:

string str = "Whats up";
char *c = new char[str.length() + 1];

我仍然可以写char *c = new char[str.length()];

在长度上加+1有什么意义?

您的代码:

string str = "Whats up";
char *c = new char[str.length() + 1];

您的问题:

在长度上加+1有什么意义?

真正的问题应该是:在你的C++程序中使用C风格的字符串有什么意义?您确定需要它们吗?

让我解释一下您的两个代码行中到底发生了什么:

"Whats up"是一个字符串文字,即一系列常量字符,准确地说是char const[9]。第 9 个字符是编译器自动添加的空字符 ''。所以数组实际上看起来像这样:

{ 'W', 'h', 'a', 't', 's', ' ', 'u', 'p', '' }

事实上,你也可以写:

char const array[9] = { 'W', 'h', 'a', 't', 's', ' ', 'u', 'p', '' };
std::string s = array;

所以你有一个char const[9]数组,用于初始化std::string。这里实际使用了std::string的哪个构造函数?如果你看一下 http://en.cppreference.com/w/cpp/string/basic_string/basic_string,你会发现这个:

basic_string( const CharT* s,
              const Allocator& alloc = Allocator() );

请记住,std::string实际上是std::basic_string<char>的typedef,所以在这种情况下,你的CharT是一个char,构造函数读作:

string( const char* s,
              const Allocator& alloc = Allocator() );

同时忽略alloc参数。它太复杂了,无法向初学者解释,而且它有一个默认参数,因此您几乎可以一直忽略它。这意味着你最终会得到:

string( const char* s);

这本身就是另一种写作方式:

string(char const *s);

所以你可以用char const *初始化std::string,你的代码会char const[9]传递给构造函数。这是有效的,因为数组会自动转换为指向其第一个元素的指针

因此,std::string获取您的数组,将其视为指针并复制 9 个字符。数组大小信息9 会丢失,但这并不重要,因为您有终止'',因此std::string知道在哪里停止。

目前为止,一切都好。您有一个包含 "Whats up" 副本的 std::string 对象。您的下一行是这样的:

char *c = new char[str.length() + 1];

首先,考虑str.length().length函数返回字符串大小,而不是数组大小。因此,尽管您传递了 9 个字符来构造字符串,但 length 返回 8。这是有道理的,因为std::string旨在让您忘记指针、数组和内存操作。这是文本,这里的文本有 8 个字符。

因此,str.length() + 1等于 8 + 1 = 9,因此您的代码行等效于:

char *c = new char[9];

您已经创建了一个名为 c 的指针,初始化为指向一个内存位置,该位置有足够的空间容纳 9 个字符,尽管当前存储在那里的内容尚未定义,因此您一定不要尝试从那里读取:

c
|
|
+------+
       |
       v
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
...|  |  |  |  |  |  |  |  |  |  |  | ...
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
       0  1  2  3  4  5  6  7  8

您创建的std::stringc指向的内存之间没有任何关系。他们住在完全不同的地方:

 c
 |
 |
 +------+
        |
        v                                   0  1  2  3  4  5  6  7  8
 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
... |  |  |  |  |  |  |  |  |  |  |  | ... |W |h |a |t |s |  |u |p || ...
 +-+-+-+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
        0  1  2  3  4  5  6  7  8           ^
                                            |
                                            |
           str -------( c_str() )-----------+

但是,如果您使用像 strcpy 这样的 C 函数将std::string的内容复制到这 9 个字符,那么很明显为什么您需要 9 个字符的空间:

strcpy(c, str.c_str());

strcpy查看源(str.c_str()(并复制一个接一个的字符进行c,直到找到''str内部以结尾,所以一切都很好。该函数从此图片右侧的 0 到 8 开始,并将所有内容复制到左侧的 08

这最终回答了您的问题:左侧必须有 9 个字符的空间。否则,strcpy将尝试将最后一个字符((写入不允许触摸的内存位置。这会导致未定义的行为,并可能导致例如崩溃或随机崩溃。

在可容纳 9 个字符的空间下,strcpy成功完成:

 c
 |
 |
 +------+
        |
        v                                   0  1  2  3  4  5  6  7  8
 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
... |  |W |h |a |t |s |  |u |p ||  | ... |W |h |a |t |s |  |u |p || ...
 +-+-+-+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
        0  1  2  3  4  5  6  7  8           ^
                                            |
                                            |
           str -------( c_str() )-----------+

故事的寓意:

使用 std::string .复制std::string可能会在内部使用非常相似的机制,但使您(以及其他烦人的事情(不必记住"+ 1"规则:

std::string s1 = "Whats up";
std::string s2 = "...";
s2 = s1;

std::string不同,C 风格的字符串使用特殊字符来表示其结尾,即空字符'',多余的一个字符用于存储终止''

你的代码中有一个缺陷。

它应该是

c* = new char[str.length()+1];

s.length((+1 不会做任何事情。

尽管编译器会自动为您设置 c 字符串大小,但最好指定确切的大小,以便您了解所有内容的机制。

C 字符串

始终需要比 std::string 值多一个空格,因为 C 字符串是在数组末尾具有终止 null 值的字符数组。 这就是为什么你总是在最后为NULL 留出空间。

最新更新