Разбиение строк в C и сохранение их внутри массива строк, но результат как мусор

Я искал здесь, но, пожалуйста, простите меня, если этот пост является дубликатом ... Я просто хочу прояснить вещи, в которых я не уверен ...

Прежде всего, позвольте мне дать представление о том, что я имею в виду:

  • Пользователю будет предложено ввести команду (аналогично командам командной строки/оболочки).
  • Команда будет получена/введена в виде строки, разделенной пробелами.
  • Далее строка будет токенизирована (разделена), будет подсчитано количество слов.
  • Имея под рукой такое количество слов, мне придется создать динамический многомерный массив строк, где размер/количество строк — это количество слов, которые мы подсчитали.

Пример:

Я Имя 1234123 123123

Количество слов/строк внутри этой команды равно 4, поэтому количество указателей на строки будет распределяться динамически.

Я хочу, чтобы эти строки хранились в динамическом многомерном массиве, где:

char ** arguments;
int count;

Я хочу получить доступ к значениям внутри массива arguments как:

for(i = 0; i < count; i++)
    printf("%s", arguments[i]);

МОЯ ПРОБЛЕМА

Когда я пытаюсь получить доступ к этим переменным в моей основной функции, значения внутри переменной arguments являются мусором, а переменная count продолжает увеличиваться, зная, что я инициализировал count переменная возвращается к 0.

Вы можете проверить мой код, если я сделал что-то расплывчатое или неправильное. Спасибо!


Вот коды, которые мне удалось создать там, где я считаю полезными:

/* This function counts the number of words inside the command input. */
int word_count(char * str)
{
    int i, ctr;

    for(ctr = i = 0; str[i] != '\0'; i++)
    {
        while(isspace(str[i]))
            i++;

        if(str[i] != '\0')
        {
            ctr++;

            while(str[i] != '\0' && ! isspace(str[i]))
                i++;
        }
    }

    return ctr;
}

/* This function splits the strings inside the command. */
void split_command(int count, char *** argv, char * command)
{
    char buffer[31];
    int i, j, k;

    *argv = (char **) malloc(sizeof(char *) * count);

    for(i = k = 0; command[i] != '\0'; i++)
    {
        while(isspace(command[i]))
            i++;

        if(command[i] != '\0')
        {
            for(j = 0 ;j < 30 && command[i] != '\0' && ! isspace(command[i]); j++, i++)
                buffer[j] = (j != 0) ? command[i] : toupper(command[i]);

            buffer[j] = '\0';

            if(strlen(buffer) > 0)
            {
                (*argv)[k] = (char *) malloc(sizeof(char) * (strlen(buffer) + 1));
                strcpy((*argv)[k], buffer);
                k++;
            }
        }
    }
}

/* This function will re-initialize the provided arguments and counters. */
void prepare_command(int * count, char *** argv)
{
    int i;

    for(i = 0; i < *count; i++)
    {
        (*argv)[i] = NULL;
        free((*argv)[i]);
    }

    *count = 0;
    free(*argv);
    *argv = NULL;
}

Главная

void main(void)
{
    char ** arguments, * command;
    int count, i;
    boolean end = False; // Assume I created an enum for boolean {False, True}

    /* Some initialization here. */
    count = 0;
    arguments = NULL;

    do
    {
        gets(command);
        count = word_count(command); /* This will return the number of strings inside the command */
        split_command(count, &arguments, command); /* This will allocate an array. */

        /* When I try to display something from here, some values are garbage and some are from the previous allocation...  */
        for(i = 0; i < count; i++)
            printf("%s\n", arguments[i]);

        prepare_command(&count, &arguments);
    }while(!end);

}

PS: я знаю strtok и просто не хочу его использовать, и у меня нет strtok_r ни в одной из моих библиотек. Поэтому я создал свою собственную функцию, чтобы позаботиться о чем-то подобном.

Это может быть длинный пост, но я бы хотел, чтобы вы помогли мне, ребята... Спасибо! Придется отредактировать этот пост, если это необходимо. Спасибо, надеюсь, вы пролили на меня свет. :3 :)


