首页 > 解决方案 > 我的代码段有时会运行,有时会抛出错误。我的内存编程有问题吗?代码是怎么回事?

问题描述

第一个文件。现在我们在“Position.h”

struct Position{
  int digit;
  int possible[9];
  int logicalSize;
  bool isPermanent;

  Position* next;
  Position* last;
};

typedef Position* Positionptr;

下一个文件。现在我们在“Board.h”

#include "Position.h"
class Board{
  private:
    //maybe use an array of structures
    Position elements[81];
    bool membership(int arr[], int digit);



  public:
    void print();
    Board(int input[]);

    int* getRow(int row);
    int* getColumn(int col);

    //make private
    void makePossibleList(int row, int col);

    void solve();

};

下一个文件。现在我们在“Board.cpp”

#include "Board.h"
#include <iostream>
using namespace std;

Board::Board(int input[]){
  for(int i = 0; i < 81; i++){

    Position temp;
    temp.digit = input[i];
    //possible is already initialized
    if(input[i] == 0){
      temp.isPermanent = false;
    }
    else{
      //this position is set and cannot be changed
      temp.isPermanent = true;
    }

    elements[i] = temp;
  }
}

void Board::print(){
  for(int i = 0; i < 9; i++){
    int* arr = getRow(i);
    for(int j = 0; j < 9; j++){
      cout << arr[j] << " ";
    }
    cout << endl;
  }
  //confirmed that it can print backwards
}

bool Board::membership(int arr[], int digit){
  for(int i = 0; i < 9; i++){
    if(arr[i] == digit) return true;
  }
  return false;
}

void Board::makePossibleList(int row, int col){

  Position temp = elements[row*9 + col];

  int* tempRow = getRow(row);
  int* tempColumn = getColumn(col);

  for(int i = 0; i < 9; i++){
    if(membership(tempRow, i)){
      continue;
    }
    else if(membership(tempColumn, i)){
      continue;
    }
    else{
      temp.possible[temp.logicalSize] = i;
      temp.logicalSize++;
    }
  }

  //find the possible values for this position
}

int* Board::getRow(int row){
  int* temp = new int[9];
  for(int i = (row*9); i < (9*(row+1)); i++){
    temp[i-row*9] = elements[i].digit;
  }
  return temp;
}

int* Board::getColumn(int col){
  int* temp = new int[9];
  for(int i = 0; i < 81; i+= 9){
    temp[((i+1)/9)] = elements[(i+col)].digit;
  }
  return temp;
}

void Board::solve(){
  cout << "here";
  for(int i = 0; i < 81; i++){
    Position temp = elements[i];

    if(temp.isPermanent){
      continue;
    }
    ///*
    else{
      int row = i/9;
      int col = i%9;
      makePossibleList(row, col);
      if(temp.logicalSize == 0){
        break; //something messed up
      }



      else{


        for(int i = 0; i < 9; i++){
          cout << temp.possible[i] << " ";
        }
        cout << endl;



        //use the end of possible list value
        temp.digit = temp.possible[temp.logicalSize-1];
        temp.digit = *(temp.possible + temp.logicalSize-1);
        temp.logicalSize--;
        //logicalSize will equal 0 if we use up the last one!!


      }




    }
    //*/

  }
  cout << "here";
  //either the loop broke because something is wrong
  //     or the loop finished and the puzzle is solved
  print();

}

//在main.cpp文件中我运行驱动代码

//上面的一切都来自 Board.h 文件下一个文件。我们现在在 main.cpp

#include <iostream>
#include "Board.h"
using namespace std;


int main() {
  cout << "Hello World!\n";

  int puzzle[] =       {0, 2, 0, 0, 9, 0, 0, 6, 0, 
                        0, 0, 9, 0, 5, 0, 3, 0, 2, 
                        0, 8, 0, 7, 0, 0, 0, 5, 0, 
                        0, 6, 0, 0, 1, 0, 0, 0, 3, 
                        0, 7, 0, 0, 3, 0, 0, 9, 0, 
                        9, 0, 0, 0, 6, 2, 0, 7, 0, 
                        0, 3, 0, 0, 0, 1, 0, 2, 0, 
                        8, 0, 2, 0, 7, 0, 4, 0, 0, 
                        0, 9, 0, 0, 8, 0, 0, 3, 0};
  Board x = Board(puzzle);
  //x.print();
  cout << "here";
  x.solve();
}

