【问题标题】:Brute force algorithm causes undefined behaviour蛮力算法导致未定义的行为
【发布时间】:2018-06-19 19:18:13
【问题描述】:

我必须为解决数独难题的作业创建一个程序。用户需要输入包含数字的二进制文件的名称(不是真正的二进制文件,它只是扩展名为 .bin,也可以用记事本、notepad++ 等打开)。这些数字代表拼图上的坐标以及这些坐标中包含的数字,例如 432 表示第 4 行第 3 列包含数字 2。填写拼图后,我需要解决它并将其打印在屏幕上。执行程序后它崩溃了,所以我决定使用一些开发人员认为最好的 MSVC 2017 调试器来查找和修复错误。这是我的代码:

数独.c

#include <stdio.h>
#include <stdlib.h>
#include "stdafx.h"
#include "sudokulib.h"

#define MALLOC_ERROR 0xFF
#define FILE_NOT_FOUND 0xFFF
#define ROWS 9
#define COLUMNS 9

int main(int argc, char ** argv)
{
    char **matrix;
    int i, args;
    int row, column, num;
    FILE * fp;
    char * filename;
    char * importedData;
    matrix = (char **)malloc(ROWS * sizeof(char *));
    if (!matrix)
        exit(MALLOC_ERROR);
    for (i = 0; i<ROWS; ++i)
    {
        matrix[i] = (char *)malloc(COLUMNS * sizeof(char));
        if (!matrix[i])
            exit(MALLOC_ERROR);
    }
    initSudoku(matrix);
    printf ("Give me the name of data file: ");
    filename = (char *)malloc(100 * sizeof(char));
    if (!filename)
        exit(MALLOC_ERROR);
    scanf("%99s", filename);
    fp = fopen(filename, "rb");
    if (!fp)
    {
        printf ("File not found\n");
        exit(FILE_NOT_FOUND);
    }
    importedData = (char *)malloc(sizeof(char)*ROWS*COLUMNS * 3);
    if (!importedData)
        exit (MALLOC_ERROR);
    args = fread(importedData, 1, 243, fp);
    i = 0;
    while (importedData[i] != ' ' && importedData[i + 1] != ' ' && importedData[i + 2] != ' ' && importedData[i] >= '1' && importedData[i + 1] >= '1' && importedData[i + 2] >= '1' && importedData[i] <= '9' && importedData[i + 1] <= '9' && importedData[i + 2] <= '9' && i < 243)
    {
        row = importedData[i] - '0' - 1; /* Convert from ascii code to number */
        column = importedData[i + 1] - '0' - 1;
        num = importedData[i + 2] - '0';
        matrix[row][column] = num;
        i = i + 3;
    }
    printf("Sudoku after importing data:\n\n");
    printSudoku(matrix);
    system("pause");
    if (solvePuzzle(matrix))
    {
        printSudoku(matrix);
    }
    else
        printf ("Puzzle has no solution\n");
    fclose(fp);
    free(filename);
    for (i = 0; i<9; ++i)
    {
        free(matrix[i]);
    }
    free(matrix);
    return 0;
}

数独库.h

#pragma once
#include <stdlib.h>
#include <stdio.h>

/* Function Prototypes Begin Here */

void printSudoku(char **);
void initSudoku(char **);
int checkRow(char **, int, int);
int checkCol(char **, int, int);
int check3x3(char **, int, int, int);
int checkIfEmpty(char **, int*, int*);
int solvePuzzle (char **);

/* Function Prototypes End Here */

void printSudoku(char ** Mat)
{
    int i, j;
    for (i = 0; i<9; ++i)
    {
        printf ("-------------------\n");
        printf("|");
        for (j = 0; j<9; ++j)
        {
            printf("%d|", Mat[i][j]);
        }
        printf("\n");
    }
    printf ("-------------------\n");
}

void initSudoku(char ** Mat)
{
    int i, j;
    for (i = 0; i<9; ++i)
        for (j = 0; j<9; ++j)
            Mat[i][j] = 0;
}


int checkRow (char ** Mat, int row, int num) // if row is free returns 1 else returns 0
{
    int col;
    for (col = 0; col < 9; col++)
    {
        if (Mat[row][col] == num)
        {
            return 0;
        }
    }
    return 1;
}

int checkCol (char ** Mat, int col, int num) // if column is free returns 1 else returns 0
{
    int row;
    for (row = 0; row < 9; row++)
    {
        if (Mat[row][col] == num)
        {
            return 0;
        }
    }
    return 1;
}

int check3x3 (char ** Mat, int row, int col, int num) // if number doesnt exist in the 3x3 grid returns 1 else returns 0
{
    row = (row / 3) * 3; // set to first row in the grid
    col = (col / 3) * 3; // set to first col in the grid
    int i;
    int j;
    for (i = 0; i < 3; i++) // grid is 3x3
    {
        for (j = 0; j < 3; j++)
        {
            if (Mat[row + i][col + j] == num)
            {
                return 0;
            }
        }
    }
    return 1;
}

