C无限指针循环(由重复值引起?)

时间:2013-09-26 06:45:05

标签: c pointers linked-list duplicates infinite-loop

使用-O3 -Wall -c -fmessage-length=0 -std=c99

编译MinGW

嗯,这就是我认为问题所在......这是细分:

我有一个使用

构建的链表
typedef struct Coordinate{
    int x;
    int y;
    struct Coordinate *next;
} Coordinate;

我在6x6板(矩阵)上添加“有效动作”(在游戏中的Reversi / Othello)。我检查移动是否有效的逻辑很正常 - 它会在列表中添加使我遇到麻烦的东西。

出于显而易见的原因,我想避免向列表中添加重复值。但是,我尝试编写的每个函数(看起来应该可以正常工作)只会崩溃应用程序,快速实现所有长时间运行。

所以这是我试图编写的函数:

int inList(Coordinate *list, int x, int y) {

    if (list == NULL) return 0;

    while (list != NULL) {
        if (list->x == x && list->y == y) return 1;
        else list = list->next;
    }
    return 0;
}

并称之为:

Coordinate *validMoves = createNewCoordinate(-1, -1); // Just so that there is *something*

if (!inList(validMoves, 1, 1)) {
validMoves->next = createNewCoordinate(1, 1);
validMoves = validMoves->next;
}

据我所知,这应该是完美的。我已经在线查看了一些示例,到目前为止,我在这个特定程序中使用指针的所有方法都非常复杂。

无论如何,真正的问题是,如果我防止重复项被输入到同一个列表中(通过指针连接),那么我会得到一个无限循环(我想这是由于两个元素被认为是相等,因为它们的非指针类型相等)。

我已经在pastebin上发布了代码的所有三部分以供完整参考(不用担心,开源,man!):     othello.c     othello_engine.c     othello_engine.h

我已经尝试过调试,但我不是很擅长,我没有看到任何值得一提的东西。任何人都可以解释可能发生的事情和/或举例说明如何避免链表中的重复? (我已经尝试了很多方法让我的大脑受伤)

编辑:我确实知道一个事实,当我稍后遍历列表时(在游戏中多次有效'转弯'之后),由于以下输出,我自己进入循环引用:

{1, 4} {3, 4} {1, 4} {3, 4} {1, 4} {3, 4}

我不知道theList = theList->next = theList(伪纠正)是如何进入的,也许我们在矩阵中......

3 个答案:

答案 0 :(得分:1)

分段错误通常是指向错误位置(可能为空)的解除引用指针。每次使用指针值时都会添加检查(如果是ptr!= NULL)。

在你的代码中,我注意到一些看起来有点不对劲的东西。当您找到要添加的新动作时,请指定它:

validMovees->next = createNewCoordinate(x, y);

然后通过调用:

使列表自己指向最后一个节点
validMoves = validMoves->next;

所以现在你的validMoves列表实际上只包含一个元素(最后一个元素),因为它指向列表的末尾。您根本不想更改validMoves指针,这应该始终是列表的开头。而是有另一个指向列表末尾的指针,如:

Coordinate *validMoves = createNewCoordinate(-1, -1); // Just so that there is *something*
Coordinate *listEnd = validMoves ;


if (!inList(validMoves, 1, 1)) {
listEnd->next = createNewCoordinate(1, 1);
listEnd = listEnd->next;
}

这可能会让你无限添加相同的动作? - 不确定没有看到你的所有代码

-----编辑-----

Coordinate *validMoves;
Coordinate *listEnd = validMoves;

if (!inList(validMoves, 1, 1))
{
    if (validMoves == NULL)
    {
        validMoves = createNewCoordinate(1, 1); // first time only
        listEnd = validMoves;
    }
    else
    {
        listEnd->next = createNewCoordinate(1, 1); // add new element to the end
        listEnd = listEnd->next; // Move listEnd to last element
    }
}

答案 1 :(得分:1)

我问你数据范围,以便我可以想一些其他方法来检查数据的重复。如果记忆不是你的约束。您可以执行以下操作。这是检查重复数据值的逻辑类型。我有一个简单的数据值查找表,其索引和值相同,并且有一个计数字段。如果计数字段为零,则表示可以输入唯一值。删除数据时,减去计数。这样,您可以跟踪计数并确保值的唯一性。因为,它是一个阵列,也不需要遍历。必须为此管理实施一些额外的代码。但是,如果设计得好,应该是可能的。

enter image description here

答案 2 :(得分:1)

将新坐标添加到链接列表时存在问题。

validMoves是指向有效移动列表中第一个Coordinate的指针,因此最初您的链接列表如下所示:

  

validMoves - > [1st_move] - > [2nd_move] - > [3rd_move] - > ... - > [last_move]

请记住,这些箭头来自结构中存储的next指针(例如,[1st_move]的{​​{1}}指针指向next,{{1 } [2nd_move]指针是[last_move])。

现在让我们看看代码运行时会发生什么,它会将新坐标添加到列表中(特别是标记为next的行):

NULL

在第2行中,发生了两件事:

  • if (!inList(validMoves, 1, 1)) { validMoves->next = createNewCoordinate(1, 1); //2 validMoves = validMoves->next; } 分配一个新的2,然后返回指向所述createNewCoordinate(1, 1)的指针,初始内容设置为{1,1,NULL}。
  • 然后,Coordinate指向的结构的Coordinate指针(即next的{​​{1}}指针)将被覆盖,并设置为指向此{1 ,1,NULL} struct。

这使您的链接列表看起来像:

  

validMoves - > [1st_move] - > [内容为{1,1,NULL}的新结构]

     

??? - > [2nd_move] - > [3rd_move] - > ... - > [last_move]

validMoves的{​​{1}}指针(过去指向[1st_move])现在指向新制作的坐标,现在没有指向{{ 1}}!因此,next以后的原始链表已经成为孤儿。

gdb 可以帮助您调试此类问题。一个很好的起点是在可疑代码区域之前添加断点,将重要变量放在显示列表中(例如[1st_move]next[2nd_move]) ,然后通过执行可疑区域步骤,并查看变量的打印值是否在每一步都有意义。

要解决此问题,我们可以将链接列表移到最后,然后将指针添加到{1,1,NULL},但只需添加新的[2nd_move]就可以更轻松(也更快)列表的开头,但你需要一个临时指针变量,如下所示:

[2nd_move]

现在,新添加的validMoves位于列表的开头,旧的validMoves->next已移至第二个位置,validMoves->next->next已移至第三个位置,依此类推(链表与添加顺序的顺序相反,但我认为在这个用例中元素的顺序无关紧要。)


您声明的申请是针对奥赛罗的。为什么不分配一个连续的Coordinate * if (!inList(validMoves, 1, 1)) { Coordinate *temp = createNewCoordinate(1, 1); temp->next = validMoves; // the new Coordinate now becomes {1,1,&[1st_move]} validMoves = temp; } Coordinate数组来存储一个位置是否有效,而不是使用链接列表?

[1st_move]

这仅使用每个单元格单元1个字节的内存(可以设置为1或0来指示移动是否有效),并且您不需要走链表以查明是否移动已存在,只需导航到[2nd_move]并检查字节是否已设置;记得在使用前初始化数组。

快乐的编码!

编辑:在上面的回答中,我假设你的size函数初始化了创建的struct size指向char的指针的值,但现在已经注意到了你在othello_engine.c中的实现使它没有初始化。您可能想要将其初始化为char * validmoves = malloc(size*size);