You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何调试七张扑克概率代码中的Run-Time Check Failure #2错误?

问题排查:Visual Studio栈溢出警告与扑克手牌概率代码错误

我编写了一段C代码,用于计算随机七张扑克手牌中无对、一对、两对、三条、葫芦、四条的概率。在Visual Studio中运行时,程序末尾弹出警告:Run-Time Check Failure #2 - Stack around the variable 'hand_counts' was corrupted,点击“Continue”后仍能输出结果。我排查过数组越界访问问题但未发现异常,在其他IDE中运行该代码则无警告且计算速度更快。想知道该错误是代码本身问题还是Visual Studio导致的?

以下是代码:

#include <stdio.h>
#include <time.h>
#include <stdlib.h>
typedef enum suit
{
    spades,
    hearts,
    diamonds,
    clubs
} suit;
typedef struct card
{
    suit suit;
    short pip;
} card;
void deck_shuffle(card deck[52])
{
    srand(time(NULL));
    for (int i = 0; i < 52; i++)
    {
        const int j = rand() % 52;
        const card temp = deck[i];
        deck[i] = deck[j];
        deck[j] = temp;
    }
}
int possibility(card hand[7])
{
    int pair_type = 0;
    int count = 0;
    for (int i = 0; i < 6; i++)
    {
        for (int j = i + 1; j < 7; j++)
        {
            if (hand[i].pip == hand[j].pip)
            {
                count++;
            }
        }
    }
    // check for no pairs.
    if (count == 0)
    {
        pair_type = 1;
    }
    // check for one pair.
    if (count == 1)
    {
        pair_type = 2;
    }
    // check for two pair.
    if (count == 2)
    {
        pair_type = 3;
    }
    // check for three of a kind.
    if (count == 3)
    {
        pair_type = 4;
    }
    // check for full house.
    int rank1 = 0;
    int rank2 = 0;
    int temp1 = hand[0].pip;
    int temp2;
    int count1 = 0;
    int count2 = 0;
    for (int i = 0; i < 6; i++)
    {
        for (int j = i + 1; j < 7; j++)
        {
            if (hand[i].pip == hand[j].pip)
            {
                rank1 = hand[j].pip;
                if (temp1 == rank1)
                {
                    count1++;
                    temp1 = rank1;
                }
                else
                {
                    temp2 = rank2;
                    rank2 = hand[j].pip;
                    if (temp2 == rank2)
                    {
                        count2++;
                    }
                }
            }
        }
    }
    count2 += 1;
    if ((count1 == 3 && count2 == 1) || (count1 == 3 && count2 == 3))
    {
        pair_type = 5;
    }
    // check for four of a kind.
    if (count1 == 6 || count2 == 6)
    {
        pair_type = 6;
    }
    return pair_type;
}
int main(void)
{
    card deck[52];
    for (int i = 0; i < 52; i++)
    {
        deck[i].suit = i / 13;
        deck[i].pip = i % 13;
    }
    int hand_counts[6] = { 0 };
    for (int i = 0; i < 1000000; i++)
    {
        deck_shuffle(deck);
        card hand[7];
        for (int j = 0; j < 7; j++)
        {
            hand[j] = deck[j];
        }
        int hand_type = possibility(hand);
        hand_counts[hand_type - 1]++;
    }
    for (int i = 0; i < 6; i++)
    {
        printf("The probability of hand type %d: is %f\n", i + 1, (double)hand_counts[i] / 1000000);
    }
    return 0;
}

问题根源

这个警告是Visual Studio的栈边界检测机制触发的,本质是代码存在内存越界问题,其他IDE未触发警告只是因为没有启用同等严格的栈检测。具体问题如下:

  1. 手牌类型判断逻辑错误,返回无效值
    possibility函数中pair_type初始化为0,当遇到葫芦(三条+一对)这类手牌时,两两配对的总count为4,前面的if判断(count=0/1/2/3)都不触发,后续的葫芦判断逻辑也无法正确识别,最终pair_type保持0返回。此时hand_counts[hand_type - 1]等价于hand_counts[-1],直接越界写入内存,破坏了hand_counts变量的栈结构,触发VS的检测警告。

    另外,四条的判断逻辑也存在缺陷,无法正确覆盖所有情况,同样可能导致返回0。

  2. 洗牌函数重复设置随机种子
    deck_shuffle函数每次被调用都执行srand(time(NULL)),而time(NULL)的精度为秒,100万次循环会在1秒内完成,导致每次洗牌使用的随机种子完全相同,生成的牌序也完全重复。这不仅会导致统计结果完全错误,还会额外增加不必要的计算开销,也是VS运行速度慢的原因之一(加上栈检测的额外开销)。


修复方案

  1. 修正随机种子设置
    将srand(time(NULL))移到main函数开头,只调用一次,确保每次洗牌的随机序列不同:

    int main(void)
    {
        srand(time(NULL)); // 移到此处
        card deck[52];
        // ... 其余代码
    }
    
  2. 重写手牌类型判断逻辑
    放弃依赖两两配对的count,改用点数计数法,更准确可靠:

    int possibility(card hand[7])
    {
        int rank_counts[13] = {0};
        // 统计每个点数出现的次数
        for (int i = 0; i < 7; i++)
        {
            rank_counts[hand[i].pip]++;
        }
    
        int has_pair = 0, has_two_pair = 0, has_three = 0, has_four = 0;
        for (int i = 0; i < 13; i++)
        {
            switch(rank_counts[i])
            {
                case 2:
                    has_pair++;
                    break;
                case 3:
                    has_three++;
                    break;
                case 4:
                    has_four++;
                    break;
                default:
                    break;
            }
        }
    
        // 判断手牌类型
        if (has_four)
            return 6; // 四条
        if (has_three && has_pair == 1)
            return 5; // 葫芦(三条+一对)
        if (has_three)
            return 4; // 三条
        if (has_pair == 2)
            return 3; // 两对
        if (has_pair == 1)
            return 2; // 一对
        return 1; // 无对
    }
    
  3. 添加边界保护(可选)
    在main函数中调用possibility后,检查返回值是否合法,避免越界:

    int hand_type = possibility(hand);
    if (hand_type >=1 && hand_type <=6)
        hand_counts[hand_type - 1]++;
    else
        printf("Invalid hand type: %d\n", hand_type);
    

内容的提问来源于stack exchange,提问作者PRACTICAL PIG

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 17:40:57