我是C ++的初学者,我目前正在使用字符串。
我的问题是为什么在编译我在下面提供的代码时,我可以在使用索引表示法时获取字符串的字符,但无法使用cout
获取字符串本身?
这是代码:
#include <iostream>
#include <string>
using namespace std;
int main()
{
string original; // original message
string altered; // message with letter-shift
original = "abc";
cout << "Original : " << original << endl; // display the original message
for(int i = 0; i<original.size(); i++)
altered[i] = original[i] + 5;
// display altered message
cout << altered[0] << " " << altered[1] << " " << altered[2] << endl;
cout << "altered : " << altered << endl;
return 0;
}
当我运行此字符串时,字符串altered
中的字符会正确显示在此行中:
cout << altered[0] << " " << altered[1] << " " << altered[2] << endl;
但字符串本身不会显示此行:
cout << "altered : " << altered << endl;
我想知道为什么会这样。
答案 0 :(得分:5)
您尚未调整altered
字符串的大小以适应循环前original
字符串的长度,因此您的代码会显示未定义的行为:
altered[i] = original[i] + 5; // UB - altered is empty
要解决此问题,请在循环前调整altered
:
altered.resize(original.size());
或使用std::string::operator+=
或类似内容附加到altered
:
altered += original[i] + 5;
这样,它在循环之前可以为空,它会自动调整大小以包含附加字符。
UB在这里发生的方式是你在静态数组中成功编写数据,std::string
用于短字符串优化(std::string::operator[]
不检查你是否正在访问此数组超过std::string::size()
),但std::string::size()
仍为0
,以及std::string::begin() == std::string::end()
。
这就是你可以单独访问数据的原因(同样,使用UB):
cout << altered[0] << " " << altered[1] << " " << altered[2] << endl;
但是cout << aligned
没有打印任何内容,考虑到operator<<
的{strong>简化 std::string
定义看起来功能,如下所示:
std::ostream &operator<<(std::ostream &os, std::string const& str)
{
for(auto it = str.begin(); it != str.end(); ++it) // this loop does not run
os << *it;
return os;
}
在一句话中, std::string
不知道你对它的基础数组做了什么,并且你的意思是字符串的长度增长。
总结一下,<algoritm>
做这种转变的方式:
std::transform(original.begin(), original.end(),
std::back_inserter(altered), // or altered.begin() if altered was resized to original's length
[](char c)
{
return c + 5;
}
(必填标题:<algorithm>
,<iterator>
)
答案 1 :(得分:2)
在您的程序字符串altered
中为空。它没有任何元素。
因此,您可能不会使用下标运算符来访问字符串中不存在的元素
altered[i] = original[i] + 5;
因此,您可以使用新字符附加字符串。有几种方法可以做到这一点。例如
altered.push_back( original[i] + 5 );
或
altered.append( 1, original[i] + 5 );
或
altered += original[i] + 5;
由于您可能不会为空字符串应用下标运算符来分配值,因此最好使用基于范围的for循环,因为实际上索引本身并未使用。例如
for ( char c : original ) altered += c + 5;
答案 2 :(得分:2)
但是,我将把它作为我的解决方案,因为它是一个现代C ++&#34;方法(加上更短更完整)。 这是我对上面给出的内容的替代方案: 请注意代码的显着减少: - ) 即使我按照LogicStuff的方式进行操作,我仍然会这样做: 但是,我实际上并不推荐这种方法,因为altered
的大小始终为零 - 使用您尝试将索引original
中的值从altered
复制到altered
的索引不会拥有< / em>的。正如LogicStuff所说,这是未定义的行为 - 它不会产生错误,因为当我们使用带有std::string
的索引时,我们实际上正在调用std::string
上的运算符来访问{{1字符串的字段。使用data
运算符在C ++标准中定义为无范围检查 - 这就是为什么没有抛出错误的原因。访问索引的安全方式是使用[]
方法:at(i)
如果altered.at(i)
}将会抛出范围错误 p>
altered.size() <= i
string original = "abc";
string altered = original;
for (auto& c : altered) c += 5; // ranged for-loop - for each element in original, increase its value by 5
cout << altered << endl;
string original = "abc"
string altered = ""; // this is actually what an empty string should be initialised to.
for (auto c : original) altered += (c+5);
和字符串追加/字符串连接的工作方式。在这个小例子中它很好,但如果push_back()
是一个字符串,拿着要解析的书的前10页怎么办?或者如果它是一百万个字符的原始输入怎么办?然后,每当original
的{{1}}字段达到其限制时,都需要通过系统调用重新分配,并复制data
的内容并为{{1}分配先前的内容}字段被释放。这是一个显着的性能障碍,相对于altered
的大小而言 - 这只是不好的做法。执行完整复制然后迭代总是更有效,对复制的字符串进行必要的调整。这同样适用于altered
。