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

C语言NMEA-0183协议$GPRMC处理代码段错误排查求助

NMEA-0183 GPS数据处理程序段错误排查

问题描述

我是C语言新手,按NMEA-0183协议编写了GPS数据处理程序,需求如下:

  • 从输入的GPS语句中筛选出**校验正确、定位状态字段为'A'**的$GPRMC语句
  • 将UTC时间转换为北京时间后,输出最后一条有效语句的时间

但运行代码时,在if (strcmp(p[0], "$GPRMC") == 0 && *(p[2]+0) == 'A')行出现段错误,需要排查解决。

代码

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

char a[100]; // 喂入的数据数组
char *time;  // 储存时间
int h, m, s, flag = 0;
char *check(char *a);
int main()
{
    while (1)
    {
        memset(a, 0, sizeof(a));
        scanf("%s", a);
        if (a[0] == 'E')
            break;
        time = check(a);
        if (time == NULL)
        {
            continue;
        }
        else
        {
            sscanf(time, "%02d%02d%02d.", &h, &m, &s);
            h = h + 8; // 换算北京时间
            h = h > 24 ? h - 24 : h;
            flag = 1;
        } // 检查返回值
    }
    if (flag)
        printf("%02d:%02d:%02d\n", h, m, s);
    else
        printf("No vaild data\n");
    return 0;
}
char *check(char *a)
{
    char *c = a;
    char *p[20] = {NULL};
    char j[100];                     // 前检测数组
    int answer;                      // 校验值
    int cal = 0;                     // 计算值
    sscanf(c, "$%[^*]*%x", j, &answer); // 拆封
    //printf("%s %x",j,answer);
    for (int s = 0; j[s] != 0; s++)
    {
        cal ^= j[s];
    }
    if (cal % 65536 == answer)
    {
        for (int i = 0; p[i] != NULL; i++)
            p[i] = i == 0 ? strtok(c, ",") : strtok(NULL, ",");
        if (strcmp(p[0], "$GPRMC") == 0 && *(p[2]+0) == 'A')
        {
            return p[1];
        }
    }
    return NULL;
}

输入输出示例

  • 输入:
$GPRMC,014600.00,A,2237.496474,N,11356.089515,E,0.0,225.5,310518,2.3,W,A*23
$GPRMC,010101.130,A,3606.6834,N,12021.7778,E,0.0,238.3,010807,,,A*6C
END
  • 期望输出:09:01:01

段错误原因分析与修复

核心问题:字段拆分循环逻辑错误

check函数中拆分NMEA字段的循环用了for (int i = 0; p[i] != NULL; i++),但p数组初始全为NULL,循环直接终止,导致p[0]、p[2]都是空指针。后续调用strcmp、访问p[2][0]时,会触发空指针访问的段错误。

修复步骤

  1. 修正字段拆分逻辑:改为循环调用strtok直到返回NULL,确保p数组被正确填充。
  2. 添加空指针检查:使用p数组元素前,先判断是否为NULL,避免非法访问。
  3. 优化校验逻辑:NMEA校验和是单字节值,直接比较cal和answer即可,无需取模65536。

修复后的代码

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

char a[100]; // 喂入的数据数组
char *time;  // 储存时间
int h, m, s, flag = 0;
char *check(char *a);
int main()
{
    while (1)
    {
        memset(a, 0, sizeof(a));
        scanf("%s", a);
        if (strncmp(a, "END", 3) == 0) // 更准确的终止判断,避免误判以E开头的合法语句
            break;
        time = check(a);
        if (time == NULL)
        {
            continue;
        }
        else
        {
            sscanf(time, "%02d%02d%02d.", &h, &m, &s);
            h += 8; // 换算北京时间
            if (h >= 24) // 修正逻辑:UTC+8后等于24时需转为0点
                h -= 24;
            flag = 1;
        }
    }
    if (flag)
        printf("%02d:%02d:%02d\n", h, m, s);
    else
        printf("No valid data\n"); // 修正拼写错误vaild→valid
    return 0;
}

char *check(char *a)
{
    char *c = a;
    char *p[20] = {NULL};
    char j[100];
    int answer;
    int cal = 0;

    // 校验格式是否正确,避免非法输入
    if (sscanf(c, "$%[^*]*%x", j, &answer) != 2)
        return NULL;

    // 计算校验和
    for (int s = 0; j[s] != '\0'; s++)
    {
        cal ^= j[s];
    }

    // 校验和不匹配直接返回
    if (cal != answer)
        return NULL;

    // 拆分NMEA字段,避免数组越界
    int i = 0;
    p[i] = strtok(c, ",");
    while (p[i] != NULL && i < 19)
    {
        i++;
        p[i] = strtok(NULL, ",");
    }

    // 检查必要字段是否存在,以及语句类型和定位状态
    if (p[0] != NULL && strcmp(p[0], "$GPRMC") == 0 && p[2] != NULL && p[2][0] == 'A')
    {
        return p[1];
    }

    return NULL;
}

额外优化点

  • 终止判断改为strncmp(a, "END", 3) == 0,避免误判以E开头的合法GPS语句。
  • 修正北京时间转换逻辑:当h等于24时转为0点(如UTC16:00转北京时间24:00,应显示00:00)。
  • 修复拼写错误vaild→valid。
  • 添加sscanf返回值检查,处理格式错误的输入。
  • 字段拆分时限制循环次数,避免数组越界。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 06:40:29