C语言可变参数函数字符串拼接循环异常触发段错误咨询
问题背景
在树莓派4(Pi4)的FreeBSD arm64环境下测试C代码时遇到异常行为,已知当前逻辑存在更规范的实现方式,当前阶段目标为先跑通基础功能。
存在问题的可变参数字符串拼接函数代码如下:
#include <stdlib.h> #include <stdint.h> /* uints */ #include <string.h> /* char */ #include <stdio.h> /* printf*/ #include <stdarg.h> /* 处理可变数量参数 */ char* combineString(int num, ...) { char* finalStr; finalStr = calloc(600, sizeof(char)); va_list vaList; /* 初始化可变参数列表 */ va_start(vaList, num); printf("%i\n", num); for (int x = 0; x < num; num++) { char* str = va_arg(vaList, char*); strcat(finalStr, str); printf("%s\n", finalStr); } va_end(vaList); return finalStr; }
异常表现
传入参数num=2指定待拼接字符串数量为2时,函数实际循环了5次而非预期的2次,num参数的设计作用为标识传入的可变参数总个数。
调用代码如下:
combineString(2, "f", "chartypehere");
实际运行输出:
2 f fchartypehere fchartypehere fchartypehere fchartypeherechartypehere
输出中chartypehere被重复拼接,不符合仅循环2次的预期。使用gcc编译后通过gdb调试触发如下段错误:
Program received signal SIGSEGV, Segmentation fault. Address not mapped to object. strcat (s=<optimized out>, append=<optimized out>) at /usr/src/lib/libc/string/strcat.c:46 46 /usr/src/lib/libc/string/strcat.c: No such file or directory.
初步判断异常属于未定义行为引发的巧合现象,但无法定位代码中具体的错误写法。
根因分析
核心错误是for循环的递增变量写错:
- 循环语句写为
for (int x = 0; x < num; num++),每次循环执行的是num++,循环变量x始终保持初始值0,永远满足x < num的判断条件,循环根本不会在执行2次后自动终止。 - 可变参数列表仅传入了2个字符串指针,循环超出实际参数个数后继续调用
va_arg读取参数属于标准未定义行为:程序会把栈上相邻内存的任意值当作char*指针解析,要么读到非法内存地址触发strcat访问未映射内存段,抛出你看到的SIGSEGV段错误;要么巧合读到之前传入的字符串常量地址,就会出现重复拼接chartypehere的现象。
修复方案
将for循环的递增操作从num++修改为x++即可修复循环次数异常的问题,修正后的循环代码如下:
for (int x = 0; x < num; x++) { char* str = va_arg(vaList, char*); strcat(finalStr, str); printf("%s\n", finalStr); }
注意:当前实现给
finalStr固定分配600字节空间,如果后续传入的待拼接字符串总长度超过599字节(需预留1字节存放字符串终止符\0),会触发缓冲区溢出,同样属于未定义行为。等基础功能跑通后,可以优化为先遍历计算所有待拼接字符串的总长度,再按需分配对应大小内存的实现,规避溢出风险。
内容的提问来源于stack exchange,提问作者CoN
相关产品推荐
相关产品推荐

