C函数返回嵌套结构体异常:split功能实现输出不符合预期
C语言split函数输出异常问题分析与修复
问题描述
尝试实现一个简易的字符串分割功能,将分割后的元素存入TOKENS结构体,再通过OUTPUT结构体返回结果集合与元素数量。但运行代码后,前几个分割出的字符串为空,仅最后一个元素正常输出。
原代码
#include <stdio.h> #include <string.h> struct TOKENS { int length; char word[]; }; struct OUTPUT { struct TOKENS *a; int l; }; struct OUTPUT explode(char *a, char *b) { struct TOKENS tokens[10]; int i = 0; char *ptr = strtok(a, b); while (ptr != NULL) { tokens[i].length = strlen(ptr); strcpy(tokens[i].word, ptr); ptr = strtok(NULL, b); i++; } struct OUTPUT r = { .a = &tokens[0], .l = i}; return r; } int main() { char str[] = "This is test for the start"; struct OUTPUT r = explode(str, " "); struct TOKENS *tokens = r.a; printf("%d\n", r.l); for (int i = 0; i < r.l; i++) { printf("%d %s (%d)\n", i, tokens[i].word, tokens[i].length); } return 0; }
实际输出
6 0 (4) 1 (2) 2 (4) 3 (3) 4 (3) 5 start (5)
问题原因
- 柔性数组未分配内存:
struct TOKENS中的char word[]是柔性数组成员,它本身不占用结构体内存,需要在分配结构体时额外为其申请空间。原代码中直接在栈上声明struct TOKENS tokens[10],每个tokens[i].word没有实际存储空间,执行strcpy时会越界写入,破坏内存布局。 - 返回栈内存指针:
explode函数中的tokens是栈上的局部数组,函数返回后栈内存会被回收,OUTPUT结构体中保存的指针指向已失效的内存区域,后续访问属于未定义行为,导致输出异常。
修复方案
改用动态内存分配,为每个TOKENS结构体分配足够空间(结构体大小 + 字符串长度 + 1),并将结果存储在堆上,避免栈内存失效问题。
修复后的代码
#include <stdio.h> #include <string.h> #include <stdlib.h> struct TOKENS { int length; char word[]; }; struct OUTPUT { struct TOKENS **a; int l; }; struct OUTPUT explode(char *a, char *b) { struct OUTPUT r = {.a = NULL, .l = 0}; char *ptr = strtok(a, b); // 先统计分割出的元素数量 int count = 0; char *temp_ptr = ptr; while (temp_ptr != NULL) { count++; temp_ptr = strtok(NULL, b); } // 重新初始化strtok,恢复原始字符串的token遍历 strtok(a, b); // 为指针数组分配内存 r.a = malloc(count * sizeof(struct TOKENS*)); if (!r.a) { r.l = 0; return r; } int i = 0; ptr = strtok(a, b); while (ptr != NULL) { int len = strlen(ptr); // 为单个TOKENS结构体+字符串分配内存 r.a[i] = malloc(sizeof(struct TOKENS) + len + 1); if (!r.a[i]) { // 分配失败时释放已分配的内存,避免内存泄漏 for (int j = 0; j < i; j++) { free(r.a[j]); } free(r.a); r.a = NULL; r.l = 0; return r; } r.a[i]->length = len; strcpy(r.a[i]->word, ptr); ptr = strtok(NULL, b); i++; } r.l = count; return r; } int main() { char str[] = "This is test for the start"; struct OUTPUT r = explode(str, " "); printf("%d\n", r.l); for (int i = 0; i < r.l; i++) { printf("%d %s (%d)\n", i, r.a[i]->word, r.a[i]->length); } // 释放动态分配的内存 for (int i = 0; i < r.l; i++) { free(r.a[i]); } free(r.a); return 0; }
输出结果
6 0 This (4) 1 is (2) 2 test (4) 3 for (3) 4 the (3) 5 start (5)
内容的提问来源于stack exchange,提问作者Googlebot
相关产品推荐
相关产品推荐

