当char[]
中的实际字符序列可能小于最大大小时,字符串构造器如何处理固定大小的char[]
?
char foo[64];//can hold up to 64
char* bar = "0123456789"; //Much less than 64 chars, terminated with '\0'
strcpy(foo,bar); //Copy shorter into longer
std::string banz(foo);//Make a large string
在此示例中,banz
对象字符串的大小将基于原始char *长度还是将其复制到的char []中?
答案 0 :(得分:7)
首先,您必须记住(或知道)C ++中的char
字符串实际上称为 以空值结尾的 字节字符串 。 以null结尾的位是一个特殊字符('\0'
),它指示字符串的结尾。
您要记住(或知道)的第二件事是数组自然会衰减到指向数组第一个元素的指针。对于您示例中的foo
,当您使用foo
时,编译器实际上会执行&foo[0]
。
最后,如果我们看例如this std::string
constructor reference,您将看到有一个接受const CharT*
的重载(数字5)(对于正常的CharT
字符串,char
是char
)。 / p>
通过
将所有内容放在一起std::string banz(foo);
您将指针传递到foo
的第一个字符,并且std::string
构造函数会将其视为以空字符结尾的字节字符串。通过找到空终止符,它知道字符串的长度。数组的实际大小无关紧要,不会使用。
如果要设置std::string
对象的大小,则需要通过传递长度参数(构造函数参考中的变量4)来明确地做到这一点:
std::string banz(foo, sizeof foo);
这将忽略空终止符,并将banz
的长度设置为数组的大小。请注意,空终止符仍将存储在字符串中,因此将指针(例如通过c_str
函数检索)传递给期望以空终止字符串的函数,则该字符串看起来很短。另请注意,空终止符之后的数据将未初始化,并且具有不确定的内容。 必须在使用该数据之前对其进行初始化,否则您将拥有undefined behavior(在C ++中,即使读取不确定的数据也将是UB)。
如MSalters的评论中所述,从读取未初始化且不确定的数据的UB也将使用显式大小来构造banz
对象。它通常会起作用,不会导致任何问题,但是确实会违反C ++规范中列出的规则。
修复起来很容易:
char foo[64] = { 0 };//can hold up to 64
上面的代码会将数组的 all 初始化为零。以下strcpy
调用将不会触及终止符之外的数组数据,因此将初始化数组的其余部分。
答案 1 :(得分:4)
被调用的构造函数是一个以const char*
作为参数的构造函数。该构造函数尝试复制该指针指向的字符数据,直到到达第一个NUL终止符。如果没有这样的NUL终止符,则构造函数的行为为 undefined 。
您的foo
类型通过指针衰减转换为char*
,然后在调用站点处隐式转换为const char*
。
也许可能有一个以std::string
作为参数的模板化const char[N]
构造函数,这将允许插入多个NUL字符(std::string
类毕竟可以对此表示支持),但是没有引入它,而现在这样做将是一项重大突破;使用
std::string foo{std::begin(foo), std::end(foo)};
还将复制整个数组foo
。