C语言GTK串口读取程序:字符串分割后首字符丢失问题排查
串口接收字符串分割后首字符丢失问题分析
我正在使用C语言结合GTK API开发串口读取程序,遇到如下问题:从串口接收的原始字符串在分割为子字符串后,首字符丢失。相关代码如下:
while (1) { memset(buffer, 0, sizeof(buffer)); int len = read(fd, buffer, sizeof(buffer)-1); if (len > 0) { buffer[len] = '\0'; printf("Received: %s\n", buffer); char splittedBuffer[5][1024]; int dataIndex = 0; int bufferIndex = 0; printf("len is %d\n", len); for (int i = 0; i < len; i++){ if(buffer[i] == ';'){ splittedBuffer[dataIndex][bufferIndex] = '\0'; dataIndex ++; bufferIndex = 0; }else{ splittedBuffer[dataIndex][bufferIndex] = buffer[i]; bufferIndex++; } } printf("data0 : %s\n",splittedBuffer[0]); printf("data1 : %s\n",splittedBuffer[1]); printf("data2 : %s\n",splittedBuffer[2]); printf("data3 : %s\n",splittedBuffer[3]); printf("data4 : %s\n",splittedBuffer[4]); printf("after op: %s\n", buffer); } else if (len < 0) { perror("Error reading from serial port"); } }
程序运行输出如下:
Received: 23;10;20;30;40; len is 16 data0 : 23 data1 : 10 data2 : 20 data3 : 30 data4 : 40 after op: 3;10;20;30;40; Received: 24;10;20;30;40; len is 16 data0 : 24 data1 : 10 data2 : 20 data3 : 30 data4 : 40 after op: 4;10;20;30;40;
问题原因分析
首字符丢失的核心原因是栈内存损坏,具体触发点如下:
- 栈上大数组的风险:你在栈上声明了
char splittedBuffer[5][1024];,这会一次性分配5120字节的栈空间。栈的默认大小通常有限(多数系统为几MB),如果当前栈帧中还有其他变量,这么大的数组可能导致栈空间溢出,破坏相邻变量(比如buffer)的内存,最终导致buffer的首字节被意外覆盖。 - 未初始化的数组:
splittedBuffer没有初始化,内存中存在随机垃圾值,虽然当前输出中分割后的子串显示正常,但垃圾值可能在栈内存中引发不可预测的越界覆盖。 - 潜在的越界写入风险:循环中未限制
bufferIndex的最大值,如果串口传入的单字段长度超过1023,会直接越界写入splittedBuffer的内存区域,进一步破坏栈上其他变量。
修复建议
- 改用动态内存分配:避免在栈上分配大数组,改用
malloc分配堆内存:
// 分配二维数组 char **splittedBuffer = malloc(5 * sizeof(char*)); for (int i = 0; i < 5; i++) { splittedBuffer[i] = malloc(1024 * sizeof(char)); memset(splittedBuffer[i], 0, 1024); // 初始化内存为0 } // 使用完成后释放内存 for (int i = 0; i < 5; i++) { free(splittedBuffer[i]); } free(splittedBuffer);
- 限制写入边界:在循环中添加
bufferIndex的上限检查,防止越界:
else{ if (bufferIndex < 1023) { // 预留一个位置给终止符'\0' splittedBuffer[dataIndex][bufferIndex] = buffer[i]; bufferIndex++; } }
- 补全最后字段的终止符:如果串口数据最后没有以
;结尾,循环结束后手动给最后一个字段添加\0:
// 循环结束后处理最后一个未闭合的字段 if (dataIndex < 5 && bufferIndex > 0) { splittedBuffer[dataIndex][bufferIndex] = '\0'; }
内容的提问来源于stack exchange,提问作者Rynxie
相关产品推荐
相关产品推荐

