Упражнение K&R 1.19 (обратная функция)

Вот задача:

Напишите функцию reverse(s), которая переворачивает строку символов s . Используйте его, чтобы написать программу, которая переворачивает ввод построчно.

Хорошо, теперь мое выступление:

#include <stdio.h>

#define LIM 40

char line[LIM];
int c;
int reverse(char line[], int lim);
int len;

int main(void) {
    while ((len = reverse(line, LIM)) > 0) {
        ;
    }
    printf("\n      END OF THE PROGRAM     \n");
    return 0;
}

********** THE REVERSE FUNCTION*********
int reverse(char s[], int lim) {
    char rev[LIM];
    int i;
    for (i = 0; i < lim - 1 && (c = getchar()) != EOF && c != '\n'; ++i) {
        s[i] = c;
    }
    if (c == '\n') {
        s[i] = c;
        ++i;
    }
    s[i] = '\0';

    int r;
    for (r = 0; r < lim - 1; ++r) {
        rev[r] = s[i];
        --i;
    }
    int x;
    for (x = 0; x < lim - 1; ++x) {
        putchar(rev[x]);
    }
    printf("\n");
    return r;
}

Вроде работает правильно, но есть некоторые баги, связанные с выводом. Например:

ВВОД: привет всем ВЫВОД: ydobyreve olleh

ВВОД: abc ВЫВОД: cba'

ВХОД: ABC ВЫХОД: CBA'

ВХОД: ABC ABC ВЫХОД: CBA CBA

ВХОД: se se ВЫВОД: es es'

Видеть? В конце вывода происходит какой-то странный " ' ", и я не могу понять, почему эти "артефакты" печатаются. Это происходит случайно (у меня). Не могли бы вы подсказать что-нибудь, что не так в коде?


person Abraham Lincoln    schedule 27.11.2016    source источник
comment
Извините, но это ужасно и слишком сложно. Я даже не знаю, с чего начать! Пустой цикл while в main? Передать глобальную переменную line в функцию reverse, но больше нигде ее не использовать? Тройное условие, если for?   -  person John3136    schedule 28.11.2016
comment
@ John3136 John3136 да, мой код еще не очень хорош, извините. Ну и насчет "реверса": 1-й "за" - получаю строку с ввода. 2-й «для» - я объявляю массив и заполняю его перевернутой строкой. 3-й «за» - я печатаю перевернутую строку.   -  person Abraham Lincoln    schedule 28.11.2016
comment
Почему бы не разделять и властвовать, сделать reverse просто перевернуть строку: не читать строку и не записывать строку. Затем вы можете сконцентрироваться на выполнении одной задачи и просто давать ей готовый ввод, пока она не заработает. Подумайте, хотите ли вы просто напечатать перевернутую строку или перевернуть ее на месте или перевернуть ее копию.   -  person John3136    schedule 28.11.2016
comment
Проблема со 2-м и 3-м циклами заключается в том, что они выполняются до lim - 1, что составляет все 40 символов. Они должны выполняться только для того количества символов, которое фактически содержится в строке. Таким образом, во втором цикле вы должны зацикливаться до тех пор, пока i не достигнет 0, при этом увеличивая r внутри цикла. Затем 3-й цикл может выполняться до тех пор, пока x не станет равным r.   -  person user3386109    schedule 28.11.2016
comment
@ user3386109 хорошо, спасибо, попробую.   -  person Abraham Lincoln    schedule 28.11.2016


Ответы (2)


У вашей функции reverse есть проблемы:

  • Вы не должны сохранять новую строку в массиве s, так как вы не хотите, чтобы она участвовала в обратной операции.

  • Вы должны остановить последующий цикл for, когда дойдете до конца строки в s, а не до конца буфера.

  • Вы должны обнулить массив rev.

  • Вам не нужно выводить массив rev по одному символу за раз, используйте его как строку.

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

#include <stdio.h>

#define LIM 40

int reverse(char line[], int size);

int main(void) {
    char line[LIM];
    int len;

    while (reverse(line, LIM) > 0) {
        continue;
    }
    printf("\n      END OF THE PROGRAM     \n");
    return 0;
}