person arjayosma    schedule 29.09.2012    source источник
comment
Компилятор видит, что вы приводите возвращаемое значение malloc(), и наказывает вас.   -  person    schedule 29.09.2012
comment
Ваш prepare_command не освободит блок памяти, поскольку вы сначала назначаете NULL элементу argv, а затем вызываете free для этого значения   -  person Serge    schedule 29.09.2012
comment
Скомпилируйте со всеми включенными предупреждениями и информацией об отладке - например. с gcc -Wall -g в Linux. Используйте отладчик, например gdb. Используйте valgrind для обнаружения утечек памяти и т. д. Используйте strdup для дублирования строки.   -  person Basile Starynkevitch    schedule 29.09.2012
comment
Основываясь на том, что я знаю, malloc возвращает пустоту *, поэтому вы должны преобразовать ее во что-то похожее на значение L... @H2CO3   -  person arjayosma    schedule 29.09.2012
comment
в основной функции command передается неинициализируемому gets.   -  person Serge    schedule 29.09.2012
comment
@aTo Судя по тому, что я видел, этот вопрос помечен как C, а не C++.   -  person    schedule 29.09.2012
comment
@Serge Спасибо за это, изначально мой код был обратным: сначала идет бесплатно, затем присваивается значение NULL, спасибо, что указали на это.   -  person arjayosma    schedule 29.09.2012
comment
@aTo (если вы не получили мой предыдущий комментарий: в C void * неявно совместим с любым типом указателя - приведение - это плохая практика.)   -  person    schedule 29.09.2012
comment
@ H2CO3 H2CO3 Я думаю, это хорошая практика, по крайней мере, указывать свои указатели ...   -  person arjayosma    schedule 29.09.2012
comment
@ H2CO3 Позвольте мне прочитать то, что вы предоставили ... Спасибо, приятель! :)   -  person arjayosma    schedule 29.09.2012
comment
На самом деле я хотел бы вернуть свой голос за ваш комментарий о приведении указателя, возвращаемого malloc... просто потому, что причина в том, чтобы сделать его не похожим, а сделать тип указателя точно таким же))   -  person Serge    schedule 29.09.2012
comment
@H2CO3 void* ... - зависит от стандарта C   -  person Serge    schedule 29.09.2012
comment
@ Серж, тебе тоже лучше прочитать вопрос / ответ, который я связал ...   -  person    schedule 29.09.2012
comment
@ H2CO3 Например, строгий C99 требует приведения типа void*   -  person Serge    schedule 29.09.2012
comment
@H2CO3 Вы читали этот комментарий? Я думаю, что это, вероятно, самый раздражающий совет, который вы видите здесь, в SO. Не то чтобы это плохой совет. Я нахожу это раздражающим, потому что это почти совершенно безвредно, но люди все время жалуются на это. Единственный достойный аргумент, который я смог найти, это то, что он может скрыть ошибку, если вы забыли включить ‹stdlib.h›, чего просто не произойдет, если вы используете приличный компилятор. Я бы хотел, чтобы люди сосредоточились на более важных вещах. – Кароли Хорват. Делать гипс действительно безвредно. В любом случае это не имеет значения, моя предыдущая программа работала с приведением.   -  person arjayosma    schedule 29.09.2012
comment
@aTo Я в первую очередь считаю это вредным, потому что приведение типов - одна из вещей, которые затрудняют чтение кода. Я не хотел быть оскорбительным с этим.   -  person    schedule 29.09.2012
comment
@ H2CO3 Я понимаю вашу точку зрения, приятель :) не беспокойтесь об этом ... Но это (кастинг) действительно очень помогло мне в моих предыдущих программах ... В любом случае спасибо за этот вклад :)   -  person arjayosma    schedule 29.09.2012
comment
@ H2CO3 Да. На самом деле, я полностью согласен с вами, что void* должен быть совместим с любым указателем. Однако я не полностью отвергаю другую точку зрения.   -  person Serge    schedule 29.09.2012


Ответы (1)


Вот фрагмент кода, который я использую:

int parse_args(char** argv, char* data) {
    char c;
    int argc = 0;

    if (argv)
        *argv = data;

    int quoteopen = 0;
    int waitingnextarg = 1;
    for (;(c = *data); data++) {
        switch(c) {
            case '\n':
            case '\t':
            case ' ':
                if (!quoteopen) {
                    if (argv)
                        *data = 0;
                    waitingnextarg = 1;
                }
            break;
            case '"':
                if (argv)
                    *data = 0;
                if (quoteopen) {
                    waitingnextarg = 1;
                }
                quoteopen ^= 1;
            break;
            default:
                if (waitingnextarg) {
                    waitingnextarg = 0;
                    if (argv)
                        argv[argc] = data;
                    argc++;
                }
            break;
        }
    }

    return argc;
}

позвонить с

int argc = parse_args(0, input);
char* argv[argc+1];
parse_args(argv, input);

Осторожно: изменяет входную строку. Это не касается каких-либо выделений памяти, а использует только предварительно выделенную память. При вызове с argv == NULL он подсчитывает и возвращает только argc, при вызове с допустимым указателем argv он изменит входную строку и заполнит argv. Если вам нужно сохранить копию вызова входной строки с помощью

int argc = parse_args(0, input);
char input_copy[strlen(input) +1];
memcpy(input_copy, input, strlen(input) +1);
char* argv[argc+1];
parse_args(argv, input_copy);
person Sergey L.    schedule 29.09.2012