Pascal指针的行为不符合预期

问题描述 投票:0回答:1

对于CodinGame,我正在建立一个名为War的纸牌游戏的裁判。规则描述here)。 TL; DR:具有最高抽牌的人将两张牌添加到他/她的牌组底部。我在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指针表现得如此不同,或者我做错了什么?

pointers pascal
1个回答
0
投票

我发现了这个问题,我还没有完全理解。

问题是由于使用动态数组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^.nextNodecards[size-1].nextNode不再连接。我不完全理解水下发生的事情......我只能猜测Setlength会重新分配内存并复制内容,从而使所有现有指针无效!如果是这种情况,我在发现的教程中没有正确解释这一点。

我可以幸运地解决这个问题,因为最多有52张卡,所以通过选择一个固定长度的数组,我可以解决这个问题。

cards : Array[0..51] of TNode;

编辑:

好的我找到了一个来源给我答案Dynamic array - Free Pascal wiki

虽然写入动态数组的元素不会在这样的数组上使用SetLength创建数组的新实例(没有Ansistrings存在的copy-on-write),但会创建一个副本!因此,如果2个动态数组变量指向同一个数组(一个已分配给另一个),则在其中一个(或两个)上使用SetLength后,它们不会这样做。在SetLength()调用之后,两个变量是不同的数组,其元素彼此独立。

© www.soinside.com 2019 - 2024. All rights reserved.