/* THE REVERSE FUNCTION */
int reverse(char s[], int size) {
    char rev[size];
    int i, r, c, len;
    for (i = 0; i < size - 1 && (c = getchar()) != EOF && c != '\n'; i++) {
        s[i] = c;
    }
    len = i;
    s[i] = '\0';

    for (i = 0; i < len; i++) {
        rev[len - i - 1] = s[i];
    }
    rev[i] = '\0';

    printf("%s\n", rev);
    return len;
}
person chqrlie    schedule 27.11.2016

Немного более модульное решение. Я не был точно уверен, что K&R имел в виду под «по одной строке за раз». Но это изменит строку до тех пор, пока она не найдет новую строку. Затем дождитесь пользователя и повторите.

#include <stdio.h>

#define MAXLINE 1000

int get_line(char s[], int limit);
int reverse(char to[], char from[], int l);

int main() {
    int size;
    char line[MAXLINE];
    while ((size = get_line(line, MAXLINE)) > 0) {
        char revline[size];
        int len = reverse(revline, line, size);
        printf("%s\n", revline);
    }

    return 0;
}

int reverse(char to[], char from[], int l) {
    int i;
    int j = l - 2;
    for (i = 0; i < l; i++, j--) {
        to[i] = from[j];
    }
    to[i] = '\0';
    return i;
}

// read a line into s until limit
// return length of line
int get_line(char s[], int limit) {
    int c = 0;
    int i = 0;

    for (i = 0; i < limit-1 && (c = getchar()) != '\n'; ++i) {
        s[i] = c;
    }
    if (c == '\n') {
        s[i] = c;
        ++i;
    }
    s[i] = '\0';
    return i;
}

Выход:

testing one two
owt eno gnitset
three four
ruof eerht
five six
xis evif
person fairclothjm    schedule 07.03.2020
comment
В некоторых случаях функция getchar() может возвращать EOF. В случае редкой ошибки чтения getchar() будет возвращать EOF при последовательных вызовах, что приведет к тому, что этот код войдет в бесконечный цикл. Если пользователь сигнализирует EOF с клавиатуры, этот код будет продолжать искать дальнейший ввод, и когда ввод, наконец, закончится \n, вывод, скорее всего, будет не таким, как ожидалось. - person ad absurdum; 07.03.2020
comment
Я не понимаю, почему функция get_line() сохраняет здесь новую строку; для этого требуется, чтобы reverse() знал о дополнительном символе. Возможно, reverse() лучше было бы назвать reverse_line(); за исключением того, что он не создает обратную линию.... - person ad absurdum; 07.03.2020
comment
@exnihilo Круто. Посмотрим ваше решение. Кроме того, reverse() фактически создает перевернутую линию. get_line() сохраняет новую строку, потому что я этого хотел. - person fairclothjm; 07.03.2020
comment
Мне не нужно предлагать решение, чтобы получить право указывать на недостатки в других решениях. Вы хотите показать OP, как использовать getchar() для получения строки ввода, поэтому покажите им хороший код. Вы должны проверить EOF, чтобы соответствовать хорошему коду, и обратите внимание, что принятое решение делает это; это идиома C при циклическом переборе getchar(). Отсутствие проверки на EOF приводит к хрупкому коду. Я уже упоминал почему, но продемонстрируйте себе, подав несколько раз EOF с клавиатуры (CTRL-D в Linux) на входе, вывод будет не таким, как ожидалось. - person ad absurdum; 08.03.2020
comment
Кроме того, reverse() на самом деле создает перевернутую линию. -- Это неправда. Строка заканчивается на \n, а перевернутая строка также заканчивается на \n. Проблема, указанная выше, заключается в том, чтобы инвертировать string. Ваш код делает это, но сохранение \n на входе означает, что reverse() должен игнорировать этот символ. Передача строки, которая не содержит \n в конце, не удастся. reverse() ожидает строку, но не возвращает строку (просто строку). Мое намерение состояло не в том, чтобы придираться к вашему ответу, а в том, чтобы предоставить конструктивную критику, которую вы проигнорировали. - person ad absurdum; 08.03.2020
comment
@exnihilo Эй, просто игривый вызов. Не имел в виду, что я не ценю обратную связь. В любом случае, я добавил проверку EOF, и, на мой взгляд, она работает лучше, чем принятый ответ. Ваше здоровье! - person fairclothjm; 08.03.2020