【问题标题】:out_of_range at memory location c++内存位置c ++的out_of_range
【发布时间】:2016-09-23 18:58:43
【问题描述】:

在这个程序中,用户在命令行中输入两个字符串。其中一个是数字,另一个是字符串。该程序旨在根据电话拨号每个按钮上的数字/字母来检查输入的数字和字符串是否相等。我得到一个例外,上面写着:

Unhandled exception at 0x775DDAE8 in Project10.exe: 
Microsoft C++ exception: std::out_of_range at memory location 0x0018F158. 

任何帮助都非常感谢你:)

using namespace std;

bool checkPswd(string keyStrokes, string password) {
    string temp;
    string temp2;
    bool temporary = 1;
    string phoneButtons[10] = {
        "", ""
        "abc", "def", "ghi", "jkl",
        "mno", "pqrs", "tuv", "wxyz"
    };
    for (int i = 0; i < keyStrokes.length(); i++) {
        for (int j = 2; j < 10; j++) {
            for (int k = 0; k < phoneButtons[j].length(); k++) {
                temp = phoneButtons[j];
                if (password.at(k) == temp.at(k)) {
                    temp2 = +(char)k;
                }
                else {
                    //do nothing
                }
            }
        }
    }
    cout << temp2;
    for (int m = 0; m < temp2.length(); m++) {
        if (temp2.at(m) == keyStrokes.at(m)) {
            //keep searching
        }
        else {
            return 0;
        }
    }
    return 1;
}

int main(int argc, char ** argv) {
    if (argc != 3) {
        cout << "Please input the key strokes from the phone and the password." << endl;
        return 1;
    }

    string keyStrokes = argv[1];
    string password = argv[2];


    bool check;

    check = checkPswd(keyStrokes, password);
    if (check) {
        cout << "Password Verified" << endl;
    }
    else {
        cout << "Wrong Password" << endl;
    }
    return 0;
}