我无法理解为什么我的代码段始终无法正常运行。它会运行一次,生病再运行一次,然后它不会运行。我正在使用 repl.it 编译器。当我在编写solve() 方法之前测试程序的其他部分时,我有一种暗示,solve() 方法是问题的根源。我的内存分配有什么问题吗?谢谢!

标签: c++

解决方案


有时有效的 C++ 代码是Undefined Behavior的强烈指示。

所以,作为第一步,我通过UBSan运行了这个

这是 Godbolt 上的结果:https ://godbolt.org/z/jqpubW


example.cpp:60:12: runtime error: member call on address 0x00000044a300 which does not point to an object of type 'std::basic_ostream<char>'
0x00000044a300: note: object has invalid vptr
<memory cannot be printed>
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior example.cpp:60:12 in 
example.cpp:62:10: runtime error: member call on address 0x00000044a300 which does not point to an object of type 'std::basic_ostream<char>'
0x00000044a300: note: object has invalid vptr
<memory cannot be printed>
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior example.cpp:62:10 in

这些是突出显示的行:

    int* arr = getRow(i);
    for(int j = 0; j < 9; j++){
      cout << arr[j] << " ";
    }
    cout << endl;

返回的行尚未初始化。

记忆消毒剂

您必须解决此问题,以查看是否存在更多问题。

之后,还要通过Address Sanitizer运行它。

让我们首先通过 Memory Sanitizer 运行它(因为有人写了一条评论说有未初始化的内存)。

这是在运行“debian-10-buster-v20200210”的 GCP micro-us-1 实例上,以防您手边没有 Linux 机器。只需启动一个实例并安装sudo apt install clang.

user@micro-us-1:~$ clang++ -fsanitize=memory -g q61003206.cc && ./a.out
Hello World!
==6314==WARNING: MemorySanitizer: use-of-uninitialized-value
    #0 0x4991d4 in Board::makePossibleList(int, int) /home/user/q61003206.cc:89:39
    #1 0x499789 in Board::solve() /home/user/q61003206.cc:125:7
    #2 0x499ce4 in main /home/user/q61003206.cc:164:5
    #3 0x7fcc3fd3609a in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2409a)
    #4 0x41f379 in _start (/home/user/a.out+0x41f379)

SUMMARY: MemorySanitizer: use-of-uninitialized-value /home/user/q61003206.cc:89:39 in Board::makePossibleList(int, int)
Exiting

这是指向temp.possible未初始化的,因为默认构造函数Position没有初始化该possible字段。解决这个问题的正确方法是制作possible一个std::array<int, 9> possible;. 此外,默认初始化Position::logicalSize

void Board::makePossibleList(int row, int col) {

  Position temp = elements[row * 9 + col];

  int *tempRow = getRow(row);
  int *tempColumn = getColumn(col);

  for (int i = 0; i < 9; i++) {
    if (membership(tempRow, i)) {
      continue;
    } else if (membership(tempColumn, i)) {
      continue;
    } else {
      // This is the problematic line as highlighted by MSAN
      temp.possible[temp.logicalSize] = i;
      temp.logicalSize++;
    }
  }

  // find the possible values for this position
}

使固定

这就是Position我之后结构的样子:

struct Position {
  int digit;
  std::array<int, 9> possible ;  // This should be a vector
  int logicalSize {0};
  bool isPermanent;

  Position *next;
  Position *last;
};

这足以解决问题,但你真的应该这样做。默认初始化所有成员,并删除未使用的成员,并为可变大小(向量)的东西使用正确的容器。

struct Position {
  int digit {-1};
  // logicalSize and possible get combined into one vector that can grow and shrink.
  std::vector<int> possible;
  bool isPermanent {false};

  // These two are unused!
  // Position *next;
  // Position *last;
};

旁注,您还必须像这样修复此块:

        // use the end of possible list value
        temp.digit = temp.possible[temp.logicalSize - 1];
        // temp.digit = *(temp.possible + temp.logicalSize - 1);
        temp.digit = temp.possible.at(temp.logicalSize - 1);
        temp.logicalSize--;   

代码审查

我建议您将其发布在 Code Review SE 上。

一些值得注意的项目,但不是唯一的:

  • 评论你的数据结构。例如,中的int possible[9]字段是什么意思Position
  • 使用现代 C++。
    • 使用 STL 提供的容器。在您的情况下,这意味着很多std::array.
    • 更少的原始指针,并且绝对不使用new.
    • 不要使用using namespace std.

推荐阅读