为什么我在C++中得到std::out_of_range错误?

dced5bon  于 2023-10-21  发布在  其他
关注(0)|答案(1)|浏览(92)

我想用C++写tic-tac-toe

#include <iostream>
#include <vector>

bool X = true;
bool O = false;
bool b = true;
int a = 0;
int j = 100;
std::string game = "";
std::vector<char> c = {'1', '2', '3', '4', '5','6','7','8','9'};

void check(){
    std::cout<<"Enter position: ";
    int pos;
    std::cin>>pos;
    if(c.at(pos-1)==pos){
      if(X == true){
         c[pos-1] = 'X';
         O = true;
         X = false;
         a = 0;
      }
      else if(O == true){
        c[pos-1] = 'O';
        X = true;
        O = false;
        a = 0;
      }
    }
    else{
      std::cout<<"Not empty, enter again.";
      a = 1;
    }
}

void print(){
  std::cout<<"|---|---|---|\n| "<<c.at(0)<<" | "<<c.at(1)<<" | "<<c.at(2)<<" | "<<"\n|---|---|---|\n| "<<c.at(3)<<" | "<<c.at(4)<<" | "<<c.at(5)<<" | "<<"\n|---|---|---|\n| "<<c.at(6)<<" | "<<c.at(7)<<" | "<<c.at(8)<<" | \n"<<"|---|---|---|\n";
}
int main(){
  print();
    int b = 0;
    while((X==true||O==true)&&b<9){
         check();
         while(a=1){
          check();
         }
         print();
         for(int i = 0;i<7;i+=3){
          if(c.at(i)==c.at(i+1)&&c.at(i+1)==c.at(i+2)&&c.at(i)==c.at(i+2)){
            j = i;
            break;
            }
          else{
              continue;
            }
          }
          for(int i = 0;i<3;i++){
            if(c.at(i)==c.at(i+3)&&c.at(i+3)==c.at(i+6)&&c.at(i)==c.at(i+6)){
               j = i;
               break;
               }
            else{
              continue;
            }
          }
          if(c.at(0)==c.at(4)&&c.at(4)==c.at(8)&&c.at(0)==c.at(8)){
            j = 0;
          }
          else if(c.at(2)==c.at(4)&&c.at(4)==c.at(6)&&c.at(2)==c.at(6)){
            j = 2;
          }
          if(j!=100){
            break;
          }
          b++;
    }
    if(j!=100){
      if(c.at(j)=='X'){
        game = "Won by X!";
      }
      else{
        game = "Won by O!";
      }
    }
    else if(j=100){
        game = "Draw";
    }
    std::cout<<game<<"\n";
}

我一直在犯这个错误:

|---|---|---|
| 1 | 2 | 3 |
|---|---|---|
| 4 | 5 | 6 |
|---|---|---|
| 7 | 8 | 9 |
|---|---|---|
Enter position: terminate called after throwing an instance of
'std::out_of_range'
what():  vector::_M_range_check: __n (which is
18446744073709551615) >= this->size() (which is 9)

所以我创建了两个函数,并在while循环中的main方法中调用它,只要bool X或bool O为true,它就会迭代。
据我所知,错误是在第一个函数中,但我没有看到它有什么问题。
我该怎么修复它?
我的代码的其余部分是否正确?

rkue9o1l

rkue9o1l1#

正如其他人在评论中所解释的那样,你混淆了赋值(=)和相等性(==)的测试。

  • while(a=1)应该是while(a==1)
  • if(j=100)应为if(j==100)

在函数check(也可以命名为take_turn)中,测试if(c.at(pos-1)==pos) { ... }应该是if(c.at(pos-1)-'0'==pos) { ... }。减去'0'c.at(pos-1)char的数值转换为从零开始的整数。
这个修复,沿着上面的两个变化,将得到程序运行,所以你可以玩井字游戏。
但是,由于您未能测试用户输入的值,因此需要进行额外的修复。当他/她输入一个无效的位置时,例如,0,你的程序将生成一个下标超出范围错误。

检查cin输入的函数

如果有一种简单的方法可以从cin获得正确的输入,那不是很好吗?
类似于下面代码中的函数get_int

int main()
{
    int pos;
    bool success = get_int(pos, 1, 9, "Enter position (press Enter to cancel): ");
    if (success)
        std::cout << "You entered " << pos << ".\n\n";
    else
        std::cout << "You canceled.\n\n";
    return 0;
}

以下是运行示例的输出:

Enter position (press Enter to cancel): Huh?
Invalid entry. Please reenter.

Enter position (press Enter to cancel): 0
0 is out of range. Please reenter.
Entries must be between 1 and 9.

Enter position (press Enter to cancel): -1
-1 is out of range. Please reenter.
Entries must be between 1 and 9.

Enter position (press Enter to cancel): 10
10 is out of range. Please reenter.
Entries must be between 1 and 9.

Enter position (press Enter to cancel): 9
You entered 9.

在下一次运行时,用户取消。

Enter position (press Enter to cancel): x1
Invalid entry. Please reenter.

Enter position (press Enter to cancel): 1x
Invalid entry. Please reenter.

