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

C语言中snprintf函数导致程序暂停并终止的问题排查

问题排查:snprintf调用导致程序崩溃的原因及修复

问题场景

编写to_string函数将customer结构体格式化为字符串,通过print_cus调用该函数时,程序在Windows系统运行时出现暂停终止,调试确认问题出在snprintf调用阶段。相关代码如下:

原to_string函数

char* to_string(customer cus)
{
    char* buffer = (char*) malloc(BUFFER_SIZE);
    snprintf(buffer, BUFFER_SIZE, "||%-30s||%2d/%2d/%4d||%-30s||%-10s||%2d/%2d/%-4d||%-10.4lf||%-16s||"
    , cus.name, cus.birth.date, cus.birth.month, cus.birth.year, cus.address, cus.phone, 
    cus.opening.date, cus.opening.month, cus.opening.year, cus.opening, cus.balance, cus.cardnum);
    return buffer;
}

相关结构体及宏定义

#define FONE_SIZE 10
#define CARD_SIZE 16
#define NAME_SIZE 50
#define ADDRESS_SIZE 100
#define MAXCUS_SIZE 300
#define CHOICE_SIZE 18
#define BUFFER_SIZE 255

typedef struct {
    int date;
    int month;
    int year;
} date;

typedef struct {
    char name[NAME_SIZE];
    date birth;
    char address[ADDRESS_SIZE];
    char phone[FONE_SIZE];
    date opening;
    double balance;
    char cardnum[CARD_SIZE];
} customer;

调用函数print_cus

void print_cus(customer* cus_arr, int cus_num)
{
    printf("||%-30s||%-10s||%-30s||%-10s||%-10s||%-10s||%-16s||\n", "Customer's name", "Birth day", "Customer's address", "Phone number", "Open date", "Balance", "Card number");

    for(int i = 0; i < cus_num; i++)
    {
        char* buffer = to_string(cus_arr[i]);
        puts(buffer);
        free(buffer);
    }
}

错误分析

  1. 格式化符与参数不匹配(直接触发崩溃)
    snprintf参数列表中错误传入了cus.opening这个date结构体变量,但对应的格式化符%-4d仅支持整数类型。将结构体直接传入需要整数的参数位置,会导致内存访问越界,直接引发程序崩溃。同时,此处多传了一个参数:开户日期部分的格式化符%2d/%2d/%-4d仅需要3个整数参数,代码中额外传入的cus.opening导致参数数量与格式化符数量不匹配,进一步加剧未定义行为。

  2. 字符串数组未预留终止符空间
    phone数组定义为FONE_SIZE=10,但格式化时使用%-10s,而C语言字符串必须以\0结尾。如果phone存满10个字符,会没有空间存放终止符,导致%s读取时越界访问内存,引发潜在崩溃或乱码。

  3. malloc未做空指针检查
    to_string中调用malloc后未检查返回值,若内存分配失败,buffer会是NULL,后续snprintf写入NULL指针会直接导致程序崩溃。

修复方案

  1. 修正snprintf参数匹配问题:删除多余的cus.opening参数,确保格式化符与参数一一对应。
  2. 调整字符串数组长度:为phone、cardnum等字符串数组各增加1位长度,预留\0的存储空间。
  3. 添加malloc返回值检查:在使用buffer前确认内存分配成功,避免写入NULL指针。

修复后的代码

修正后的宏定义和结构体

#define FONE_SIZE 11  // 预留1位存'\0'
#define CARD_SIZE 17  // 预留1位存'\0'
#define NAME_SIZE 50
#define ADDRESS_SIZE 100
#define MAXCUS_SIZE 300
#define CHOICE_SIZE 18
#define BUFFER_SIZE 255

typedef struct {
    int date;
    int month;
    int year;
} date;

typedef struct {
    char name[NAME_SIZE];
    date birth;
    char address[ADDRESS_SIZE];
    char phone[FONE_SIZE];
    date opening;
    double balance;
    char cardnum[CARD_SIZE];
} customer;

修正后的to_string函数

char* to_string(customer cus)
{
    char* buffer = (char*) malloc(BUFFER_SIZE);
    if (buffer == NULL) {  // 检查内存分配是否成功
        return NULL;
    }
    // 移除多余的cus.opening参数,确保参数与格式化符匹配
    snprintf(buffer, BUFFER_SIZE, "||%-30s||%2d/%2d/%4d||%-30s||%-10s||%2d/%2d/%4d||%-10.4lf||%-16s||"
    , cus.name, cus.birth.date, cus.birth.month, cus.birth.year, cus.address, cus.phone, 
    cus.opening.date, cus.opening.month, cus.opening.year, cus.balance, cus.cardnum);
    return buffer;
}

优化后的print_cus函数(增加NULL检查)

void print_cus(customer* cus_arr, int cus_num)
{
    printf("||%-30s||%-10s||%-30s||%-10s||%-10s||%-10s||%-16s||\n", "Customer's name", "Birth day", "Customer's address", "Phone number", "Open date", "Balance", "Card number");

    for(int i = 0; i < cus_num; i++)
    {
        char* buffer = to_string(cus_arr[i]);
        if (buffer != NULL) {  // 检查buffer是否有效
            puts(buffer);
            free(buffer);
        }
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 07:18:19