按索引

时间:2015-11-22 21:21:39

标签: c++ string

我是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;

我想知道为什么会这样。

3 个答案:

答案 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)

altered的大小始终为零 - 使用您尝试将索引original中的值从altered复制到altered的索引不会拥有< / em>的。正如LogicStuff所说,这是未定义的行为 - 它不会产生错误,因为当我们使用带有std::string的索引时,我们实际上正在调用std::string上的运算符来访问{{1字符串的字段。使用data运算符在C ++标准中定义为无范围检查 - 这就是为什么没有抛出错误的原因。访问索引的安全方式是使用[]方法:at(i)如果altered.at(i) 范围错误 p>

但是,我将把它作为我的解决方案,因为它是一个现代C ++&#34;方法(加上更短更完整)。

这是我对上面给出的内容的替代方案:

altered.size() <= i

请注意代码的显着减少: - )

即使我按照LogicStuff的方式进行操作,我仍然会这样做:

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