- android - 多次调用 OnPrimaryClipChangedListener
- android - 无法更新 RecyclerView 中的 TextView 字段
- android.database.CursorIndexOutOfBoundsException : Index 0 requested, 光标大小为 0
- android - 使用 AppCompat 时,我们是否需要明确指定其 UI 组件(Spinner、EditText)颜色
我的 str_split
函数返回(或者至少我认为它返回)一个 char**
- 所以本质上是一个字符串列表。它需要一个字符串参数、一个用于拆分字符串的 char
分隔符和一个指向 int
的指针以放置检测到的字符串数。
我这样做的方法可能非常低效,它是创建一个 x 长度的缓冲区(x = 字符串的长度),然后复制字符串的元素直到我们到达定界符,或者 '\0'
字符。然后它将缓冲区复制到 char**
,这就是我们要返回的内容(并且之前已经过 malloc
,并且可以从 main() 中释放
),然后清除缓冲区并重复。
虽然算法可能有问题,但逻辑绝对正确,因为我的调试代码 (_D) 显示它被正确复制。我坚持的部分是当我在 main
中创建一个 char**
时,将它设置为等于我的函数。它不会返回 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;
}
最佳答案
您正在存储指向堆栈上存在的缓冲区的指针。从函数返回后使用这些指针会导致未定义的行为。
要解决此问题,需要执行以下操作之一:
允许函数修改输入字符串(即用空终止符替换定界符)并返回指向它的指针。调用者必须意识到这可能发生。请注意,在这里提供字符串文字在 C 中是非法的,因此您需要这样做:
char my_string[] = "Helo_World_poopy_pants";
char **result = str_split(my_string, '_', &num_strings);
在这种情况下,该函数还应明确表示字符串文字是 Not Acceptable 输入,并将其第一个参数定义为 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
,因为所有内容都存储在一起。
关于c - 为什么我的 string_split 实现不起作用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55347699/
今天有小伙伴给我留言问到,try{...}catch(){...}是什么意思?它用来干什么? 简单的说 他们是用来捕获异常的 下面我们通过一个例子来详细讲解下
我正在努力提高网站的可访问性,但我不知道如何在页脚中标记社交媒体链接列表。这些链接指向我在 facecook、twitter 等上的帐户。我不想用 role="navigation" 标记这些链接,因
说现在是 6 点,我有一个 Timer 并在 10 点安排了一个 TimerTask。之后,System DateTime 被其他服务(例如 ntp)调整为 9 点钟。我仍然希望我的 TimerTas
就目前而言,这个问题不适合我们的问答形式。我们希望答案得到事实、引用资料或专业知识的支持,但这个问题可能会引发辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visit the
我就废话不多说了,大家还是直接看代码吧~ ? 1
Maven系列1 1.什么是Maven? Maven是一个项目管理工具,它包含了一个对象模型。一组标准集合,一个依赖管理系统。和用来运行定义在生命周期阶段中插件目标和逻辑。 核心功能 Mav
我是一名优秀的程序员,十分优秀!