用于字符串循环的CPP从意外索引处开始吗?

时间:2019-03-06 19:05:27

标签: c++ string loops for-loop

for(unsigned long i=add1.length()-1;i>=0;i--){
        int presum = stoi(to_string(add1.at(i))) + stoi(to_string(add2.at(i))) + curcarry;

我有这段代码在字符串(add1)的各个字符之间循环,从字符串的最后一个到第一个。然后,它使用索引获取当前字符并将其转换为整数,并将其添加到保证长度相等的另一个字符串(add2)的当前字符中。

我收到一个字符串out_of_range错误,在查看调试菜单时,我看到虽然我的字符串都应该是正确的,但长度只有3个,但是i值最终以18446744073709551615

附加的是i值的图像,以及长度显然不相关的字符串。发生了什么事?

调试值:

enter image description here

2 个答案:

答案 0 :(得分:5)

对于无符号值i>=0始终为true。因此,您的索引为零,然后回绕到最大的unsigned long值,在您的情况下为18446744073709551615。

像这样编写循环(例如)

for (unsigned long i = add1.length(); i-- > 0; ) {

答案 1 :(得分:1)

将评论收集到johnanswer

由于无符号值永远不会小于0,因此您将陷入无穷循环,并在达到0后环绕并以一个巨大值结束。

您现在有几个选择:

  1. 在递减之前检查索引(有关此内容的幽默观点,请参见here):

    for (unsigned long i = add1.length(); i-- > 0; )

  2. 通过围绕下溢行为进行补偿:

    for (unsigned long i = add1.length() - 1; i < add1.length(); --i)

  3. 反向迭代器–我认为这是最可取的变体。但是我们需要初始化两个变量并依赖于序列(逗号)运算符,有些可能不赞成...:

    for(auto i1 = add1.rbegin(), i2 = add2.rbegin(); i1 != add1.rend(); ++i1, ++i2)
    //                                      ++, even if going reverse!   ^     ^
    {
        // use *i1 and *i2 here
    }

您可能更喜欢size_t类型而不是unsigned long,它将始终具有适当的大小,并且是std::string::length或任何STL容器的size函数返回的类型也是如此。

不过,我看不到在处理add2是较短字符串的情况下–上面的 any 会导致未定义的行为!如果add1是较短的那个,则您将从add2的中间开始。涵盖两者:

decltype(add1.rend()) ir, er;
for(auto i1 = add1.rbegin(), i2 = add2.rbegin(); ; ++i1, ++i2)
                                                ^ leaving out condition!
{
    if(i1 == add1.rend())
    {
        ir = i2;          // handle rest of add2 after loop
        er = add2.rend(); // needed to be able to compare with appropriate end
        break;
    }
    if(i2 == add2.rend())
    {
        ir = i1;          // handle rest of add1 after loop
        er = add1.rend(); // needed to be able to compare with appropriate end
        break;
    }
    // adding the values as you did before
}
for(; ir != er; ++ir)
{
    // special handling: append whatever remained from add1 or add2
}

最后:stoi(to_string(addN.at(i))) –我简直无法想象效率低下的任何事情……

  1. at会进行范围检查,但是通过循环,您已经确定了范围(假定已根据上述变体之一对其进行了修复),因此索引运算符(addN[i])不会,是更好的选择。诚然,这次,因为循环不正确,at阻止了您未定义的行为。因此,您可以先从at开始,然后在知道代码正确运行后立即替换为索引运算符...
  2. (更为沉重):中间字符串是(实际上)很长一段距离。由于C ++标准保证数字(仅用于!!!)具有后续代码点,因此您可以简单地执行addN[i] - '0'(或*iX - '0',如果使用迭代器)来获取值。如果您想保持便携性,也不要像字母if('A' <= c && c <= 'Z') c += 'a' - 'A'那样使用字母(对于此示例,已经有function这样做,您绝对应该这样做)。

如果我们已经达到了效率:如果您将所有新密码的数字都前缀(在结果字符串之前)(嗯,没有显示代码,但是我可以想象您正在这样做……),您将一次又一次地移动所有已插入的值。宁可追加新数字,完成后将std::reverse应用于结果。