我正在尝试用 C 语言编写一个字符串分割函数,与其他高级语言中已经可用的函数非常相似。
为了避免使用标准 C 数组进行常量内存(重新)分配的麻烦,我想我可以尝试使用链表来做到这一点。这是代码:
#include <stdlib.h>
#include <string.h>
struct sList {
char* str;
struct sList* next;
};
int splitstr (const char* str, const char del, struct sList* subs) {
int subs_nb = 1, i = 0, len = 0;
char* s = str;
struct sList* sub = subs;
while (i < strlen(s)) {
if (s[i] == del) {
sub = malloc(sizeof(struct sList));
sub->str = malloc(sizeof(char) * (len + 1));
strncpy(sub->str, s, len);
sub->str[len] = '\0';
s = s+i;
i = 0;
len = 0;
subs_nb++;
sub = sub->next;
} else {
len++;
}
i++;
}
sub = malloc(sizeof(struct sList));
sub->str = malloc(sizeof(char) * (strlen(s)+1));
strcpy(sub->str, s);
sub->next = NULL;
return subs_nb;
}
我还编写了一个 getter 函数(我单独测试过),以便通过索引轻松访问链表的任何成员:
struct sList* sList_get(struct sList* list, int a) {
struct sList* l = list;
int i = 0;
while(l != NULL) {
if (i == a) return list;
l = l->next;
i++;
}
// If the program reaches this line, a is greater than the number of existing elements in the list
return NULL;
}
最后,我尝试编写一个简单的单元测试来看看这是否按预期工作:
int main () {
struct sList* list;
splitstr("bla.bla.bla.bla.bla.", '.', list);
printf("%s\n", sList_get(list, 0)->str);
return 0;
}
预期输出是第一次出现
.
之前的子字符串:bla
。
像往常一样,在处理动态内存时,我会遇到分段错误。
使用
-g
编译并通过gdb运行程序,我注意到,直到第一个sub = malloc(...)
,sub
和subs
都指向相同的地址。然而,一旦到达下一条指令,sub->str = malloc(...)
,sub
突然指向不同的地址:
(gdb)
35 sub = malloc(sizeof(struct sList));
(gdb) print subs
$1 = (struct sList *) 0x555555555100 <_start>
(gdb) print sub
$2 = (struct sList *) 0x555555555100 <_start>
(gdb) n
36 sub->str = malloc(sizeof(char) * (len + 1));
(gdb) print subs
$3 = (struct sList *) 0x555555555100 <_start>
(gdb) print sub
$4 = (struct sList *) 0x5555555592a0
我认为这很可能是内存泄漏,然后我想通过 valgrind 运行它,它在每个
definitely lost
指令处报告一个 sub = malloc(...)
。
这里有什么问题?
正如@Barmar 指出的,我的内存分配顺序错误;如果我在将其设置为
sub = malloc(...)
后尝试 subs
,而不是为 subs
分配内存,它会使其变得如此 sub
现在指向由 malloc
返回的内存中完全不同的地址,无论subs
指向什么。
我重写了代码如下,现在它可以完美地按预期工作:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct sList {
char* str;
struct sList* next;
};
int splitstr (const char* str, const char del, struct sList* subs);
struct sList* sList_get(struct sList* list, int a);
int main () {
struct sList* list = malloc(sizeof(struct sList));
splitstr("bla.bla.hahaha.bla.bLa", '.', list);
printf("%s\n", sList_get(list, 2)->str);
return 0;
}
int splitstr (const char* str, const char del, struct sList* subs) {
if (subs == NULL) return -1;
int subs_nb = 1, i = 0, len = 0;
char* s = str;
struct sList* sub = subs;
while (i < strlen(s)) {
if (s[i] == del) {
sub->str = malloc(sizeof(char) * (len + 1));
strncpy(sub->str, s, len);
sub->str[len] = '\0';
s = s+i+1;
i = 0;
len = 1;
subs_nb++;
sub->next = malloc(sizeof(struct sList));
sub = sub->next;
} else {
len++;
}
i++;
}
sub->str = malloc(sizeof(char) * (strlen(s)+1));
strcpy(sub->str, s);
sub->next = NULL;
return subs_nb;
}
struct sList* sList_get(struct sList* list, int a) {
struct sList* l = list;
int i = 0;
for (int i = 0; i < a; i++) {
if (l == NULL) return NULL;
l = l->next;
}
return l;
}
(我还重写了 getter 函数,因为我认为它表现不佳 - 我不确定它是否真的如此。)