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

长字符串下Segmentation fault问题:自定义printf调用vfprintf崩溃求助

解决自定义printf风格函数的段错误问题

嘿,我来帮你捋捋这个段错误的事儿!你写的mr_asprintf是想把{i}替换成%d、{s}替换成%s,然后调用vfprintf对吧?格式字符串到23个字符左右就崩,核心问题肯定出在内存分配上——你看你一开始用calloc(1, sizeof(char))给newFormat只分配了1个字节的空间,这完全不够用啊!

问题根源

初始只分配1字节内存,后续拼接替换后的字符串时,必然会越界写入不属于你的内存区域。这种越界操作刚开始可能没触发问题,但当字符串长度达到某个值(比如你说的23字符)时,刚好踩坏了程序的关键内存(比如栈帧、堆管理结构),直接就触发Segmentation fault了。

修复思路和代码示例

要解决这个问题,得保证newFormat的内存能动态容纳替换后的内容,同时正确处理格式字符串的替换逻辑:

#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>

// 调整函数签名,符合asprintf的常规用法:返回写入长度,通过**ret返回分配的字符串
int mr_asprintf(char **ret, const char *format, ...) {
    va_list args;
    va_start(args, format);
    
    // 初始分配足够内存:原格式字符串长度的2倍(替换后字符数可能增加)
    size_t newFormatSize = strlen(format) * 2 + 1;
    char *newFormat = malloc(newFormatSize);
    if (!newFormat) {
        va_end(args);
        return -1;
    }
    newFormat[0] = '\0';
    
    const char *ptr = format;
    char *newPtr = newFormat;
    
    while (*ptr != '\0') {
        if (*ptr == '{' && *(ptr + 1) != '\0') {
            // 匹配到{,检查后续是i还是s
            if (*(ptr + 1) == 'i') {
                strcpy(newPtr, "%d");
                newPtr += 2;
                ptr += 2; // 跳过{i}两个字符
            } else if (*(ptr + 1) == 's') {
                strcpy(newPtr, "%s");
                newPtr += 2;
                ptr += 2; // 跳过{s}两个字符
            } else {
                // 不是目标格式,直接复制{
                *newPtr++ = *ptr++;
            }
        } else {
            // 普通字符,直接复制
            *newPtr++ = *ptr++;
        }
        
        // 检查内存是否不足,不足则扩容
        size_t usedBytes = newPtr - newFormat;
        if (usedBytes >= newFormatSize - 1) {
            newFormatSize *= 2;
            char *tempBuf = realloc(newFormat, newFormatSize);
            if (!tempBuf) {
                free(newFormat);
                va_end(args);
                return -1;
            }
            newFormat = tempBuf;
            newPtr = newFormat + usedBytes;
        }
    }
    *newPtr = '\0'; // 确保字符串以'\0'结尾
    
    // 先计算需要的缓冲区大小,再分配内存写入
    int writeLen = vsnprintf(*ret, 0, newFormat, args);
    if (writeLen > 0) {
        *ret = malloc(writeLen + 1);
        if (*ret) {
            vsnprintf(*ret, writeLen + 1, newFormat, args);
        } else {
            writeLen = -1;
        }
    }
    
    // 清理临时内存
    free(newFormat);
    va_end(args);
    return writeLen;
}

关键修改点

  • 动态内存管理:初始分配足够的内存,并且在内存不足时用realloc扩容,彻底避免越界
  • 正确的格式替换:逐个字符遍历格式字符串,准确识别{i}和{s}并替换
  • 错误检查:所有内存分配操作都做了失败检查,避免内存泄漏和崩溃
  • 符合asprintf规范:调整函数签名,通过指针返回分配的字符串,返回值为写入的字符数

内容的提问来源于stack exchange,提问作者F.MANNOU

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.22 08:50:54