Инициализируйте массив строк в методе в C, используя mallo c () - PullRequest
0 голосов
/ 26 февраля 2020

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

Он должен содержать не менее 5 токенов (1 команда и 4 аргумента).

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

int main(int argc, const char * argv[]) {
    bool moreCommands = false;

    char * in = "ls -l";

    char * parsedInput[6];

    parseUserInput(in, parsedInput);

    return 0;
}

void parseUserInput(const char * input, char * userInput[]) {


    int i = 0;
    int j = 0; // Indicates current index of userInput
    userInput[j] = malloc(8); // 8 is arbitrary

    while (input[i] != '\0') {

        printf("Char %d: %c\n", i, input[i]); // Debug information

        if (input[i] == ' ') { // If space is encountered, start new token in next index of 
                               // string array
            j++;
            userInput[j] = malloc(8);
        } else {
            strlcat(userInput[j], &input[i], 1); // Concat char to end of token in input

        }
        i++;
    }
}

Отладчик ясно показывает 6 элементов в массиве parsedInput типа char *, но локальная переменная userInput (которая должна быть тем же массивом, поскольку она передается по ссылке) не имеет индексов в отладчик.

Я не понимаю, что происходит.

ps Я пытался найти решение, но не смог его найти.

Я хочу метод инициализации массива parsedInput со строками, соответствующими каждому «токену».

Спасибо!

1 Ответ

1 голос
/ 26 февраля 2020

Вы можете делать именно то, что вы пытаетесь, но если вы объявите массив указателей с automati c срок хранения , то вы должны обязательно ограничить количество токены, которые вы назначаете номеру, который вы объявляете для защиты границ вашего массива. Вам также следует рассмотреть возможность выделения 1 дополнительного указателя, чтобы позволить вам сохранить NULL в качестве следующего указателя после вашего последнего действующего токена, который будет служить sentinel NULL, отмечающим конец действительных указателей в вашем массиве. (как это делается с *argv[] и требуется для функций execv, execvp и execve)

Для работы с string-literal , без изменений строки input. Самый простой способ обработки разделительных слов в ' ' (пробел) - просто использовать пару указателей (указатель начала и конца), где вы перемещаете указатель конца вниз по строке, пока не найдете пробел, а затем выделяете память для количества символов между началом и концом (+1 для символа, заканчивающегося нулем ), а затем просто скопируйте символы в только что выделенное хранилище и завершите строку строкой.

Когда ваш l oop продвигает ваш указатель конца на единицу, чтобы он указывал на следующий символ после пробела, установите указатель начала на указатель конца, чтобы он находился в начале следующего токена. Переместите ваш массив указателей указателя к следующему указателю и установите для NULL для стража после копирования каждого слова.

Кроме того, вам нужно сохранить переменную состояния, простую int для использования в качестве 1 (true)/0 (false) флаг для отслеживания, если вы in-word читаете символы или out читаете пробелы. Это позволяет пропускать начальные пробелы, несколько включенных пробелов между токенами или конечные пробелы после последнего токена. Это позволяет вам разделить "ls -l" или " ls -l " на те же два токена "ls" и "-l".

Ваш parseUserInput() может быть записан как:

#define MAXARG 6

void parseUserInput (char **userInput, const char *input)
{
    int i = 0, in = 0;                      /* index, and in/out of word flag */
    const char *p = input, *ep = p;         /* pointer and end-pointer */

    while (i < MAXARG) {                    /* loop while pointers remain */
        if (!*ep || *ep == ' ') {           /* if at nul-char or space */
            size_t len = ep - p;            /* get length of token */
            if (in && len) {                /* in-word and chars in token */
                /* allocate/validate storage for token */
                if (!(userInput[i] = malloc (len + 1))) {
                    perror ("malloc-userInput[i]");
                    break;
                }
                memcpy (userInput[i], p, len);  /* copy len chars to storage */
                userInput[i++][len] = 0;        /* nul-terminate, advance index */
                userInput[i] = NULL;        /* set next pointer NULL */
            }
            if (!*ep)                       /* if at end, break */
                break;
            in = 0;                         /* set in-word flag 0 (false) */
        }
        else {  /* normal word char */
            if (!in)                        /* if not in-word */
                p = ep;                     /* update start to end-pointer */
            in = 1;                         /* set in-word flag 1 (true) */
        }
        ep++;   /* advance to next character */
    }
}

( примечание: порядок параметров переключается, чтобы порядок соответствовал strcpy, memcpy, et c ...)

Добавление короткого main() с свой пример вы можете сделать:

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

#define MAXARG 6

void parseUserInput (char **userInput, const char *input)
{
    int i = 0, in = 0;                      /* index, and in/out of word flag */
    const char *p = input, *ep = p;         /* pointer and end-pointer */

    while (i < MAXARG) {                    /* loop while pointers remain */
        if (!*ep || *ep == ' ') {           /* if at nul-char or space */
            size_t len = ep - p;            /* get length of token */
            if (in && len) {                /* in-word and chars in token */
                /* allocate/validate storage for token */
                if (!(userInput[i] = malloc (len + 1))) {
                    perror ("malloc-userInput[i]");
                    break;
                }
                memcpy (userInput[i], p, len);  /* copy len chars to storage */
                userInput[i++][len] = 0;        /* nul-terminate, advance index */
                userInput[i] = NULL;        /* set next pointer NULL */
            }
            if (!*ep)                       /* if at end, break */
                break;
            in = 0;                         /* set in-word flag 0 (false) */
        }
        else {  /* normal word char */
            if (!in)                        /* if not in-word */
                p = ep;                     /* update start to end-pointer */
            in = 1;                         /* set in-word flag 1 (true) */
        }
        ep++;   /* advance to next character */
    }
}

int main (void) {

    char *in = "ls -l",
        *parsedInput[MAXARG + 1] = { NULL };    /* add +1 for sentinel NULL at end */

    parseUserInput (parsedInput, in);           /* note: parameter order change */

    for (char **p = parsedInput; *p; p++) {     /* loop over filled pointers */
        puts (*p);
        free (*p);      /* don't forget to free what you allocate */
    }
}

( примечание: не забудьте free() выделенное вами хранилище)

Пример Использование / Вывод

$ ./bin/splitinput
ls
-l

Просмотрите все и дайте мне знать, если у вас есть дополнительные вопросы.

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