为什么在调用istream::getline后可以打印字符串'\0'之后的部分
Why the part after '\0' of a string can be printed after calling istream::getline
为了巩固对istream::getline
的理解,我测试了以下代码:
#include <iostream>
#include <sstream>
using namespace std;
int main()
{
string s("abcdefgh \nijklmnopqrst");
string s1;
stringstream ss(s);
ss >> s1;
cout <<"s1:"<< s1 << endl;
ss.getline(&s1[0], 250, '\n');
if(s1[0]==' '&&s1[1]=='[=11=]')
cout << "new s1:"<<s1 << endl;
getchar();
return 1;
}
据我了解,ss.getline
调用将提取空格和终止符 '\n'
然后将空格和 '[=16=]'
分配给 s1[0]
和 s1[1]
分别指代cpluscplus,s1[1]
之后的字符保持不变,因为一旦到达终止符,提取就会停止。但是一个意想不到的问题是可以打印出s1
。控制台打印
s1:abcdefgh
new s1: cdefgh
为什么在这种情况下可以打印字符串的'\0'之后的部分?
std::string
允许包含空字符。与传统的 c 字符串不同,空字符不用于确定其长度 (尽管在其末尾存储了一个空字符以允许它与 c 字符串接受函数一起使用)。它的长度是单独存储的,可以使用 size()
或 length()
成员函数检索。所以当你用operator<<
打印出来的时候,运算符并没有在发现null时停止打印,而是在打印完s1.size()
个字符后停止打印。
C++ 字符串和使用它们的 I/O 工具并不真正关心 NUL
字节。他们知道自己的长度,如果他们的数据是 N 字节长,那么其中一些字节是否 NUL
并不重要,iostream
s 将继续前进直到达到记录的长度(设置时你阅读 ss >> s1;
).
当您这样做时,您明显滥用了您的字符串:
ss.getline(&s1[0], 250, '\n');
因为 &s1[0]
正在绕过 std::string
的安全访问器来获取原始 char*
(更糟糕的是,你告诉 getline
它最多可以提取 250 个字符,当底层缓冲区可能小得多),并保持 length/capacity 信息不变(因此它仍然认为它包含 ss >> s1;
读取的许多字符)。
您真的想要 std::getline
,它面向 std::string
,并且可以正常工作(包括根据需要为您调整输出大小,调整已知字符串长度,确保现有数据不会遗留在地点):
std::getline(ss, s1, '\n');
为了巩固对istream::getline
的理解,我测试了以下代码:
#include <iostream>
#include <sstream>
using namespace std;
int main()
{
string s("abcdefgh \nijklmnopqrst");
string s1;
stringstream ss(s);
ss >> s1;
cout <<"s1:"<< s1 << endl;
ss.getline(&s1[0], 250, '\n');
if(s1[0]==' '&&s1[1]=='[=11=]')
cout << "new s1:"<<s1 << endl;
getchar();
return 1;
}
据我了解,ss.getline
调用将提取空格和终止符 '\n'
然后将空格和 '[=16=]'
分配给 s1[0]
和 s1[1]
分别指代cpluscplus,s1[1]
之后的字符保持不变,因为一旦到达终止符,提取就会停止。但是一个意想不到的问题是可以打印出s1
。控制台打印
s1:abcdefgh
new s1: cdefgh
为什么在这种情况下可以打印字符串的'\0'之后的部分?
std::string
允许包含空字符。与传统的 c 字符串不同,空字符不用于确定其长度 (尽管在其末尾存储了一个空字符以允许它与 c 字符串接受函数一起使用)。它的长度是单独存储的,可以使用 size()
或 length()
成员函数检索。所以当你用operator<<
打印出来的时候,运算符并没有在发现null时停止打印,而是在打印完s1.size()
个字符后停止打印。
C++ 字符串和使用它们的 I/O 工具并不真正关心 NUL
字节。他们知道自己的长度,如果他们的数据是 N 字节长,那么其中一些字节是否 NUL
并不重要,iostream
s 将继续前进直到达到记录的长度(设置时你阅读 ss >> s1;
).
当您这样做时,您明显滥用了您的字符串:
ss.getline(&s1[0], 250, '\n');
因为 &s1[0]
正在绕过 std::string
的安全访问器来获取原始 char*
(更糟糕的是,你告诉 getline
它最多可以提取 250 个字符,当底层缓冲区可能小得多),并保持 length/capacity 信息不变(因此它仍然认为它包含 ss >> s1;
读取的许多字符)。
您真的想要 std::getline
,它面向 std::string
,并且可以正常工作(包括根据需要为您调整输出大小,调整已知字符串长度,确保现有数据不会遗留在地点):
std::getline(ss, s1, '\n');