我的str_split
函数返回(或至少我认为是)char**
-因此本质上是一个字符串列表。它需要一个字符串参数,一个用于分隔字符串的char
分隔符和一个指向int
的指针来放置检测到的字符串数。
我这样做的方法可能效率很低,它是制作一个x长度(x =字符串长度)的缓冲区,然后复制字符串元素,直到到达定界符或'\0'
字符为止。然后,它将缓冲区复制到我们正在返回的char**
中(先前已malloc
被释放,可以从main()
中释放),然后清除缓冲区并重复执行。
尽管该算法可能比较困难,但是由于我的调试代码(_D)表明它已正确复制,因此逻辑肯定是合理的。我被困住的部分是当我在char**
中创建一个main
时,将其设置为等于我的函数。它不会返回null,不会使程序崩溃或引发任何错误,但是似乎也不起作用。我假设这是术语未定义行为。
无论如何,经过一番思考(我是所有这一切的新手),我尝试了其他一些事情,您将在代码中看到这些东西,目前已注释掉。当我使用malloc将缓冲区复制到新字符串,并将该副本传递给上述char **时,它似乎运行得很好。但是,这会造成明显的内存泄漏,因为我以后无法释放它……所以我迷路了。
当我做一些研究时,发现this post几乎完全遵循我的代码思想并且可以正常工作,这意味着str_split的格式(返回值,参数等)没有固有的问题。功能。到目前为止,他只有1个malloc用于char **,并且工作正常。
下面是我的代码。我一直在试图弄清楚这个问题,它使我的大脑混乱不堪,所以我非常感谢帮助!!事先对“ i”,“ b”,“ c”表示抱歉,我知道这有点令人费解。
编辑:应使用以下代码进行提示,
ret[c] = buffer;
printf("Content of ret[%i] = \"%s\" \n", c, ret[c]);
它确实可以正确打印。只有当我从main调用函数时,它才会变得怪异。我猜是因为它超出范围了?
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define DEBUG
#ifdef DEBUG
#define _D if (1)
#else
#define _D if (0)
#endif
char **str_split(char[], char, int*);
int count_char(char[], char);
int main(void) {
int num_strings = 0;
char **result = str_split("Helo_World_poopy_pants", '_', &num_strings);
if (result == NULL) {
printf("result is NULL\n");
return 0;
}
if (num_strings > 0) {
for (int i = 0; i < num_strings; i++) {
printf("\"%s\" \n", result[i]);
}
}
free(result);
return 0;
}
char **str_split(char string[], char delim, int *num_strings) {
int num_delim = count_char(string, delim);
*num_strings = num_delim + 1;
if (*num_strings < 2) {
return NULL;
}
//return value
char **ret = malloc((*num_strings) * sizeof(char*));
if (ret == NULL) {
_D printf("ret is null.\n");
return NULL;
}
int slen = strlen(string);
char buffer[slen];
/* b is the buffer index, c is the index for **ret */
int b = 0, c = 0;
for (int i = 0; i < slen + 1; i++) {
char cur = string[i];
if (cur == delim || cur == '\0') {
_D printf("Copying content of buffer to ret[%i]\n", c);
//char *tmp = malloc(sizeof(char) * slen + 1);
//strcpy(tmp, buffer);
//ret[c] = tmp;
ret[c] = buffer;
_D printf("Content of ret[%i] = \"%s\" \n", c, ret[c]);
//free(tmp);
c++;
b = 0;
continue;
}
//otherwise
_D printf("{%i} Copying char[%c] to index [%i] of buffer\n", c, cur, b);
buffer[b] = cur;
buffer[b+1] = '\0'; /* extend the null char */
b++;
_D printf("Buffer is now equal to: \"%s\"\n", buffer);
}
return ret;
}
int count_char(char base[], char c) {
int count = 0;
int i = 0;
while (base[i] != '\0') {
if (base[i++] == c) {
count++;
}
}
_D printf("Found %i occurence(s) of '%c'\n", count, c);
return count;
}
答案 0 :(得分:0)
使用res
数组存储在ret[c] = buffer;
中的字符串指针指向一个自动数组,该函数在函数返回时会超出范围。该代码随后具有未定义的行为。您应该使用strdup()
分配这些字符串。
还请注意,当字符串不包含分隔符时,返回NULL
是不合适的。为什么不返回带有单个字符串的数组?
这是一个更简单的实现:
#include <stdlib.h>
char **str_split(const char *string, char delim, int *num_strings) {
int i, n, from, to;
char **res;
for (n = 1, i = 0; string[i]; i++)
n += (string[i] == delim);
*num_strings = 0;
res = malloc(sizeof(*res) * n);
if (res == NULL)
return NULL;
for (i = from = to = 0;; from = to + 1) {
for (to = from; string[to] != delim && string[to] != '\0'; to++)
continue;
res[i] = malloc(to - from + 1);
if (res[i] == NULL) {
/* allocation failure: free memory allocated so far */
while (i > 0)
free(res[--i]);
free(res);
return NULL;
}
memcpy(res[i], string + from, to - from);
res[i][to - from] = '\0';
i++;
if (string[to] == '\0')
break;
}
*num_strings = n;
return res;
}
答案 1 :(得分:0)
您正在存储指向堆栈上存在的缓冲区的指针。从函数返回后使用这些指针会导致未定义的行为。
要解决此问题,需要执行以下操作之一:
允许该函数修改输入字符串(即用空终止符替换定界符)并将指针返回到其中。呼叫者必须意识到这可能发生。请注意,按照此处的操作提供字符串文字在C语言中是非法的,因此您需要这样做:
char my_string[] = "Helo_World_poopy_pants";
char **result = str_split(my_string, '_', &num_strings);
在这种情况下,函数还应明确指出字符串文字是不可接受的输入,并将其第一个参数定义为const char* string
(而不是char string[]
)。
允许该函数创建字符串的副本,然后修改副本。您已经表达了对泄漏此内存的担忧,但是这种担忧主要与程序的设计有关,而不是必需的。
单独复制每个字符串,然后稍后将它们全部清除是完全有效的。主要问题是它不方便,而且也毫无意义。
让我们谈第二点。您有几种选择,但是如果您坚持要通过调用free
轻松清理结果,请尝试以下策略:
分配指针数组时,还应使其足够大以容纳字符串的副本:
// Allocate storage for `num_strings` pointers, plus a copy of the original string,
// then copy the string into memory immediately following the pointer storage.
char **ret = malloc((*num_strings) * sizeof(char*) + strlen(string) + 1);
char *buffer = (char*)&ret[*num_strings];
strcpy(buffer, string);
现在,在buffer
上执行所有字符串操作。例如:
// Extract all delimited substrings. Here, buffer will always point at the
// current substring, and p will search for the delimiter. Once found,
// the substring is terminated, its pointer appended to the substring array,
// and then buffer is pointed at the next substring, if any.
int c = 0;
for(char *p = buffer; *buffer; ++p)
{
if (*p == delim || !*p) {
char *next = p;
if (*p) {
*p = '\0';
++next;
}
ret[c++] = buffer;
buffer = next;
}
}
当您需要清理时,只需将其free
调用一次,因为所有内容都存储在一起了。