Как хранить символы в массиве, используя указатели и malloc? - PullRequest
0 голосов
/ 22 января 2019

Моя проблема сейчас в том, что я выделил место для разных слов, но у меня проблемы с сохранением этого в виде массива. Даже при том, что есть подобные посты, вроде этого, у меня ничего не получается, и я полностью застрял здесь. Я хочу сохранить этот формат (я не хочу менять определение функции). Благодарен за помощь и комментарии!

#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 голосов
/ 22 января 2019

Я отредактировал ваш код, и теперь он работает правильно:

#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]);
    }
}

Но я предлагаю вам немного почистить.

0 голосов
/ 22 января 2019

Моя проблема сейчас в том, что я выделил место для разных слов, используя 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
        }
    }
}

Как функция это может выглядетькак это: (замена malloc на calloc для инициализированного пространства)

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
0 голосов
/ 22 января 2019

Давайте начнем с логики.

Как обрабатывается строка типа A quick brown fox.?Я бы предложил:

  1. Подсчитать количество слов и объем памяти, необходимый для хранения слов.(В C каждая строка заканчивается нулевым завершающим байтом, \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 -ое слово еще не существует, и wordarray_word_len(&words, i), чтобы получить длину этого слова (намного быстрее, чем вызов strlen(wordarray_word_ptr(&words, i))).

Основная причина, по которой мы не можем использовать char * здесь, заключается в том, что realloc() область данных (на которую указывают указатели слова) может изменить свой адрес.Если бы это произошло, нам пришлось бы настроить каждый указатель в нашем массиве.Вместо этого гораздо проще использовать смещения для области данных.

Единственный недостаток этого подхода заключается в том, что удаление слов не означает соответствующего сокращения в области данных.Однако можно написать простую функцию «компактора», которая перепаковывает данные в новую область, так что дыры, оставленные удаленными словами, «перемещаются» в конец области данных.Обычно в этом нет необходимости, но вы можете добавить элемент в структуру wordarray, скажем, количество потерянных символов в результате удаления слов, чтобы сжатие можно было выполнить эвристически, в следующий раз, когда область данных будет изменена в противном случае..

0 голосов
/ 22 января 2019

Вот решение с использованием 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, а затемновый адрес, который он содержит, также должен быть изменен.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...