Pascal 指针未按预期运行
Pascal pointers not behaving as expected
对于 CodinGame I'm building a referee for a card game called War. Rules described here)。 TL;DR:拿到最高牌的人将两张牌都加到 his/her 牌堆的底部。
我在 Pascal 中构建了一个链表来保存卡片堆。但是 pascal 指针的行为并不像我预期的那样:
例如,我给程序输入以下内容(稍作修改):
9
8C
KD
AH
QH
3D
KD
AH
QH
6D
9
8D
2D
3H
4D
4S
2D
3H
4D
7H
示例输出为:
nrCards: 5
Player1: 13 14 12 6
Player2: 2 3 4 7
Player1: 14 12 6
Player2: 3 4 7
Player1: 12 6
Player2: 4 7
Player1: 6
Player2: 7
Player1:
Player2: 6 7
2 5
即最后一个元素的 nextNode 指针大多没有正确更新...
代码:
program Answer;
{$H+}
uses sysutils, math, strutils;
type
TNode = record
val : Int32;
nextNode : ^TNode;
end;
TNodePtr = ^TNode;
TNodePtrPtr = ^TNodePtr;
var
size1 : Int32;
size2 : Int32;
cards : Array of TNode;
player1first : TNodePtr = nil;
player1last : TNodePtr = nil;
player2first : TNodePtr = nil;
player2last : TNodePtr = nil;
winnerLast : TNodePtrPtr = nil;
i : Int32;
Line: String;
turns : Int32 = 0;
nrCards : Int32 = 1;
cardIt : TNodePtr = nil;
function ParseIn(i : Int32) : String;
begin
ParseIn := ExtractWord(i, Line, [' ']);
end;
function War(pl1it, pl2it : TNodePtr) : Int32;
var
nrCards : Int32 = 5;
i :Int32; // not reuse?
begin
for i := 0 to 3 do begin
pl1it := pl1it^.nextNode;
pl2it := pl2it^.nextNode;
if (pl1it = nil) or (pl2it = nil) then
exit(0);
end;
while (pl1it^.val = pl2it^.val) do begin
nrCards := nrCards + 4;
for i := 0 to 3 do begin
pl1it := pl1it^.nextNode;
pl2it := pl2it^.nextNode;
if (pl1it = nil) or (pl2it = nil) then
exit(0);
end;
end;
if pl1it^.val > pl2it^.val then
// player 1 wins
War := nrCards
else
// player 2 wins
War := -nrCards;
end;
begin
readln(Line);
size1 := StrToInt(ParseIn(1));
Setlength(cards, size1);
for i := 0 to size1-1 do begin
readln(Line);
//writeln(StdErr, Line);
case Line[1] of
'1' : cards[i].val := 10;
'J' : cards[i].val := 11;
'Q' : cards[i].val := 12;
'K' : cards[i].val := 13;
'A' : cards[i].val := 14;
else cards[i].val := Integer(Line[1])-48;
end;
if i = size1-1 then
cards[i].nextNode := nil
else
cards[i].nextNode := @cards[i+1];
end;
readln(Line);
size2 := StrToInt(ParseIn(1));
Setlength(cards, size1+size2);
for i := size1 to size1+size2-1 do begin
readln(Line);
//writeln(StdErr, Line);
case Line[1] of
'1' : cards[i].val := 10;
'J' : cards[i].val := 11;
'Q' : cards[i].val := 12;
'K' : cards[i].val := 13;
'A' : cards[i].val := 14;
else cards[i].val := Integer(Line[1])-48;
end;
if i = size1+size2-1 then
cards[i].nextNode := nil
else
cards[i].nextNode := @cards[i+1];
end;
player1first := @cards[0];
player1last := @cards[size1-1];
player2first := @cards[size1];
player2last := @cards[size1+size2-1];
// now for the game
while (player1first <> nil) and (player2first <> nil) do begin
if player1first^.val <> player2first^.val then begin
if player1first^.val > player2first^.val then begin
// player 1 wins
writeln(StdErr, 'Player1 wins');
winnerLast := @player1last;
end else begin
// player 2 wins
writeln(StdErr, 'Player2 wins');
winnerLast := @player2last;
end;
winnerLast^^.nextNode := player1first;
winnerLast^ := player1first;
player1first := player1first^.nextNode;
winnerLast^^.nextNode := player2first;
winnerLast^ := player2first;
player2first := player2first^.nextNode;
winnerLast^^.nextNode := nil;
end else begin
// war
nrCards := War(player1first, player2first);
writeln(StdErr, 'nrCards: ', nrCards);
if nrCards = 0 then
break;
if nrCards > 0 then begin
writeln(StdErr, 'Player1 wins');
winnerLast := @player1last;
end else begin
writeln(StdErr, 'Player2 wins');
winnerLast := @player2last;
nrCards := -nrCards;
end;
for i := 0 to nrCards-1 do begin
winnerLast^^.nextNode := player1first;
winnerLast^ := player1first;
player1first := player1first^.nextNode;
end;
for i := 0 to nrCards-1 do begin
winnerLast^^.nextNode := player2first;
winnerLast^ := player2first;
player2first := player2first^.nextNode;
end;
winnerLast^^.nextNode := nil;
end;
turns := turns + 1;
write(StdErr, 'Player1: ');
cardIt := player1first;
while cardIt <> nil do begin
write(StdErr, cardIt^.val, ' ');
cardIt := cardIt^.nextNode;
end;
writeln(StdErr, ' ');
write(StdErr, 'Player2: ');
cardIt := player2first;
while cardIt <> nil do begin
write(StdErr, cardIt^.val, ' ');
cardIt := cardIt^.nextNode;
end;
writeln(StdErr, ' ');
end;
// end game
if nrCards = 0 then
// equal
writeln('PAT')
else if player2first = nil then
// player 2 won
writeln('1 ',turns)
else
// player 2 won
writeln('2 ',turns);
flush(StdErr); flush(output); // DO NOT REMOVE
end.
我有 C/C++/C# 背景,这可以解释我的编码风格。 C 中的相同程序可以正常工作。恕我直言,我已经按字面意思 翻译了它...
C代码:
#define _CRT_SECURE_NO_WARNINGS
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
struct TNode {
int val;
struct TNode* nextNode;
};
int War(struct TNode* pl1It, struct TNode* pl2It) {
int nrCards = 5;
for (int i = 0; i < 4; ++i) {
pl1It = pl1It->nextNode;
pl2It = pl2It->nextNode;
if (pl1It == NULL || pl2It == NULL) {
return 0;
}
}
while (pl1It->val == pl2It->val) {
nrCards += 4;
for (int i = 0; i < 4; ++i) {
pl1It = pl1It->nextNode;
pl2It = pl2It->nextNode;
if (pl1It == NULL || pl2It == NULL) {
return 0;
}
}
}
if (pl1It->val > pl2It->val) {
// player 1 wins
return nrCards;
} else {
// player 2 wins
return -nrCards;
}
}
int main()
{
struct TNode cards[52];
int size1;
scanf("%d", &size1);
for (int i = 0; i < size1; ++i) {
char Line[4];
scanf("%s", Line);
if (Line[0] == '1') cards[i].val = 10;
else if (Line[0] == 'J') cards[i].val = 11;
else if (Line[0] == 'Q') cards[i].val = 12;
else if (Line[0] == 'K') cards[i].val = 13;
else if (Line[0] == 'A') cards[i].val = 14;
else cards[i].val = (int)Line[0] - 48;
if (i == size1 - 1) cards[i].nextNode = NULL;
else cards[i].nextNode = &cards[i + 1];
}
int size2;
scanf("%d", &size2);
for (int i = size1; i < size1 + size2; ++i) {
char Line[4];
scanf("%s", Line);
if (Line[0] == '1') cards[i].val = 10;
else if (Line[0] == 'J') cards[i].val = 11;
else if (Line[0] == 'Q') cards[i].val = 12;
else if (Line[0] == 'K') cards[i].val = 13;
else if (Line[0] == 'A') cards[i].val = 14;
else cards[i].val = (int)Line[0] - 48;
if (i == size1 + size2 - 1) cards[i].nextNode = NULL;
else cards[i].nextNode = &cards[i + 1];
}
struct TNode* player1first = &cards[0];
struct TNode* player1last = &cards[size1 - 1];
struct TNode* player2first = &cards[size1];
struct TNode* player2last = &cards[size1 + size2 - 1];
int nrOfCards = 1; // has to do with check
int turns = 0;
// now for the game
while (player1first != NULL && player2first != NULL) {
if (player1first->val != player2first->val) {
struct TNode** winnerLast = NULL;
if (player1first->val > player2first->val) {
// player 1 wins
fprintf(stderr, "Player 1 wins.\n");
winnerLast = &player1last;
} else {
// player 2 wins
fprintf(stderr, "Player 2 wins.\n");
winnerLast = &player2last;
}
(*winnerLast)->nextNode = player1first;
(*winnerLast) = player1first;
player1first = player1first->nextNode;
(*winnerLast)->nextNode = player2first;
(*winnerLast) = player2first;
player2first = player2first->nextNode;
(*winnerLast)->nextNode = NULL;
} else {
// war
fprintf(stderr, "War: ");
nrOfCards = War(player1first, player2first);
if (nrOfCards == 0) break;
struct TNode** winnerLast;
if (nrOfCards > 0) {
// Player 1 wins
fprintf(stderr, "Player 1 wins.\n");
winnerLast = &player1last;
} else {
// Player 2 wins
fprintf(stderr, "Player 2 wins.\n");
nrOfCards = -nrOfCards;
winnerLast = &player2last;
}
for (int i = 0; i < nrOfCards; ++i) {
(*winnerLast)->nextNode = player1first;
(*winnerLast) = player1first;
player1first = player1first->nextNode;
}
for (int i = 0; i < nrOfCards; ++i) {
(*winnerLast)->nextNode = player2first;
(*winnerLast) = player2first;
player2first = player2first->nextNode;
}
(*winnerLast)->nextNode = NULL;
}
turns = turns + 1;
}
// end game
if (nrOfCards == 0) {
// equal
printf("PAT\n");
} else if (player2first == NULL) {
// player 2 won
printf("1 %d\n", turns);
} else {
// player 2 won
printf("2 %d\n", turns);
}
// Write an action using printf(). DON'T FORGET THE TRAILING \n
// To debug: fprintf(stderr, "Debug messages...\n");
return 0;
}
有人可以解释为什么 pascal 指针的行为如此不同,或者我做错了什么吗?
我找到了问题,但我还没有完全理解。
问题是由于动态数组cards
的取消使用引起的。这也是 C 代码和 Pascal 代码之间的最大区别。我在做
readln(Line);
size1 := StrToInt(ParseIn(1));
Setlength(cards, size1);
for i := 0 to size1-1 do begin
readln(Line);
[...]
end;
readln(Line);
size2 := StrToInt(ParseIn(1));
Setlength(cards, size1+size2);
for i := size1 to size1+size2-1 do begin
第二个 Setlength 似乎使我初始数组的最后一个元素无效。似乎 player1last^.nextNode
和 cards[size-1].nextNode
不再连接。我不太明白在水下发生了什么...我只能猜测 Setlength 重新分配内存并复制内容从而使所有现有指针失效!如果是这种情况,我找到的教程中没有正确解释。
幸运的是我可以解决这个问题,因为最多有 52 张卡片,所以通过选择一个固定长度的数组我可以解决这个问题。
cards : Array[0..51] of TNode;
编辑:
好的,我找到了一个给我答案的来源 Dynamic array - Free Pascal wiki
Although writing to elements of dynamic arrays does not create a new instance of the array (no copy-on-write as it exists for Ansistrings) using SetLength on such arrays does create a copy! So if 2 dynamic array variables point to the same array (one has been assigned to the other) they do not do so after using SetLength on one (or both) of them. After the SetLength() call the two variables are distinct arrays whose elements are independent from each other.
对于 CodinGame I'm building a referee for a card game called War. Rules described here)。 TL;DR:拿到最高牌的人将两张牌都加到 his/her 牌堆的底部。 我在 Pascal 中构建了一个链表来保存卡片堆。但是 pascal 指针的行为并不像我预期的那样:
例如,我给程序输入以下内容(稍作修改):
9
8C
KD
AH
QH
3D
KD
AH
QH
6D
9
8D
2D
3H
4D
4S
2D
3H
4D
7H
示例输出为:
nrCards: 5
Player1: 13 14 12 6
Player2: 2 3 4 7
Player1: 14 12 6
Player2: 3 4 7
Player1: 12 6
Player2: 4 7
Player1: 6
Player2: 7
Player1:
Player2: 6 7
2 5
即最后一个元素的 nextNode 指针大多没有正确更新...
代码:
program Answer;
{$H+}
uses sysutils, math, strutils;
type
TNode = record
val : Int32;
nextNode : ^TNode;
end;
TNodePtr = ^TNode;
TNodePtrPtr = ^TNodePtr;
var
size1 : Int32;
size2 : Int32;
cards : Array of TNode;
player1first : TNodePtr = nil;
player1last : TNodePtr = nil;
player2first : TNodePtr = nil;
player2last : TNodePtr = nil;
winnerLast : TNodePtrPtr = nil;
i : Int32;
Line: String;
turns : Int32 = 0;
nrCards : Int32 = 1;
cardIt : TNodePtr = nil;
function ParseIn(i : Int32) : String;
begin
ParseIn := ExtractWord(i, Line, [' ']);
end;
function War(pl1it, pl2it : TNodePtr) : Int32;
var
nrCards : Int32 = 5;
i :Int32; // not reuse?
begin
for i := 0 to 3 do begin
pl1it := pl1it^.nextNode;
pl2it := pl2it^.nextNode;
if (pl1it = nil) or (pl2it = nil) then
exit(0);
end;
while (pl1it^.val = pl2it^.val) do begin
nrCards := nrCards + 4;
for i := 0 to 3 do begin
pl1it := pl1it^.nextNode;
pl2it := pl2it^.nextNode;
if (pl1it = nil) or (pl2it = nil) then
exit(0);
end;
end;
if pl1it^.val > pl2it^.val then
// player 1 wins
War := nrCards
else
// player 2 wins
War := -nrCards;
end;
begin
readln(Line);
size1 := StrToInt(ParseIn(1));
Setlength(cards, size1);
for i := 0 to size1-1 do begin
readln(Line);
//writeln(StdErr, Line);
case Line[1] of
'1' : cards[i].val := 10;
'J' : cards[i].val := 11;
'Q' : cards[i].val := 12;
'K' : cards[i].val := 13;
'A' : cards[i].val := 14;
else cards[i].val := Integer(Line[1])-48;
end;
if i = size1-1 then
cards[i].nextNode := nil
else
cards[i].nextNode := @cards[i+1];
end;
readln(Line);
size2 := StrToInt(ParseIn(1));
Setlength(cards, size1+size2);
for i := size1 to size1+size2-1 do begin
readln(Line);
//writeln(StdErr, Line);
case Line[1] of
'1' : cards[i].val := 10;
'J' : cards[i].val := 11;
'Q' : cards[i].val := 12;
'K' : cards[i].val := 13;
'A' : cards[i].val := 14;
else cards[i].val := Integer(Line[1])-48;
end;
if i = size1+size2-1 then
cards[i].nextNode := nil
else
cards[i].nextNode := @cards[i+1];
end;
player1first := @cards[0];
player1last := @cards[size1-1];
player2first := @cards[size1];
player2last := @cards[size1+size2-1];
// now for the game
while (player1first <> nil) and (player2first <> nil) do begin
if player1first^.val <> player2first^.val then begin
if player1first^.val > player2first^.val then begin
// player 1 wins
writeln(StdErr, 'Player1 wins');
winnerLast := @player1last;
end else begin
// player 2 wins
writeln(StdErr, 'Player2 wins');
winnerLast := @player2last;
end;
winnerLast^^.nextNode := player1first;
winnerLast^ := player1first;
player1first := player1first^.nextNode;
winnerLast^^.nextNode := player2first;
winnerLast^ := player2first;
player2first := player2first^.nextNode;
winnerLast^^.nextNode := nil;
end else begin
// war
nrCards := War(player1first, player2first);
writeln(StdErr, 'nrCards: ', nrCards);
if nrCards = 0 then
break;
if nrCards > 0 then begin
writeln(StdErr, 'Player1 wins');
winnerLast := @player1last;
end else begin
writeln(StdErr, 'Player2 wins');
winnerLast := @player2last;
nrCards := -nrCards;
end;
for i := 0 to nrCards-1 do begin
winnerLast^^.nextNode := player1first;
winnerLast^ := player1first;
player1first := player1first^.nextNode;
end;
for i := 0 to nrCards-1 do begin
winnerLast^^.nextNode := player2first;
winnerLast^ := player2first;
player2first := player2first^.nextNode;
end;
winnerLast^^.nextNode := nil;
end;
turns := turns + 1;
write(StdErr, 'Player1: ');
cardIt := player1first;
while cardIt <> nil do begin
write(StdErr, cardIt^.val, ' ');
cardIt := cardIt^.nextNode;
end;
writeln(StdErr, ' ');
write(StdErr, 'Player2: ');
cardIt := player2first;
while cardIt <> nil do begin
write(StdErr, cardIt^.val, ' ');
cardIt := cardIt^.nextNode;
end;
writeln(StdErr, ' ');
end;
// end game
if nrCards = 0 then
// equal
writeln('PAT')
else if player2first = nil then
// player 2 won
writeln('1 ',turns)
else
// player 2 won
writeln('2 ',turns);
flush(StdErr); flush(output); // DO NOT REMOVE
end.
我有 C/C++/C# 背景,这可以解释我的编码风格。 C 中的相同程序可以正常工作。恕我直言,我已经按字面意思 翻译了它...
C代码:
#define _CRT_SECURE_NO_WARNINGS
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
struct TNode {
int val;
struct TNode* nextNode;
};
int War(struct TNode* pl1It, struct TNode* pl2It) {
int nrCards = 5;
for (int i = 0; i < 4; ++i) {
pl1It = pl1It->nextNode;
pl2It = pl2It->nextNode;
if (pl1It == NULL || pl2It == NULL) {
return 0;
}
}
while (pl1It->val == pl2It->val) {
nrCards += 4;
for (int i = 0; i < 4; ++i) {
pl1It = pl1It->nextNode;
pl2It = pl2It->nextNode;
if (pl1It == NULL || pl2It == NULL) {
return 0;
}
}
}
if (pl1It->val > pl2It->val) {
// player 1 wins
return nrCards;
} else {
// player 2 wins
return -nrCards;
}
}
int main()
{
struct TNode cards[52];
int size1;
scanf("%d", &size1);
for (int i = 0; i < size1; ++i) {
char Line[4];
scanf("%s", Line);
if (Line[0] == '1') cards[i].val = 10;
else if (Line[0] == 'J') cards[i].val = 11;
else if (Line[0] == 'Q') cards[i].val = 12;
else if (Line[0] == 'K') cards[i].val = 13;
else if (Line[0] == 'A') cards[i].val = 14;
else cards[i].val = (int)Line[0] - 48;
if (i == size1 - 1) cards[i].nextNode = NULL;
else cards[i].nextNode = &cards[i + 1];
}
int size2;
scanf("%d", &size2);
for (int i = size1; i < size1 + size2; ++i) {
char Line[4];
scanf("%s", Line);
if (Line[0] == '1') cards[i].val = 10;
else if (Line[0] == 'J') cards[i].val = 11;
else if (Line[0] == 'Q') cards[i].val = 12;
else if (Line[0] == 'K') cards[i].val = 13;
else if (Line[0] == 'A') cards[i].val = 14;
else cards[i].val = (int)Line[0] - 48;
if (i == size1 + size2 - 1) cards[i].nextNode = NULL;
else cards[i].nextNode = &cards[i + 1];
}
struct TNode* player1first = &cards[0];
struct TNode* player1last = &cards[size1 - 1];
struct TNode* player2first = &cards[size1];
struct TNode* player2last = &cards[size1 + size2 - 1];
int nrOfCards = 1; // has to do with check
int turns = 0;
// now for the game
while (player1first != NULL && player2first != NULL) {
if (player1first->val != player2first->val) {
struct TNode** winnerLast = NULL;
if (player1first->val > player2first->val) {
// player 1 wins
fprintf(stderr, "Player 1 wins.\n");
winnerLast = &player1last;
} else {
// player 2 wins
fprintf(stderr, "Player 2 wins.\n");
winnerLast = &player2last;
}
(*winnerLast)->nextNode = player1first;
(*winnerLast) = player1first;
player1first = player1first->nextNode;
(*winnerLast)->nextNode = player2first;
(*winnerLast) = player2first;
player2first = player2first->nextNode;
(*winnerLast)->nextNode = NULL;
} else {
// war
fprintf(stderr, "War: ");
nrOfCards = War(player1first, player2first);
if (nrOfCards == 0) break;
struct TNode** winnerLast;
if (nrOfCards > 0) {
// Player 1 wins
fprintf(stderr, "Player 1 wins.\n");
winnerLast = &player1last;
} else {
// Player 2 wins
fprintf(stderr, "Player 2 wins.\n");
nrOfCards = -nrOfCards;
winnerLast = &player2last;
}
for (int i = 0; i < nrOfCards; ++i) {
(*winnerLast)->nextNode = player1first;
(*winnerLast) = player1first;
player1first = player1first->nextNode;
}
for (int i = 0; i < nrOfCards; ++i) {
(*winnerLast)->nextNode = player2first;
(*winnerLast) = player2first;
player2first = player2first->nextNode;
}
(*winnerLast)->nextNode = NULL;
}
turns = turns + 1;
}
// end game
if (nrOfCards == 0) {
// equal
printf("PAT\n");
} else if (player2first == NULL) {
// player 2 won
printf("1 %d\n", turns);
} else {
// player 2 won
printf("2 %d\n", turns);
}
// Write an action using printf(). DON'T FORGET THE TRAILING \n
// To debug: fprintf(stderr, "Debug messages...\n");
return 0;
}
有人可以解释为什么 pascal 指针的行为如此不同,或者我做错了什么吗?
我找到了问题,但我还没有完全理解。
问题是由于动态数组cards
的取消使用引起的。这也是 C 代码和 Pascal 代码之间的最大区别。我在做
readln(Line);
size1 := StrToInt(ParseIn(1));
Setlength(cards, size1);
for i := 0 to size1-1 do begin
readln(Line);
[...]
end;
readln(Line);
size2 := StrToInt(ParseIn(1));
Setlength(cards, size1+size2);
for i := size1 to size1+size2-1 do begin
第二个 Setlength 似乎使我初始数组的最后一个元素无效。似乎 player1last^.nextNode
和 cards[size-1].nextNode
不再连接。我不太明白在水下发生了什么...我只能猜测 Setlength 重新分配内存并复制内容从而使所有现有指针失效!如果是这种情况,我找到的教程中没有正确解释。
幸运的是我可以解决这个问题,因为最多有 52 张卡片,所以通过选择一个固定长度的数组我可以解决这个问题。
cards : Array[0..51] of TNode;
编辑:
好的,我找到了一个给我答案的来源 Dynamic array - Free Pascal wiki
Although writing to elements of dynamic arrays does not create a new instance of the array (no copy-on-write as it exists for Ansistrings) using SetLength on such arrays does create a copy! So if 2 dynamic array variables point to the same array (one has been assigned to the other) they do not do so after using SetLength on one (or both) of them. After the SetLength() call the two variables are distinct arrays whose elements are independent from each other.