代码:
string str = "Whats up";
char *c = new char[str.length() + 1];
我仍然可以写char *c = new char[str.length()];
在长度上添加+1有什么意义?
答案 0 :(得分:6)
您的代码:
string str = "Whats up";
char *c = new char[str.length() + 1];
你的问题:
在长度上添加+1有什么意义?
真正的问题应该是:在C ++程序中使用C风格的字符串有什么意义?你确定需要它们吗?
让我解释一下你的两个代码行中到底发生了什么:
"Whats up"
是一个字符串文字,即一系列不变的字符,确切地说是char const[9]
。第9个字符是编译器自动添加的空字符,'\0'
。所以数组实际上是这样的:
{ 'W', 'h', 'a', 't', 's', ' ', 'u', 'p', '\0' }
事实上,你也可以写:
char const array[9] = { 'W', 'h', 'a', 't', 's', ' ', 'u', 'p', '\0' };
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);
因此,您可以使用std::string
初始化char const *
,并且您的代码会将构造函数传递给char const[9]
。这是有效的,因为数组会自动转换为指向其第一个元素的指针。
因此std::string
接受您的数组,将其视为指针并复制9个字符。数组大小信息9
已丢失,但无关紧要,因为您有终止'\0'
,因此std::string
知道停止的位置。
到目前为止,这么好。您有一个std::string
对象,其中包含"Whats up"
的副本。你的下一行是这样的:
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::string
和内存c
之间没有任何关系。他们住在完全不同的地方:
c
|
|
+------+
|
v 0 1 2 3 4 5 6 7 8
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
... | | | | | | | | | | | | ... |W |h |a |t |s | |u |p |\0| ...
+-+-+-+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
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
,直到找到'\0'
。 str
内部以\0
结尾,所以一切都很好。该功能从此图片右侧的 0 到 8 ,并将所有内容复制到 0 到 8 左
这最终回答了你的问题:左边必须有9个字符的空格。否则,strcpy
将尝试将最终字符(\0
)写入您不允许触摸的内存位置。这导致未定义的行为并且可能导致例如崩溃或随机崩溃。
有9个字符的空间,strcpy
成功完成:
c
|
|
+------+
|
v 0 1 2 3 4 5 6 7 8
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
... | |W |h |a |t |s | |u |p |\0| | ... |W |h |a |t |s | |u |p |\0| ...
+-+-+-+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
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;
答案 1 :(得分:2)
与std::string
不同,C风格的字符串使用特殊字符来表示其结尾,即空字符'\0'
,额外的一个字符用于存储终止'\0'
。
答案 2 :(得分:-6)
您的代码存在缺陷。
应该是
c* = new char[str.length()+1];
s.length()+ 1不会做任何事情。
虽然编译器会自动为你设置c字符串大小,但最好指定确切的大小,这样你就可以看到所有内容的机制。
C字符串总是需要比std :: string值多一个空格,因为c字符串是在数组末尾具有终止空值的字符数组。这就是为什么你总是在最后给空间留出空间。