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

C语言实现Luhn算法校验异常排查:部分VISA卡识别失效

信用卡校验代码问题排查:合法VISA卡号被误判为无效

我用C语言实现了Luhn算法及Amex、Mastercard、VISA的卡类型校验逻辑,卡类型校验功能正常,但Luhn算法的check_sum函数存在问题。check50测试显示,合法VISA卡号4111111111111111、4222222222222被判定为INVALID,期望输出为VISA。以下是完整代码及测试结果:

#include <cs50.h>
#include <math.h>
#include <stdio.h>

long get_card_no(void);
int digit_count(long card_no);
int check_amex(long card_no, int noDigits);
int check_mastercard(long card_no, int noDigits);
int check_visa(long card_no, int noDigits);
int check_sum(long card_no, int noDigits);

int main(void)
{
    long card_no = get_card_no();
    int noDigits = digit_count(card_no);
    int amex = check_amex(card_no, noDigits);
    int mastercard = check_mastercard(card_no, noDigits);
    int visa = check_visa(card_no, noDigits);
    int checkSum = check_sum(card_no, noDigits);
    if (checkSum == 0)
    {
        if (amex)
        {
            printf("AMEX\n");
        }
        else if (mastercard)
        {
            printf("MASTERCARD\n");
        }
        else if (visa)
        {
            printf("VISA\n");
        }
        else
        {
            printf("INVALID\n");
        }
    }
    else
    {
        printf("INVALID\n");
    }
}


long get_card_no(void)
{
    long n;
    do
    {
        n = get_long("Number: ");
    }
    while(n < 0);
    return n;
}

int digit_count(long card_no)
{
    int count = 0;
    while(card_no > 1)
    {
        card_no = card_no / 10;
        count++;
    }
    return count;
}

int check_sum(long card_no, int noDigits)
{
    int digit1;
    int digit2;
    int sum1 = 0;
    int sum2 = 0;
    int sum;
    for (int i = 1; i < noDigits; i += 2)
    {
        digit1 = (int)(card_no / pow(10, i)) % 10;
        digit1 *= 2;
        if (digit1 > 9)
        {
            digit1 = digit1 / 10 + digit1 % 10;
        }
        sum1 += digit1;
    }
    for (int j = 0; j < noDigits; j += 2)
    {
        digit2 = (int)(card_no / pow(10, j)) % 10;
        sum2 += digit2;
    }
    sum = sum1 + sum2;
    return sum % 10;

}

int check_amex(long card_no, int noDigits)
{
    int first_digit = card_no / pow(10, (noDigits - 1));
    int second_digit = (int)(card_no / pow(10, (noDigits - 2))) % 10;
    if (noDigits == 15 && first_digit == 3 && (second_digit == 4 || second_digit == 7))
    {
        return 1;
    }
    else
    {
        return 0;
    }
}

int check_mastercard(long card_no, int noDigits)
{
    int first_digit = card_no / pow(10, (noDigits - 1));
    int second_digit = (int)(card_no / pow(10, (noDigits - 2))) % 10;
    if (noDigits == 16 && first_digit == 5 && (second_digit == 1 || second_digit == 2 ||      second_digit == 3 || second_digit == 4 || second_digit == 5))
    {
        return 1;
    }
    else
    {
        return 0;
    }
}

int check_visa(long card_no, int noDigits)
{
    int first_digit = card_no / pow(10, (noDigits - 1));
    if ((noDigits == 16 || noDigits == 13) && first_digit == 4)
    {
        return 1;
    }
    else
    {
        return 0;
    }
}

check50 v3.3.8测试结果:

:) credit.c exists
:) credit.c compiles
:) identifies 378282246310005 as AMEX
:) identifies 371449635398431 as AMEX
:) identifies 5555555555554444 as MASTERCARD
:) identifies 5105105105105100 as MASTERCARD
:( identifies 4111111111111111 as VISA
    expected "VISA\n", not "INVALID\n"
:) identifies 4012888888881881 as VISA
:( identifies 4222222222222 as VISA
    expected "VISA\n", not "INVALID\n"
:) identifies 1234567890 as INVALID
:) identifies 369421438430814 as INVALID
:) identifies 4062901840 as INVALID
:) identifies 5673598276138003 as INVALID
:) identifies 4111111111111113 as INVALID
:) identifies 4222222222223 as INVALID

问题根源及修复方案

1. 数字计数函数digit_count循环条件错误

当前digit_count的循环条件为while(card_no > 1),会导致位数计算少一位:

  • 13位卡号4222222222222最后会停在card_no=1,返回12位而非正确的13位
  • 16位卡号4111111111111111同理少算一位