int isValid (char ** Mat, int row, int col, int num)
{
    return (checkRow(Mat, row, num) && checkCol(Mat, col, num) && check3x3(Mat, row, col, num));
}

int checkIfPuzzleSolved (char ** Mat, int *row, int *col) // if function finds a box empty (puzzle not solved) returns 0 else returns 1
{
    for (*row = 0; *row < 9; *row++)
    {
        for (*col = 0; *col < 9; *col++)
        {
            printf("ROW: %d COL: %d\n",*row,*col);
            if (Mat[*row][*col] == 0)
            {
                return 0;
            }
        }
    }
    return 1;
}


int solvePuzzle (char ** Mat)
{
    int row;
    int col;
    if (checkIfPuzzleSolved(Mat, &row, &col))
    {
        return 1;
    }
    int num;
    for (num = 1; num <= 9; num++)
    {
        //if (checkRow (Mat,row,num) && checkCol (Mat,col,num) && check3x3 (Mat,row,col,num))
        if (isValid(Mat, row, col, num))
        {
            Mat[row][col] = num;
            if (solvePuzzle(Mat))
                return 1;
            Mat[row][col] = 0;
        }
    }
    return 0;
}

调试器在这个函数中发现了一个错误:

int checkIfPuzzleSolved (char ** Mat, int *row, int *col) // if function finds a box empty (puzzle not solved) returns 0 else returns 1
{
    for (*row = 0; *row < 9; *row++)
    {
        for (*col = 0; *col < 9; *col++)
        {
            printf("ROW: %d COL: %d\n",*row,*col);
            if (Mat[*row][*col] == 0) /* DEBUGGER ERROR CODE 0xC0000005: Access violation reading location 0xCDCA247C
            {
                return 0;
            }
        }
    }
    return 1;
}

让我困惑的两件事:

1) 我不明白solvePuzzle 在拼图中的第一个框(第一行第一列)被卡住的原因。似乎checkIfPuzzleSolved 认为第一个框是空的(包含 0),即使使用 printSudoku 我可以看到修改该框的算法在 3 和 4 之间切换它的值,显然 0 != 3 和 0 != 4 .

2) 在checkIfPuzzleSolvedprintf 在屏幕上打印行号和列号并不断产生以下结果:

ROW: 0 COL: 0
ROW: 0 COL: 0
ROW: 0 COL: -858993460

还用调试器仔细检查过,这些值确实是提到的。

我的思路如下:

1) 使用checkIfEmpty 判断谜题中是否包含0,表示谜题还没有解开。 row 和 col 变量是通过引用传入函数的,所以当函数找到一个空框并返回时,row 和 col 会保存空框的坐标。

2) 在循环中,调用checkRowcheckColcheck3x3 以检查是否可以在不违反数独规则的情况下将数字放入所需的框中。 isValid 是为了便于阅读。

3) 递归调用solvePuzzle,直到解开谜题,同时如果一个数错了,将其重置为0。

我已经尝试了我能想到的一切来解决这个问题,浪费了几个小时一遍又一遍地阅读我的代码以找到一个逻辑错误,但一切似乎都很好。有什么想法吗?

编辑:根据 Michael Beer 的要求,这是一个示例二进制文件:

data.bin

142156177191216228257289311329364375418422441484534546562579625663682698739743787794824855883896917933951968

【问题讨论】:

  • *row++; 解析为*(row++);,相当于row++。您正在增加指针,而不是计数器。
  • @melpomene 我明白了。那么我是否将指针增加 sizeof(int) 而不是将其引用的值增加 1?如果是这样,关于语法,编写“将您指向的地址的值增加 1”的正确方法是什么?
  • @JohnM。 (*row)++++(*row)++*row*row += 1
  • @melpomene 谢谢,这解决了问题。如果您想将其作为官方答案,我可以接受并投票。顺便说一句, *row++ 不应该从右到左优先并被评估为 (*row)++ 吗?我以为是这样的。
  • 否,后缀运算符(++--[]()-&gt;foo.foo)的优先级高于前缀运算符。

标签: c


【解决方案1】:

*row++; 解析为*(row++);,相当于row++。您正在增加指针,而不是计数器。 – 梅尔波梅尼

我明白了。那么我是否将指针增加 sizeof(int) 而不是将其引用的值增加 1?如果是这样,关于语法,编写“将您指向的地址的值增加 1”的正确方法是什么?

(*row)++++(*row)++*row*row += 1。 – 梅尔波梅尼

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-12-08
    • 1970-01-01
    • 2015-06-22
    • 1970-01-01
    • 2015-01-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多