使用-O3 -Wall -c -fmessage-length=0 -std=c99
嗯,这就是我认为问题所在......这是细分:
我有一个使用
构建的链表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
(伪纠正)是如何进入的,也许我们在矩阵中......
答案 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)
我问你数据范围,以便我可以想一些其他方法来检查数据的重复。如果记忆不是你的约束。您可以执行以下操作。这是检查重复数据值的逻辑类型。我有一个简单的数据值查找表,其索引和值相同,并且有一个计数字段。如果计数字段为零,则表示可以输入唯一值。删除数据时,减去计数。这样,您可以跟踪计数并确保值的唯一性。因为,它是一个阵列,也不需要遍历。必须为此管理实施一些额外的代码。但是,如果设计得好,应该是可能的。
答案 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);
。