这两种不同的字符串初始化方式在C++中有什么区别?

5
源代码
#include <iostream>
#include <string>
using namespace std;
int main(){
    std::string s{'a', 'b', '\0', 'c'};
    std::string s1="ab\0c";
    cout<<s.size()<<" "<<s<<endl;
    cout<<s1.size()<<" "<<s1<<endl;
    return 0;
}

输出结果为:

4 abc
2 ab

我想知道为什么会发生这种现象,以及在C++中这两种初始化方式之间是否有区别?谢谢。


3
我无法复制粘贴图片。 - Guillaume Racicot
3
请将您的源代码以文本形式直接放在问题中。许多用户不会查看屏幕截图中的代码。 - François Andrieux
1
这是我第一次提问,非常抱歉。我已经解决了问题,并再次感谢。 - Lixun Bai
1
很不幸,用户们往往会在第一时间对新帖进行负面评价,而不是纠正新用户的错误。 - François Andrieux
2
一个通用的提示,除非你特别需要刷新缓冲区,否则不要使用 endl。只需使用 "\n" 即可。当写入终端控制台时,它通常会进行行缓冲,而当写入其他管道时,您不必每次都刷新缓冲区,从而避免了不必要的开销。 - wich
显示剩余2条评论
2个回答

8
对于s,您正在匹配接受字符初始化列表的构造函数:在此处列表中为(9)。 string类允许您从包括嵌入式NULs的任意数据构建字符串,就像在这种情况下一样。初始化列表知道其自己的长度,因此string捕获所有字符。
对于s1,在上面链接的列表中匹配的构造函数是(5),它接受一个const char* - 编译器在调用该构造函数之前将提供的char数组衰减为这样的指针,这意味着构造函数没有数组的长度信息。相反,它假定您有意使用ASCIIZ NUL结束符约定(如“C”字符串),并扫描数据以查找第一个NUL,并将其视为终止符。因此,只有2个字符被捕获到字符串中。
请注意,您可以使用明确的方式捕获4个字符...
std::string s1 { "ab\0c", 4};

...与列表中的构造函数(4)相匹配。

Rakete1111在下面的评论中提出了另一种创建这种字符串的新方法:auto s1 = "ab\0c"s;


谢谢,你帮我更好地理解了。通常,我没有注意到这些差异。我应该更加关注STL的构造函数。顺便问一下,这是否意味着第二种方式类似于C语言中的char s[] =“ab\0c”? - Lixun Bai
不客气。关于C语言中的char s[ ]="ab\0c" - 是和否 - s会捕获所有4个字符,但如果你将它传递给一个期望const char*的函数,它会衰减,所以例如strdup(s)会返回一个新的缓冲区,缺少最后的'c',类似于string构造函数的行为。希望这有助于澄清事情! - Tony Delroy
哦,我明白了。感谢你澄清了我的困惑,我学到了很多! - Lixun Bai
1
您也可以使用字符串字面值:auto s1 = "ab\0c"s; - Rakete1111
@Rakete1111:说得好 - 我希望我最近使用的所有编译器都已经支持了这个! - Tony Delroy
显示剩余3条评论

0

原因是std::strings不是一等对象,它们是标准库对象,必须遵守C++语法规则。与字符串字面量不同,它们是一等构造。

std::string允许嵌入null字符,因此当它被初始化为数组时,它会看到整个数组。当它被初始化为字符串字面量时,它看到的是一个char*,它唯一检测字符串结尾的方法是搜索null字符。


2
原因与std::string是否是“一等对象”或遵循“C语法规则”无关(它们显然不遵循这些规则)。 - Lightness Races in Orbit
有趣的是,C++实际上为最终用户和库代码提供了足够的能力来捕获字符串字面值构造函数参数的长度 - 例如template <size_t N> string(char (&arg)[N]) { ...can use N in body... };,但是std::string API选择不使用这种方法。 - Tony Delroy
2
FYI:{'a','b','\0','c'}不是一个数组。它是一个std::initialization_list<char>。从技术上讲,字符串字面值是一个数组。它的类型是const char[N] - NathanOliver

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接