CS50 Credit:10位卡号4062901840未判定为INVALID的问题排查
CS50 Credit 问题集故障排查
问题现象
完成CS50 credit问题集开发后,测试发现卡号4062901840无法通过校验:运行check50检测时,输入该10位卡号按规则应输出Invalid,但程序实际无有效输出。
已使用20组其他10位卡号完成测试,均能正确判定为无效。代码仅采用课程已讲授的语法实现,未使用数组或额外类库,需定位问题根因。
原有程序逻辑
程序首先提示用户输入卡号,按Luhn算法做校验:
- 从卡号倒数第二位开始,每隔一位数字乘以2,将所有乘积的各位数字相加求和;
- 将上述和值与所有未乘2的数位之和相加;
- 若总和末位为0(即总和模10余0),则通过Luhn校验;
- 通过Luhn校验后,进一步判定卡组织类型,不满足规则则输出invalid。
卡组织判定规则:
- American Express(15位):开头为34或37
- Mastercard(16位):开头为51、52、53、54或55
- Visa(13位或16位):开头为4
问题代码
#include <cs50.h> #include <stdio.h> int main(void) { long cnum, cnumClone; int count, first, tempo, sum = 0; do{ printf("Enter card number\n"); scanf("%ld", &cnum); } while(cnum == 0); // 复制卡号用于遍历计算 cnumClone = cnum; // 统计卡号位数 for(count = 1; cnumClone != 0; count++) { // 取最后一位 tempo = cnumClone % 10; // 移除最后一位 cnumClone /= 10; // 选中需要乘2的数位 if(count % 2 == 0) { tempo *= 2; // 处理乘积为两位数的情况 if (tempo >=10) { tempo = tempo % 10; tempo += 1; sum += tempo; }else{ sum += tempo; } }else{ // 不需要乘2的数位直接累加 sum += tempo; } } // Luhn算法最后一步校验 if (sum % 10 == 0) { // 循环计数从1开始,实际位数需要减1 count -= 1; // 16位卡号判断是Mastercard还是Visa if(count == 16) { first = cnum / 100000000000000; if(first == 51 || first== 52 || first == 53 || first == 54 || first == 55) { printf("MASTERCARD\n"); }else{ first = first /10; if(first == 4) { printf("VISA\n"); }else{ printf("INVALID\n"); } } } // 15位卡号判断是否为Amex if(count == 15) { first = cnum / 10000000000000; if(first == 34 || first == 37) { printf("AMEX\n"); }else{ printf("INVALID\n"); } } // 13位卡号判断是否为Visa if (count == 13) { first = cnum / 1000000000000; if(first == 4) { printf("VISA\n"); } } }else{ // Luhn校验不通过直接输出无效 printf("INVALID\n"); } }
根因定位
卡号4062901840无输出是两个逻辑漏洞共同导致的:
- 手动计算该卡号的Luhn校验和,模10结果恰好为0,程序会进入Luhn校验通过的分支,不会触发外层else的INVALID输出。
- 进入校验通过分支后,代码仅覆盖了16位、15位、13位三种合法长度的判断逻辑,该卡号是10位,三个判断分支全部不命中,没有任何对应输出语句,因此程序运行后无有效输出。
代码还存在一个同类隐藏bug:13位长度的判断分支中,若卡号开头不是4,没有写INVALID输出逻辑,遇到符合Luhn规则、13位但开头非4的卡号时,同样会出现无输出问题。
修复方案
两处调整即可覆盖所有异常场景:
- 补全13位Visa判断分支的else逻辑,开头不符合4时输出INVALID
- 在三个长度判断的末尾加兜底逻辑:只要长度不属于13/15/16三种合法长度,直接输出INVALID
修改后的对应代码段:
// 13位Visa判断部分 if (count == 13) { first = cnum / 1000000000000; if(first == 4) { printf("VISA\n"); } else { printf("INVALID\n"); } } // 新增兜底长度判断 if (count < 13 || count > 16) { printf("INVALID\n"); }
修改后重新编译测试,4062901840会正常输出INVALID,其他边界场景的无输出问题也会被一并解决。
内容的提问来源于stack exchange,提问作者LaResistance
相关产品推荐
相关产品推荐