修复:将循环条件改为while(card_no > 0),确保所有数字都被计数:

int digit_count(long card_no)
{
    int count = 0;
    while(card_no > 0)
    {
        card_no = card_no / 10;
        count++;
    }
    return count;
}

2. pow函数的精度误差问题

pow是浮点函数,计算pow(10, i)时可能出现精度偏移(比如pow(10,15)实际值可能略小于1e15),强制转换为整数后会导致取数字时出错。

修复:用整数运算替代pow,或直接逐位处理数字避免幂运算。

修复后的check_sum函数(推荐逐位处理)

int check_sum(long card_no)
{
    int sum1 = 0;
    int sum2 = 0;
    int count = 0;
    long temp = card_no;

    while (temp > 0)
    {
        int digit = temp % 10;
        if (count % 2 == 1) // 从右数第2、4、6...位
        {
            digit *= 2;
            sum1 += (digit / 10) + (digit % 10);
        }
        else // 从右数第1、3、5...位
        {
            sum2 += digit;
        }
        temp /= 10;
        count++;
    }
    return (sum1 + sum2) % 10;
}

注意:修改后main函数调用check_sum时只需传入card_no,同时更新函数声明。

修复卡类型校验函数中的pow问题

以check_visa为例,用整数循环构建除数:

int check_visa(long card_no, int noDigits)
{
    if (!(noDigits == 13 || noDigits == 16))
    {
        return 0;
    }
    long divisor = 1;
    for (int i = 0; i < noDigits - 1; i++)
    {
        divisor *= 10;
    }
    int first_digit = card_no / divisor;
    return (first_digit == 4) ? 1 : 0;
}

同理修改check_amex和check_mastercard函数中的幂运算部分。

3. 修复后的完整代码

#include <cs50.h>
#include <stdio.h>

long get_card_no(void);
int digit_count(long card_no);
int check_amex(long card_no, int noDigits);
int check_mastercard(long card_no, int noDigits);
int check_visa(long card_no, int noDigits);
int check_sum(long card_no);

int main(void)
{
    long card_no = get_card_no();
    int noDigits = digit_count(card_no);
    int amex = check_amex(card_no, noDigits);
    int mastercard = check_mastercard(card_no, noDigits);
    int visa = check_visa(card_no, noDigits);
    int checkSum = check_sum(card_no);
    if (checkSum == 0)
    {
        if (amex)
        {
            printf("AMEX\n");
        }
        else if (mastercard)
        {
            printf("MASTERCARD\n");
        }
        else if (visa)
        {
            printf("VISA\n");
        }
        else
        {
            printf("INVALID\n");
        }
    }
    else
    {
        printf("INVALID\n");
    }
}

long get_card_no(void)
{
    long n;
    do
    {
        n = get_long("Number: ");
    }
    while(n < 0);
    return n;
}

int digit_count(long card_no)
{
    int count = 0;
    while(card_no > 0)
    {
        card_no = card_no / 10;
        count++;
    }
    return count;
}

int check_sum(long card_no)
{
    int sum1 = 0;
    int sum2 = 0;
    int count = 0;
    long temp = card_no;

    while (temp > 0)
    {
        int digit = temp % 10;
        if (count % 2 == 1)
        {
            digit *= 2;
            sum1 += (digit / 10) + (digit % 10);
        }
        else
        {
            sum2 += digit;
        }
        temp /= 10;
        count++;
    }
    return (sum1 + sum2) % 10;
}

int check_amex(long card_no, int noDigits)
{
    if (noDigits != 15)
    {
        return 0;
    }
    long divisor = 1;
    for (int i = 0; i < noDigits - 2; i++)
    {
        divisor *= 10;
    }
    int first_two = card_no / divisor;
    return (first_two == 34 || first_two == 37) ? 1 : 0;
}

int check_mastercard(long card_no, int noDigits)
{
    if (noDigits != 16)
    {
        return 0;
    }
    long divisor = 1;
    for (int i = 0; i < noDigits - 2; i++)
    {
        divisor *= 10;
    }
    int first_two = card_no / divisor;
    return (first_two >= 51 && first_two <= 55) ? 1 : 0;
}

int check_visa(long card_no, int noDigits)
{
    if (!(noDigits == 13 || noDigits == 16))
    {
        return 0;
    }
    long divisor = 1;
    for (int i = 0; i < noDigits - 1; i++)
    {
        divisor *= 10;
    }
    int first_digit = card_no / divisor;
    return (first_digit == 4) ? 1 : 0;
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 20:50:55