Enter position (press Enter to cancel): x 1
Invalid entry. Please reenter.

Enter position (press Enter to cancel): 1 x
Invalid entry. Please reenter.

Enter position (press Enter to cancel):
Canceled...

You canceled.

get_int应该执行以下算法:
1.将一行文本读入字符串。
1.如果字符串为空,则取消输入操作。返回false以指示操作已取消。
1.修剪字符串开头或结尾处的任何空格。
1.剩下的字符串应该包含一个有效的int,没有其他的。不允许在数字之前或之后添加额外的字符。
1.用户输入的数字应介于minmax之间(含)。
1.如果满足这些条件,则将字符串转换为int,并将其存储在参数pos中。这就是如何将输入值发送回调用例程。返回true,表示已成功从cin中提取数字。
1.否则,显示错误消息,并要求用户重试。保持循环,直到用户正确地完成它,或者取消它。
下面的函数就是这样工作的:

// main.cpp
#include <iostream>
#include <sstream>
#include <string>
bool get_int(
    int& n, 
    int const min, 
    int const max, 
    std::string const& prompt)
{
    std::string s;
    for (;;)
    {
        if (prompt.empty())
            std::cout << "Enter an integer (press Enter to cancel): ";
        else
            std::cout << prompt;
        if (!std::getline(std::cin, s))
        {
            std::cout << "\nERROR: `std::getline` failed unexpectedly.\n\n";
            return false;
        }
        if (s.empty())
        {
            std::cout << "Canceled...\n\n";
            return false;
        }
        std::stringstream sst(s);
        sst >> n;
        if (!sst.eof())
            sst >> std::ws;
        bool parses{ sst.eof() && !sst.fail() };
        bool is_in_range{ parses && min <= n && n <= max };
        if (!parses)
        {
            std::cout << "Invalid entry. Please reenter.\n\n";
        }
        else if (!is_in_range)
        {
            std::cout << n << " is out of range. Please reenter.\n"
                << "Entries must be between " << min << " and " << max << ".\n\n";
        }
        else
        {
            return true;
        }
    }
}
int main()
{
    int pos;
    bool success = get_int(pos, 1, 9, "Enter position (press Enter to cancel): ");
    if (success)
        std::cout << "You entered " << pos << ".\n\n";
    else
        std::cout << "You canceled.\n\n";
    return 0;
}
// end file: main.cpp

好好研究这段代码。在你的课本或CppReference上找出你不理解的东西。

  • int& n是参考参数。引用是另一个变量的别名。在本例中,n是调用get_int时使用的相应参数的别名,即它是pos的别名。函数get_int对参数n所做的任何更改都将自动对调用例程中的变量pos进行更改。这就是get_int如何将应答发送回调用例程的方式。
  • for (;;)是无限循环。唯一的退出(这里)是在执行return语句时。
  • prompt.empty()s.empty()测试字符串是否为空。
  • std::getline(std::cin, s)std::cin中提取字符,并将其存储在字符串s中,当遇到'\n'时停止,但不会将'\n'存储在字符串s中。
  • 您可以通过在if语句中使用它来测试getline是否成功。这就是if (!std::getline(std::cin, s))所做的。细节有点棘手。getline返回std::cin(或在其调用中使用的任何流对象)。当它被用于if语句(或任何其他需要bool的上下文)时,std::cin被隐式转换为!std::cin.fail(),即“not cin failed”。
  • std::stringstream是stringstream对象使用的类型。stringstream对象是一个流对象,类似于std::cin,除了stringstream从字符串中提取字符,而cin从键盘获取字符。
  • std::stringstream sst(s);创建一个名为sst的stringstream对象,并使用字符串s的内容加载它。
  • sst >> n从字符串流中提取数字n,就像std::cin >> n从键盘中提取数字一样。
  • sst.eof()测试提取是否因为sst用完字符而停止。“eof”意思是“文件结束”。在这个程序中,我们希望用完所有字符。当我们这样做时,这意味着数字后面没有任何字符。
  • sst.fail()测试提取是否失败。这通常是因为sst在提取有效数字之前遇到了非数字字符或eof。
  • sst >> std::ws;提取空白,并丢弃它,当遇到eof或非空白字符时停止。std::ws是一个I/O操作器;查一下。
  • 我们允许数字后面有空格。使用std::ws来摆脱它,会强制发生以下两件事之一:1. sst命中eof,在这种情况下我们很高兴。2. sst没有命中eof,在这种情况下,数字后面有多余的字符。这将触发解析错误。
  • bool parsesbool is_in_range是布尔变量。它们用于存储真/假表达式的答案,以便稍后在if语句中使用。
  • 解析失败可能有两种原因:1.我们没有命中eof,因为数字后面有多余的字符。2. sst >> n导致sst失败,因为无法从sst中提取有效数字。

当你觉得你可以准确地解释函数get_int是如何工作的时候,你应该把这段代码给你的老师看,并解释你是在StackOverflow上找到它的。解释它是如何工作的,让你的老师知道你完全理解它。
只有这样,你才应该得到允许,在你的家庭作业中使用类似的东西。

相关问题