Странное поведение массива с помощью strtok и fgets - PullRequest
0 голосов
/ 30 сентября 2018

Я работаю над программой, которая действует как интерпретатор оболочки, которая читает команды с аргументами и создает дочерний элемент, который выполняет команду с execvp().Я застрял на некоторых манипуляциях со строками для сбора массива символов *args[], особенно с использованием fgets и strtok.

Вот MCVE моего кода.

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>

#define MAX_LINE 80

int main(void){
    //initialize variables
    char *args[MAX_LINE/2 + 1];
    char input[MAX_LINE];
    //char input[MAX_LINE] = "some sentence unknown"; // <-- this line works fine..

    int counter = 0;
    printf("COMMANDER>");
    fflush(stdout);

    //receive input
    fgets(input,MAX_LINE,stdin);

    //parse input
    char *parser;
    parser = strtok(input," \r\t");

    //parse line
    while(parser != NULL){
                args[counter] = parser;
        counter++;  
        parser = strtok(NULL," ");
    }

    //print results
    int i = 0;
    for(i = 0; i < counter + 1;i++){
        printf("1");
        printf(" - %d: %s\n",i,args[i]);    
    } 

    return 0;
}

Проблема здесь в выходе.Когда я пытаюсь запустить это, я получаю следующий вывод:

COMMANDER>some sentence unknown
1 - 0: some
1 - 1: sentence
1 - 2: unknown

1 - 3: (null)

Моя проблема заключается в том, что пустое пространство.Я не могу сказать, откуда он исходит, и независимо от того, что я делаю, он появляется.

Из того, что я могу сказать, это может быть символ \ n в конце строки или что-то еще, но передача этогов execvp, поскольку execvp(args[0],args) создает ошибку, поскольку она интерпретирует эту пустую строку как аргумент "".

Есть закомментированная строка, которая является просто строковым присваиванием в начале main.Если вместо fgets используется это назначение, программа работает, и я получаю желаемый ввод:

COMMANDER>some sentence unknown
1 - 0: some
1 - 1: sentence
1 - 2: unknown
1 - 3: (null)

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

Ответы [ 2 ]

0 голосов
/ 30 сентября 2018

Строка, читаемая fgets(), содержит завершающий символ новой строки '\n'.Вы должны включить его в список символов, распознаваемых strtok() как разделители.Кроме того, вы должны передать один и тот же список обоим вызовам.

Вы также забыли установить запись со смещением count в NULL.Локальные объекты с автоматическим хранением неинициализированы.Также обратите внимание, что вы не должны передавать пустой указатель на printf для спецификатора преобразования %s: он имеет неопределенное поведение, хотя некоторые реализации проверяют аргумент и выводят определенную строку.

Вот исправленная версия:

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>

#define MAX_LINE 80

int main(void){
    char *args[MAX_LINE/2 + 1];
    char input[MAX_LINE];
    int counter;

    printf("COMMANDER>");
    fflush(stdout);

    //receive input
    if (fgets(input, sizeof input, stdin)) {
        //parse input
        char *parser;
        parser = strtok(input, " \f\n\r\t");

        //parse line
        count = 0;
        while (parser != NULL) {
            args[counter] = parser;
            counter++;  
            parser = strtok(NULL, " \f\n\r\t");
        }
        args[counter] = NULL;

        //print results
        int i;
        for (i = 0; i <= counter; i++) {
            printf("1 - %d: %s\n", i, args[i] ? args[i] : "(null)");
        } 
    }
    return 0;
}
0 голосов
/ 30 сентября 2018

Если вы прочитаете, например, эту fgets ссылку , вы увидите, что там написано

Анализ останавливается, если возникает конец файла или обнаружен символ новой строки, вв каком случае str будет содержать символ новой строки .

[Выделить мое]

Что вы«пустое место» - это новая строка, которую fgets добавил в конце строки.


Однако в вашем коде есть намного худшая проблема.

С помощью

char *args[MAX_LINE/2 + 1];

вы определяете массив указателей, но вы оставляете этот массив неинициализированным .В C неинициализированные локальные (и нестатические) переменные действительно неинициализированы.Их содержимое будет неопределенным и будет казаться почти случайным.

Точнее, если вы получите нулевой указатель на args[counter], это просто удача.

Попыткаиспользование этого указателя любым способом без инициализации приведет к неопределенному поведению .

Простым решением является явная инициализация массива, который будет заполнен нулевыми указателями:

char *args[MAX_LINE/2 + 1] = { NULL };

Выше будет "инициализировать нулями" все элементы, что для указателей означает, что они будут NULL.

...