如何实现基于栈存储输出的字符串分割函数,避免动态内存分配?
如何在C语言中实现无动态内存分配的栈上字符串分割函数?
我原本在循环内通过栈上的变长数组实现了字符串分割,代码如下:
#include <stdlib.h> #include <stdio.h> #include <string.h> void main() { for (int i = 0; i < 1000; i++) { char *str, *delm; str = "The first part/the second part/still in the second part"; // str comes from within the loop delm = "/"; // The function block size_t len = strlen(str); char *ptr; int pos; ptr = strstr(str, delm); if (ptr == NULL) { pos = -1; } else { pos = ptr - str; } if (pos > -1) { char first[pos + 1]; strncpy(first, str, pos); first[pos] = 0; char second[len - pos + 1]; strncpy(second, str + pos + 1, len - pos); second[len - pos] = 0; // end of the block printf("%s - %s\n", first, second); } } }
由于这段分割逻辑经常使用,我想把它封装成类似下面的函数来提升可读性:
void split(const char *str, const char *delm, char *first, char *second){ // the code }
但这样的话,外部无法预知first和second的尺寸,只能用malloc或realloc动态分配,可能影响性能。我想知道有没有技巧能让函数的输出直接存储在栈上,无需动态内存分配?
方案1:让调用者预先分配栈空间并传入长度限制
修改函数签名,让调用者传入预先在栈上分配好的缓冲区,以及对应的最大长度,函数内部负责检查长度是否足够,避免缓冲区溢出。这种方式完全不需要动态内存,所有存储都在调用者的栈上。
示例代码:
#include <stdlib.h> #include <stdio.h> #include <string.h> // 返回值:成功分割返回0,分隔符不存在返回-1,缓冲区不足返回-2 int split(const char *str, const char *delm, char *first, size_t first_max_len, char *second, size_t second_max_len) { char *ptr = strstr(str, delm); if (ptr == NULL) { return -1; } size_t first_len = ptr - str; if (first_len >= first_max_len) { return -2; // 缓冲区不够存第一部分 } strncpy(first, str, first_len); first[first_len] = '\0'; size_t second_len = strlen(ptr + 1); if (second_len >= second_max_len) { return -2; // 缓冲区不够存第二部分 } strncpy(second, ptr + 1, second_len); second[second_len] = '\0'; return 0; } int main() { for (int i = 0; i < 1000; i++) { const char *str = "The first part/the second part/still in the second part"; const char *delm = "/"; char *ptr = strstr(str, delm); if (ptr != NULL) { // 在调用者栈上分配刚好足够的变长数组 char first[ptr - str + 1]; char second[strlen(str) - (ptr - str) + 1]; if (split(str, delm, first, sizeof(first), second, sizeof(second)) == 0) { printf("%s - %s\n", first, second); } } } return 0; }
优点:完全栈分配,性能无损耗;函数封装性好,调用者可以灵活控制缓冲区大小。
缺点:调用者需要先计算所需缓冲区大小,多了一步操作。
方案2:用宏封装分割逻辑(最贴合原代码写法)
直接把分割逻辑封装成宏,宏展开后会直接在调用者的栈帧中分配数组,相当于把代码块嵌入调用点,既实现了复用,又保留了栈分配的特性。
示例代码:
#include <stdlib.h> #include <stdio.h> #include <string.h> #define SPLIT(str, delm, first_var, second_var) \ do { \ const char *_split_str = (str); \ const char *_split_delm = (delm); \ size_t _split_len = strlen(_split_str); \ char *_split_ptr = strstr(_split_str, _split_delm); \ if (_split_ptr != NULL) { \ int _split_pos = _split_ptr - _split_str; \ char first_var[_split_pos + 1]; \ char second_var[_split_len - _split_pos + 1]; \ strncpy(first_var, _split_str, _split_pos); \ first_var[_split_pos] = '\0'; \ strncpy(second_var, _split_str + _split_pos + 1, _split_len - _split_pos); \ second_var[_split_len - _split_pos] = '\0'; #define SPLIT_END \ } \ } while(0) int main() { for (int i = 0; i < 1000; i++) { const char *str = "The first part/the second part/still in the second part"; const char *delm = "/"; SPLIT(str, delm, first, second) printf("%s - %s\n", first, second); SPLIT_END } return 0; }
优点:完全保留原代码的栈变长数组特性,无需额外计算,调用方式简洁;没有函数调用开销。
缺点:宏的调试难度略高;如果分割逻辑复杂,宏代码会显得臃肿。
方案3:C11及以上用结构体返回栈缓冲区指针(需调用者提前分配)
定义一个结构体存储两个指向栈缓冲区的指针,调用者预先在栈上分配好缓冲区,函数填充结构体中的指针和内容。本质和方案1类似,但用结构体打包参数,提升可读性。
示例代码:
#include <stdlib.h> #include <stdio.h> #include <string.h> typedef struct { char *first; char *second; } SplitResult; // 返回值:成功返回非空结构体,分隔符不存在返回空指针 SplitResult split(const char *str, const char *delm, char *first_buf, size_t first_buf_len, char *second_buf, size_t second_buf_len) { SplitResult res = {NULL, NULL}; char *ptr = strstr(str, delm); if (ptr == NULL) { return res; } size_t first_len = ptr - str; if (first_len >= first_buf_len) { return res; } strncpy(first_buf, str, first_len); first_buf[first_len] = '\0'; res.first = first_buf; size_t second_len = strlen(ptr + 1); if (second_len >= second_buf_len) { return res; } strncpy(second_buf, ptr + 1, second_len); second_buf[second_len] = '\0'; res.second = second_buf; return res; } int main() { for (int i = 0; i < 1000; i++) { const char *str = "The first part/the second part/still in the second part"; const char *delm = "/"; char *ptr = strstr(str, delm); if (ptr != NULL) { char first[ptr - str + 1]; char second[strlen(str) - (ptr - str) + 1]; SplitResult res = split(str, delm, first, sizeof(first), second, sizeof(second)); if (res.first != NULL && res.second != NULL) { printf("%s - %s\n", res.first, res.second); } } } return 0; }
优点:用结构体打包返回结果,代码可读性更好;同样是纯栈分配。
缺点:需要C11或更高版本支持;调用者仍需提前分配缓冲区。
内容的提问来源于stack exchange,提问作者Googlebot
相关产品推荐
相关产品推荐

