【问题标题】:Implementing a split string by delimiter function in C在C中通过分隔符函数实现拆分字符串
【发布时间】:2013-06-12 08:53:28
【问题描述】:

我正在尝试用 C 编写一个函数,它接受一个指向以'\0' 结尾的连续字符的指针——即一个字符串——和一个单一的常量字符分隔符,然后输出一个指向连续指针的指针,每个指针它指向一个新字符串。这些新字符串对应于在每个分隔符处中断并正确终止的输入字符串。简而言之,我想动态构建一个字符串数组。

为此,我计划使用 malloc() 来分配我需要的内存。 “父数组”将是sizeof(char *) * (count + 2) 字节长,以容纳指向每个分隔子字符串的第一个字符的指针,以及一个终止符。同样,每个“子数组”的长度为 sizeof(char) * (j + 1) 字节,以容纳每个子字符串的所有字符,再加上一个终止符。

到目前为止我的代码是这样的。

#include <stdio.h>
#include <stdlib.h>

char *split(char *string, const char delimiter);

int main(int argc, char *argv[]) {
    char *x = split(argv[1], '.');
    while (*x) {
        printf("%d\n", *x);
    }
    return 0;
}

char *split(char *string, const char delimiter) {
    int length, count, i, j = 0;
    while(*(string++)) {
        if (*string == delimiter) count++;
        length++;
    }
    string -= length;
    char *array = (char *)malloc(sizeof(char *) * (length + 1));
    for(i, j = 0; i < (count + 1); i++) {
        while(*(string++) != delimiter) j++;
        string -= j;
        *array = (char *)malloc(sizeof(char) * (j + 1));
        while(*(string++) != delimiter) *(*array++) = *(string++);
        **array = '\0';
        string++;
        array += sizeof(char *);
    }
    *array = '\0';
    array -= (sizeof(char *) * (length + 1));
    return array;  
}

我的问题是为什么编译器会吐出以下错误?

split2.c: In function ‘split’:
split2.c:25: warning: assignment makes integer from pointer without a cast
split2.c:26: error: invalid type argument of ‘unary *’ (have ‘int’)
split2.c:27: error: invalid type argument of ‘unary *’ (have ‘int’)

我的猜测是,当分配“父数组”的内存时,编译器期望int 值,而不是char * 将存储在那里。如果是这种情况,我该如何正确更正我的代码?

我知道使用string.h 有更简单的方法来做这种事情;我编写这段代码的动机是为了更好地了解指针在 C 中的工作原理。

提前非常感谢!

【问题讨论】:

  • 你为什么要双重取消引用array
  • 为什么不使用strtok
  • length 和 count 在首次使用前都未初始化。
  • @wildplasser: i 也是如此 :)
  • 我在看到前两个错误后停止阅读。它们已经够多了。

标签: c string pointers


【解决方案1】:

我认为您希望 array 作为双指针 char **array

char **array = (char **)malloc(sizeof(char *) * (length + 1));

正如您的逻辑所说,您需要一个 char* 数组,每个数组都指向一个字符串。所以array 应该是双指针。如果您进行此修改,请将返回类型也更改为 char**

如果你想使用双指针,试试这个:

char **split(char *string, const char delimiter) {
    int length = 0, count = 0, i = 0, j = 0;
    while(*(string++)) {
        if (*string == delimiter) count++;
        length++;
    }
    string -= (length + 1); // string was incremented one more than length
    char **array = (char **)malloc(sizeof(char *) * (length + 1));
    char ** base = array;
    for(i = 0; i < (count + 1); i++) {
        j = 0;
        while(string[j] != delimiter) j++;
        j++;
        *array = (char *)malloc(sizeof(char) * j);
        memcpy(*array, string, (j-1));
        (*array)[j-1] = '\0';
        string += j;
        array++;
    }
    *array = '\0';
    return base;  
}

稍后释放这个数组,比如:

i = 0;
while(base[i]) {
    free(base[i]);
    i++;
}
free(base);
base = NULL;

【讨论】:

  • free(base); 后面应始终跟有base = NULL; 以避免出现悬空指针。人们通常会编写一个宏函数来一起完成这些工作。
  • @raj 这是一个非常有用的答案;我想在接受之前先研究一下代码。关于您认为数组应该是char** 的推理:当数组首次初始化以保存 malloc() 的返回时,它只是指向一块连续内存的指针。在我决定稍后在此块中存储一些char*s 之后,它才变成指向指针的指针。我如何协调这与类型应该是单指针还是双指针?也就是说,它开始时是单指针,后来变成双指针?如果我在块的第一个字节中存储一个指针怎么办...
  • ... 然后是 int,例如,在下一个。那么“指向指针的指针”参数肯定是无效的吗?思考这个问题的正确方法是什么?谢谢!
  • 抱歉回复晚了。您分配的每个指针都指向一个连续的内存块。您声明的指针的数据类型决定了应该访问该内存块的类型。这里,array 被声明为char**,并被分配了一个char* 块。这意味着,这个arraychar* 数组的基地址。这就是双指针。指向另一个指针的指针。所以,在这里,从概念上讲,array 本身就是一个双指针。
  • 是的,您可以在指针数组中存储一个int 值(安全,但不推荐,只要指针的大小等于int 的大小),它将被视为地址此后的值,类似于将int 类型转换为float。但是,如果您尝试 free 该地址只是一个 int 值并且可能不是有效地址,则会出现问题。
【解决方案2】:
    *array = (char *)malloc(sizeof(char) * (j + 1));

应该是

    array = (char *)malloc(sizeof(char) * (j + 1));  // malloc returns a pointer, no need to dereference here

然后是这个

    while(*(string++) != delimiter) *(*array++) = *(string++);

应该是

    while(*(string++) != delimiter) *array++ = *(string++); // dereferenceing once would do

最后是这个

    **array = '\0';

应该是

    *array = '\0'; // same as above

上述所有变化的原因都是一样的。 array 是指针,而不是指向指针的指针。

此外,在您的代码中,循环索引 i 从未被初始化,因此必然会导致不确定的行为。要么在声明中初始化它,比如

int length, count, i = 0, j = 0;

或在循环初始化之类的

for(i = 0, j = 0; i < (count + 1); i++) {

希望这会有所帮助!

【讨论】:

  • 你的答案没有错,但你应该避免类型转换 void*.stackoverflow.com/questions/605845/…
  • @Dayalrai: 不错:) 这些天我使用C++ 并没有看到这篇文章,但很高兴知道,谢谢你的链接!
  • @legends2k:如何找到拆分字符串的结尾?
  • @raj How can the end of split strings be found?,好点,我会在最后留下一个空指针
  • @DavidRF: '\0' 等于NULL,空指针的想法好吗?
【解决方案3】:
char *array = (char *)malloc(sizeof(char *) * (length + 1));

应该是

char **array = (char **)malloc(sizeof(char **) * (length + 1));

*array = (char *)malloc(sizeof(char) * (j + 1));

应该是

array[i] = (char *)malloc(sizeof(char) * (j + 1));

你好像是初学者,我建议你更喜欢array[i]而不是使用*array或其他指针操作,这在开始时更简单。

【讨论】:

    猜你喜欢
    • 2018-05-05
    • 2011-03-29
    • 2010-11-10
    • 2014-03-02
    • 1970-01-01
    • 1970-01-01
    • 2015-02-26
    • 2013-10-30
    • 1970-01-01
    相关资源
    最近更新 更多