如何使用指向切片字符串的指针

时间:2016-09-02 03:21:57

标签: c string function pointers memory-management

我读到了关于指向tutorialsPoint的指针的指针。

我自己做了一点测试。我想按空格切片,以便每个单词(包括标点符号)都被视为一个标记,并且逐行返回标记。

以下是代码:

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

char** split(const char* s)
{
    int i = 0, j = 0;
    char** word = malloc(strlen(s)+1);
    * word = malloc(strlen(s)+1);

    while (*s != '\0') {
        if (*s == ' ') {
            i++;
        } else {
            word[i][j] = *s;
            j++;
        }
        i++;
        s++;
    }

    return word;
    //free(word);  //with or without this i get the same outcome.
}

int main(void)
{
    char** words = split("He said 'hello' to me!");
    int i = 0;
    while (words[i] != NULL) {
        puts(words[i]);
        free(words[i]);
        i += 1;
    }
    free(words);
}

它编译,但当我在终端上运行时,我得到分段错误。我在if语句中添加了printf,它会打印每个字母。

我也使用了valgrind,但我无法理解它的含义。

预期产出:

He
said
'hello'
to
me!

2 个答案:

答案 0 :(得分:1)

split就是这样解决的。

char **split(const char *s){
    int i = 0, j;
    int len = strlen(s);
    len += (len & 1);//+1 if len is odd
    char **word = malloc((len / 2 + 1) * sizeof(*word));//+1 for NULL

    while (*s) {
        while(*s == ' ')
            ++s;//skip spaces
        if(!*s)
            break;
        for(len = 0; s[len] && s[len] != ' '; ++len)
            ;
        word[i] = malloc(len + 1);
        for(j = 0; j < len; ++j)
            word[i][j] = *s++;
        word[i++][j] = '\0';
    }
    word[i] = NULL;
    return word;
}

答案 1 :(得分:0)

首先,我会评论你的尝试:

下面:

return word;
free(word);

免费获胜不会被执行!你看到在<{em> return语句之后的任何内容,都没有执行!

此外,使用malloc()动态分配的空间是错误的,请检查我们如何在2D dynamic array中执行此操作,或/并阅读下面的示例。

你看,在没有分配足够的情况下,你正在访问超出内存,这将导致未定义的行为,但你很幸运得到一个分段错误,它会提醒你! :)

我不会调试你的代码,因为这是一个很好的练习机会,但如果你想要更多的话。

以下是我将如何做到这一点,这是一种更简单的方法,但如果你做到了,那么你就可以自己动手了! :)

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

// We return the pointer
char **get(int N, int M) // Allocate the array */
{
    // TODO: Check if allocation succeeded. (check for NULL pointer)
    int i;
    char** table;
    table = malloc(N*sizeof(char *));
    for(i = 0 ; i < N ; i++)
        table[i] = malloc( M*sizeof(char) );
    return table;
}

void free2Darray(char** p, int N) {
    int i;
    for(i = 0 ; i < N ; i++)
        free(p[i]);
    free(p);
}

void zeroFill(char** p, int N, int M) {
    int i, j;
    for(i = 0 ; i < N ; i++)
        for(j = 0 ; j < M ; j++)
            p[i][j] = 0;
}

void print(char** p, int N, int M) {
    int i;
    for(i = 0 ; i < N ; i++)
        if(strlen(p[i]) != 0)
            printf("array[%d] = %s\n", i, p[i]);
}

void split(const char* s, char** words) {
    int i = 0, word_idx = 0, char_idx = 0;
    while(s[i] != '\0') {
        if(s[i] != ' ') {
            words[word_idx][char_idx++] = s[i];
        } else {
            word_idx++;
            char_idx = 0;
        }
        ++i;
    }
}

int main(void)
{
    char** words = get(10, 15); // 10 words, 14 chars max (+1 for n$
    zeroFill(words, 10, 15);

    split("He said 'hello' to me!", words);
    print(words, 10, 15);

    free2Darray(words, 10);
    return 0;

}

输出:

C02QT2UBFVH6-lm:~ gsamaras$ nano main.c
C02QT2UBFVH6-lm:~ gsamaras$ gcc -Wall main.c 
C02QT2UBFVH6-lm:~ gsamaras$ ./a.out 
array[0] = He
array[1] = said
array[2] = 'hello'
array[3] = to
array[4] = me!

解释:

  1. 我假设一个句子最多有10个单词,每个单词都会 最多14个字符(NULL终止符为+1)。
  2. 动态分配10行15列的2D数组。
  3. 将其所有元素初始化为零。我这样做是为了确保 单词以NULL结尾。我当然可以跳过这一步 手动将NULL终止符插入到的确切位置 split()
  4. 将字符串拆分为标记。
  5. 打印文字。
  6. 释放2D阵列的动态分配内存。
  7. 现在,让我再解释一下split()函数:

    实际上你的尝试非常好,所以你可能已经知道我在做什么了:

    1. 我循环遍历字符串,直到遇到NULL终止符。
    2. 只要我不满足空白,我就插入电流 当前单词中的字符。 word_idx让我记住了哪一个 我现在正在填写这个词,以及那个词的哪个特征 char_idx
    3. 当我遇到空白(else案例)时,我必须前进 下一个字(不需要NULL - 终止我填写的字符串 之前,因为我没有初始化我的2D数组),通过递增 word_idx由1.我还将char_idx设置为0,以便我可以开始 填写位置0的新单词。