如何调试七张扑克概率代码中的Run-Time Check Failure #2错误?
我编写了一段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未触发警告只是因为没有启用同等严格的栈检测。具体问题如下:
手牌类型判断逻辑错误,返回无效值
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。
洗牌函数重复设置随机种子
deck_shuffle函数每次被调用都执行srand(time(NULL)),而time(NULL)的精度为秒,100万次循环会在1秒内完成,导致每次洗牌使用的随机种子完全相同,生成的牌序也完全重复。这不仅会导致统计结果完全错误,还会额外增加不必要的计算开销,也是VS运行速度慢的原因之一(加上栈检测的额外开销)。
修复方案
修正随机种子设置
将srand(time(NULL))移到main函数开头,只调用一次,确保每次洗牌的随机序列不同:int main(void) { srand(time(NULL)); // 移到此处 card deck[52]; // ... 其余代码 }重写手牌类型判断逻辑
放弃依赖两两配对的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; // 无对 }添加边界保护(可选)
在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

