如何使用指针和malloc将字符存储到数组中?

时间:2019-01-22 12:28:53

标签: c arrays function pointers

我现在的问题是我已经为不同的单词占用了空间,但是在将其存储为数组时遇到了问题。即使有类似的帖子,对于我来说似乎也无济于事,而且我完全陷入了困境。我想保持这种格式(我不想更改函数的定义)。感谢所有帮助和评论!

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



int i, len = 0, counter = 0;
char ** p = 0;

for(i = 0; s[i] != '\0'; i++){
    len++;

    if(s[i] == ' ' || s[i+1] == '\0'){

        counter ++;
 for(i = 0; i < len; i++){
    p[i] = s[i];
    }

}
printf("%d\n", len);
printf("%d\n", counter);
return p; 
}

int main() {
char *s = "This is a string";
int n;
int i;



for(i = 0; i < n*; i++){
 //also not sure how to print this 
}
}

4 个答案:

答案 0 :(得分:0)

这是使用sscanf的解决方案。 scanf和sscanf将空格视为输入的结尾。我已经利用它使它为您工作。

char *str = (char*) "This is a string";
char buffer[50];
char ** p = (char**)malloc(1 * sizeof(*p));
for (int i = 0; str[0] != NULL; i++)
{
    if (i > 0)
    {
        p = (char**)realloc(p, i * sizeof(p));
    }

    sscanf(str, "%s", buffer);
    int read = strlen(buffer);
    str += read + 1;                

    p[i] = (char*)malloc(sizeof(char)*read + 1);
    strcpy(p[i], buffer);
    printf("%s\n", p[i]);
}

由于此指针在两个维度上都在增长,因此每次找到新字符串时,我们都需要调整p本身的大小,然后还应该重新调整其包含的新地址的大小。

答案 1 :(得分:0)

我现在的问题是我已经使用malloc为不同的单词占用了空间,但是在将其存储为数组时遇到了问题。

如果需要用于字符串集合的可寻址存储器,则需要一个指针集合以及每个所需指针的存储器。

在您的代码中:

p = (char**)malloc(counter*sizeof(char*));

您已经创建了指针集合,但是尚未在这些位置创建内存来容纳字符串。 (顺便说一下,演员不是必需的)

这是创建指针集合和每个指针的存储器的基本步骤:

//for illustration, pick sizes for count of strings needed,
//and length of longest string needed.
#define NUM_STRINGS 5
#define STR_LEN     80

char **stringArray = NULL;

stringArray = malloc(NUM_STRINGS*sizeof(char *));// create collection of pointers
if(stringArray)
{
    for(int i=0;i<NUM_STRINGS;i++)
    {
        stringArray[i] = malloc(STR_LEN + 1);//create memory for each string
        if(!stringArray[i])                  //+1 room for nul terminator
        {
            //handle error
        }
    }
}

作为函数,它看起来可能像这样:(用calloc代替malloc来初始化空间)

char ** Create2DStr(size_t numStrings, size_t maxStrLen)
{
    int i;
    char **a = {0};
    a = calloc(numStrings, sizeof(char *));
    for(i=0;i<numStrings; i++)
    {
      a[i] = calloc(maxStrLen + 1, 1);
    }
    return a;
}

在您的split()函数中使用它:

char** split(const char* s, int *n){

    int i, len = 0, counter = 0, lenLongest = 0
    char ** p = 0;

    //code to count words and longest word

    p = Create2DStr(counter, longest + 1); //+1 for nul termination
    if(p)
    {
        //your searching code
        //...
        // when finished, free memory

答案 2 :(得分:0)

让我们从逻辑开始。

如何处理诸如A quick brown fox.之类的字符串?我建议:

  1. 计算单词数,以及存储单词所需的内存量。 (在C语言中,每个字符串都以一个终止的nul字节\0结尾。)

  2. 为指针和单词分配足够的内存。

  3. 复制源字符串中的每个单词。

我们有一个字符串作为输入,我们想要一个字符串数组作为输出。最简单的选择是

char **split_words(const char *source);

其中,如果发生错误,则返回值为NULL,否则返回由NULL指针终止的指针数组。所有这些都是一次动态分配的,因此在返回值上调用free()将同时释放指针及其内容。

让我们根据上述要点开始实施逻辑。

#include <stdlib.h>

char **split_words(const char *source)
{
    size_t      num_chars = 0;
    size_t      num_words = 0;
    size_t      w = 0;
    const char *src;
    char      **word, *data;

    /* Sanity check. */
    if (!source)
        return NULL;  /* split_words(NULL) will return NULL. */

    /* Count the number of words in source (num_words),
       and the number of chars needed to store
       a copy of each word (num_chars). */
    src = source;
    while (1) {

        /* Skip any leading whitespace (not just spaces). */
        while (*src == '\t' || *src == '\n' || *src == '\v' ||
               *src == '\f' || *src == '\r' || *src == ' ')
            src++;

        /* No more words? */
        if (*src == '\0')
            break;

        /* We have one more word. Account for the pointer itself,
           and the string-terminating nul char. */
        num_words++;
        num_chars++;

        /* Count and skip the characters in this word. */
        while (*src != '\0' && *src != '\t' && *src != '\n' &&
               *src != '\v' && *src != '\f' && *src != '\r' &&
               *src != ' ') {
            src++;
            num_chars++;
        }
    }

    /* If the string has no words in it, return NULL. */
    if (num_chars < 1)
        return NULL;

    /* Allocate memory for both the pointers and the data.
       One extra pointer is needed for the array-terminating
       NULL pointer. */
    word = malloc((num_words + 1) * sizeof (char *) + num_chars);
    if (!word)
        return NULL; /* Not enough memory. */

    /* Since 'word' is the return value, and we use
       num_words + 1 pointers in it, the rest of the memory
       we allocated we use for the string contents. */
    data = (char *)(word + num_words + 1);

    /* Now we must repeat the first loop, exactly,
       but also copy the data as we do so. */
    src = source;
    while (1) {

        /* Skip any leading whitespace (not just spaces). */
        while (*src == '\t' || *src == '\n' || *src == '\v' ||
               *src == '\f' || *src == '\r' || *src == ' ')
            src++;

        /* No more words? */
        if (*src == '\0')
            break;

        /* We have one more word. Assign the pointer. */
        word[w] = data;
        w++;

        /* Count and skip the characters in this word. */
        while (*src != '\0' && *src != '\t' && *src != '\n' &&
               *src != '\v' && *src != '\f' && *src != '\r' &&
               *src != ' ') {
            *(data++) = *(src++);
        }

        /* Terminate this word. */
        *(data++) = '\0';
    }

    /* Terminate the word array. */
    word[w] = NULL;

    /* All done! */
    return word;
}

我们可以通过一个小的测试main()来测试上述内容:

#include <stdio.h>

int main(int argc, char *argv[])
{
    char  **all;
    size_t  i;

    all = split_words(" foo Bar. BAZ!\tWoohoo\n More");
    if (!all) {
        fprintf(stderr, "split_words() failed.\n");
        exit(EXIT_FAILURE);
    }

    for (i = 0; all[i] != NULL; i++)
        printf("all[%zu] = \"%s\"\n", i, all[i]);

    free(all);

    return EXIT_SUCCESS;
}

如果我们编译并运行上面的代码,则会得到

all[0] = "foo"
all[1] = "Bar."
all[2] = "BAZ!"
all[3] = "Woohoo"
all[4] = "More"

这种方法的缺点(使用一个malloc()调用来为指针和数据分配内存)是,我们不能轻易地增加数组。我们真的可以将其视为一大团块。


一种更好的方法,尤其是如果我们打算动态添加新单词时,是使用一种结构:

typedef struct {
    size_t   max_words;  /* Number of pointers allocated */
    size_t   num_words;  /* Number of words in array */
    char   **word;       /* Array of pointers */
} wordarray;

不幸的是,这次我们需要分别分配每个单词。但是,如果我们使用一种结构来描述公共分配缓冲区中的每个单词,请说

typedef struct {
    size_t   offset;
    size_t   length;
} wordref;

typedef struct {
    size_t   max_words;
    size_t   num_words;
    wordref *word;
    size_t   max_data;
    size_t   num_data;
    char    *data;
} wordarray;
#define  WORDARRAY_INIT  { 0, 0, NULL, 0, 0, NULL }

static inline const char *wordarray_word_ptr(wordarray *wa, size_t i)
{
    if (wa && i < wa->num_words)
        return wa->data + wa->word[i].offset;
    else
        return "";
}

static inline size_t wordarray_word_len(wordarray *wa, size_t i)
{
    if (wa && i < wa->num_words)
        return wa->word[i].length;
    else
        return 0;
}

想法是,如果您声明

wordarray  words = WORDARRAY_INIT;

您可以使用wordarray_word_ptr(&words, i)来获得指向第i个单词的指针,或者如果还没有第i个单词并且{{1} }以获取该单词的长度(比调用wordarray_word_len(&words, i)快得多)。

在这里我们不能使用strlen(wordarray_word_ptr(&words, i))的根本原因是char *对数据区域(字指针将指向的区域)的访问可能会改变其地址。如果发生这种情况,我们必须调整数组中的每个指针。改用对数据区域的偏移量要容易得多。

此方法的唯一缺点是删除单词并不意味着数据区域相应缩小。但是,可以编写一个简单的“压缩器”功能,将数据重新打包到一个新的区域,以便将删除的单词留下的空洞“移动”到数据区域的末尾。通常,这不是必需的,但是您可能希望将一个成员添加到realloc()结构中,说出从单词删除中丢失的字符数,以便可以在下次数据区将被试探性地进行压缩时否则调整大小。

答案 3 :(得分:0)

我编辑了您的代码,它现在可以正常工作:

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

char** split(const char* s, int *n);
char** split(const char* s, int *n) {
    int i, len = 0, counter = 0;
    char ** p = 0;
    for(int i = 0; ; ++i) {
        if(s[i] == '\0') {
            break;
        }
        if(s[i] == ' ') {
            counter += 1;
        }
    }
    ++counter;

    p = (char **) malloc(counter * sizeof(char*));

    for(int i = 0, c = 0; ; ++i, ++c) {
        if(s[i] == '\0') {
            break;
        }
        len = 0;
        while(s[len + i + 1] != ' ' && s[len + i + 1] != '\0') {
            ++len;
        }
        p[c] = (char *) malloc(len * sizeof(char) + 1);
        int k = 0;
        for(int j = i; j < i + len + 1; ++j) {
            p[c][k++] = s[j];
        }
        p[c][k] = '\0';
        i += len + 1;
    }
    *n = counter;
    return p;
}

int main() {
    char *s = "This is a string";
    int n;
    int i;
    char** split_s = split(s, &n);

    for(i = 0; i < n; i++) {
        printf("%s\n", split_s[i]);
    }
}

但是我建议您进行一些清理。