【问题讨论】:

    标签: c++ exception indexoutofboundsexception


    【解决方案1】:
    for (int k = 0; k < phoneButtons[j].length(); k++) {
    

    k 将在此处从 0 迭代到比 `phoneButtons[j] 中的字符数少 1

       if (password.at(k)  // ... rest of the code is irrelevant
    

    小测验:如果password 的字符数少于phoneButtons[j],你认为这里会发生什么?

    for (int m = 0; m < temp2.length(); m++) {
        if (temp2.at(m) == keyStrokes.at(m)) {
    

    这里也有同样的错误。我还没有分析显示代码的其余部分是否存在相同逻辑错误的任何其他情况。

    这就是为什么你应该使用#include &lt;algorithm&gt;,而不是使用这些容易出错的C 风格for 循环。 std::find() 比这种 C 风格的基于循环的搜索更不容易出错。

    【讨论】:

    • 因此,通过这种解释,我认为您的意思是我应该将其修复为
    • 我的意思是,如果你从 0 迭代到一个字符串的长度,然后访问第二个字符串中相同范围的字符,那么第二个字符串最好至少与第一个字符串,否则最终会出现未定义的行为和崩溃。如果phoneButtons[j].length() 是 5,password 是 2 个字符长,你认为你的代码在尝试 password.at(4) 时会做什么?
    • 那么我如何在字符串数组中搜索单个字符。我感到很困惑。抱歉打扰了,但我大约 2 周半前才开始使用 c++。
    • 我大约在 20 年前开始使用 C++,而且我还在不断地学习新事物。 std::string 有一个 find() 方法,用于查找字符串中的字符。使用find() 比手动迭代字符串中的每个字符要容易得多。在您的情况下,您不断地弄错字符串的结尾,使用不同字符串的长度来迭代有问题的字符串。使用std::stringfind() 方法。或者,在#include &lt;algorithm&gt; 中使用通用的std::find
    【解决方案2】:

    问题原因

    手动循环遍历多个数组并从中读取很容易出错。

    • 您的循环不断覆盖temp2,而不是附加到它。
    • 稍后,您的代码会假设 temp2 的长度与 keystrokes 的长度相同。

    这就是为什么您不像以前那样“手动”进行操作的原因。很难完美地跟踪所有内容,也很难始终知道访问给定索引是安全的。

    更好的方法

    更好的方法是制作一个将一件事翻译成另一件事的函数。您可以使用迭代器实现一种高度通用的方法。迭代器可以引用许多数据结构,甚至可以读/写流。它非常灵活。

    由于编译器可以“看到一切”并且知道所有类型的一切,它可以生成非常高效且非常安全的代码。

    这是一个基于迭代器的实现,它将字符转换为数字,然后比较数字。

    Live Demo

    #include <iostream>
    #include <iterator>
    #include <vector>
    #include <algorithm>
    
    template<
        typename InputIt,
        typename OutputIt,
        typename V = typename std::iterator_traits<InputIt>::value_type,
        typename OutTag = typename std::iterator_traits<OutputIt>::iterator_category,
        typename = typename std::enable_if<
            std::is_same<OutTag, std::output_iterator_tag>::value>::type
    >
    OutputIt convertToDigits(InputIt st, InputIt en, OutputIt dest)
    {
        using ButtonPair = std::pair<char, int>;
        using ButtonLookup = std::vector<ButtonPair>;
    
        static ButtonLookup lookup{
            { 'a', 2 }, { 'b', 2 }, { 'c', 2 },
            { 'd', 3 }, { 'e', 3 }, { 'f', 3 },
            { 'g', 4 }, { 'h', 4 }, { 'i', 4 },
            { 'j', 5 }, { 'k', 5 }, { 'l', 5 },
            { 'm', 6 }, { 'n', 6 }, { 'o', 6 },
            { 'p', 7 }, { 'q', 7 }, { 'r', 7 }, { 's', 7 },
            { 't', 8 }, { 'u', 8 }, { 'v', 8 },
            { 'w', 9 }, { 'x', 9 }, { 'y', 9 }, { 'z', 9 }
        };
    
        auto ls = std::begin(lookup);
        auto le = std::end(lookup);
    
        std::for_each(st, en, [&](char c) {
            if (c >= 'A' && c <= 'Z')
                c += 'a' - 'A';
            auto m = std::find_if(ls, le, [&](auto const& p) {
                return p.first == c;
            });
            if (m != le)
                *dest++ = m->second;
        });
        return dest;
    }
    
    int main(int argc, char **argv)
    {
        if (argc != 3)
            return 1;
        std::vector<std::string> args(argv, argv + argc);
    
        std::string const& numbers = args.at(1);
        std::string const& characters = args.at(2);
    
        std::cout << " Chars: \"" << characters << '\"' << std::endl;
        std::cout << " Check: \"" << numbers << '\"' << std::endl;
    
        std::vector<int> digits;
    
        convertToDigits(std::begin(characters), std::end(characters), 
            std::back_inserter(digits));
    
        for (int& digit : digits)
            digit += '0';
    
        std::cout << "Expect: \"" << std::string(
            std::begin(digits), std::end(digits)) << '\"' << std::endl;
    
        bool match = std::equal(std::begin(numbers), std::end(numbers),
                std::begin(digits), std::end(digits));
    
        return !match;
    }
    

    工作原理

    convertToDigits 采用一对迭代器,指的是要转换的范围的开始和结束。第三个参数是一个输出迭代器,用于存储输出。

    main 函数为数字 int 值设置了一个 vector,并使用 std::back_inserter 生成一个输出迭代器,每次将向量存储到其中时,它都会对向量执行 push_back

    翻译函数只是在查找表中进行线性搜索。我选择了线性搜索,因为列表太小了。它将紧密地打包在内存中,并且开销非常低。无论如何,速度在这里并不重要。它是瞬时的。

    我将'0'添加到数字值以将它们转换为ASCII,因此它们与命令行字符串相当。

    剩下的就很明显了,看转换后的字符串是否匹配预期的字符串,如果匹配则以0退出码退出。

    【讨论】:

    • 这如何向 OP 解释为什么他们使用超出范围的索引来访问内存?
    • @JamesAdkison 现在可以了。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-09-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多