C 中的甲板洗牌将编译但分段错误

Deck shuffling in C will compile but Segmentation Faults

typedef enum {Clubs = 1, Diamonds, Hearts, Spades} suit_t;
typedef enum {Ace = 1, Two = 2, Three = 3, Four = 4, Five = 5,
              Six = 6, Seven = 7, Eight = 8, Nine = 9, Ten = 10,
              Jack = 11, Queen = 12, King = 13} face_t;

typedef struct card_t
{
        suit_t suit;
        face_t face;
} card;

typedef struct stack_t
{
    struct card_t deck[53];
    int size;
} stack_h;

void shuffle_deck(stack_h *stack)
{
   stack_h temp, *cardptr;
   int i,num;

   for(i = 0; i < stack->size; i++)
   {
    i = rand() %stack->size;
    *cardptr = *stack;
    temp = *cardptr;
    *cardptr = *stack;
    *stack = temp;
   }
}

所以我尝试了很多不同的方法来洗牌,但不幸的是我没有运气我想做的是很好地洗牌它编译的牌但是当它洗牌时它会分段错误。关于如何解决它的任何想法?除了洗牌功能,一切正常。

这一行:

*cardptr = *stack;

发生在变量 cardptr 指向任何东西之前,因此它指向随机内存,您会覆盖它。

洗牌是一个众所周知的已解决问题。 Google "Fisher-Yates".

您的交换代码充其量是可疑的。使用:

可能更接近正确
for (i = 0; i < stack->size; i++)
{
    int j = rand() % stack->size;
    card_t temp = stack->deck[i];
    stack->deck[i] = stack->deck[j];
    stack->deck[j] = temp;
}

这可能不是一个公正的洗牌。请参阅 Fisher-Yates at Wikipedia and also The Danger of Naïveté 关于编码恐怖的内容。

首先考虑算法。执行随机播放的方法有很多种,但常用的技术 (Fisher-Yates) 是:

  1. 如果要打乱的元素少于两个,则停止。
  2. Select从剩余待洗牌的池中随机取一个元素,并将其与池中的最后一个元素交换。
  3. 将池的大小减一,然后返回步骤 1。

在 C 中的实现可能如下所示:

void shuffle_deck(stack_h *stack)
{
   int last;

   for (last = stack->size - 1; last > 0; last -= 1)
   {
      int i = rand() % (last + 1);

      if (i < last) {
          card_t temp = stack->deck[i];
          stack->deck[i] = stack->deck[last];
          stack->deck[last] = temp;
      } // else the chosen element is already in the target position
   }
}