Нашли или выдавили из себя код, который нельзя назвать нормальным,
на который без улыбки не взглянешь?
Не торопитесь его удалять или рефакторить, — запостите его на
говнокод.ру, посмеёмся вместе!
#include <stdio.h>
#define MAXLINE 1000 /* максимальная длина строки в потоке */
int getline(char line[], int maxline);
main()
{
int len; /* длина текущей строки */
char line[MAXLINE]; /* текущая введённая строка */
while((len = getline(line, MAXLINE)) != (-1))
printf("%s", line);
system("PAUSE");
return 0;
}
/* getline: считывает строку в s, удаляет пробелы и знаки табуляции в конце строки, возвращает её длину. Удаляет полностью пустые строки */
getline(char s[], int lim)
{
int c, i;
for(i = 0; i < lim - 1 && (c = getchar()) != EOF && c != '\n'; ++i)
s[i] = c;
if (c == EOF)
return (-1); /* конец ввода */
else if (c == '\n') {
while(s[i-1] == ' ' || s[i-1] == '\t') /* "удаление" пробелов и знаков табуляции в конце строки */
--i;
if (i > 0) { /* если строка непустая */
s[i] = '\n';
++i;
}
s[i] = '\0';
return i;
}
}
Задача: убирать в конце каждой строки лишние пробелы и знаки табуляции, удалять полностью пустые строки.
Решение: при считывании строки вида "abc_____\n" на выходе получаем массив символов "abc\n\0___" (пробелы в конце). Печатаем строки по мере поступления, профит.
1) если конец ввода наступит посередине последней строки - ты её не обрабатываешь - так и надо?
2) если хочешь пыщьпыщь производительности, то следи за позицией последнего введенного непробельного символа еще в цикле чтения, где getchar
3) не ну я понимаю, что это хтонический Си, но опускать возвращаемый тип в определении тела функции - некрасиво
1) Нет, конечно. Эпик фэйл, писала ночью и не заметила. Тогда вопрос снимается - точно говнокод.
2) Да, пожалуй, так и надо делать.
3) Спасибо за замечание. Пока что только учусь, поэтому правильное оформление ещё не вошло в привычку.
Кроме алгоритмического говна (строки 29-30, как верно заметил Steve_Brown), есть еще говно в проектировании. У тебя функция getline (кстати, такая есть стандартной либе glibc, поэтому это имя лучше не использовать) делает сразу три вещи: читает из stdin, фильтрует пробельные символы в конце, еще и пустые строки удаляет. Логичнее разбить эту функцию на три отдельные, скажем, read_line, trim_trailing и delete_empty_line. Такое и тестировать проще, и алгоритм станет намного нагляднее. На счет производительности: сомневаюсь, что после такой переделки она ощутимо просядет.
Не обязательно. Спецификация языка С говорят, что при чтении текстовых потоков в общем случае не гарантируется полного совпадения того, что было записано, и того, что потом читается. В частности, приводится как раз такой пример: если вы писали в файл строки с пробелами в конце, то при чтении эти пробелы могут магическим образом пропасть (т.е. платформа имеет право неявно "оптимизировать" текстовые файлы, удаляя на лету лишний whitespace).
Какой-то частной программе, работающей на нескольких платформах, возможное различие в поведении текстовых потоков может мешать. Вот для того, чтобы изолироваться от таких различий и создать некий platform abstraction layer, может и пригодиться функция, которая и читает стоку из файла, и выкидиывает из нее лишние пробелы на конце. Т.е. это просто функция чтения строки, которая ведет себя на всех платформах одинаково.
(Понятно, что имя лучше дать другое. И внутренне можно реализовать через подфункции.)
Good point. Но это должно быть оформлено соответственно. Все-таки оригинальный ГК мало походит на функцию, выполняющую абстракцию от конкретной платформы.
По поводу подфункций: а если надо будет еще сделать функцию, которая делает то же самое, но еще и в начале пробельные символы удаляет? И что, копипастить? Так что только подфункции, тут без вариантов.
Конечно, с ООП это решалось бы проще, через цепочку классов-фильтров. Се ля ви.
while(s[i-1] == ' ' || s[i-1] == '\t')
Совершенно не проверяет на выход за пределы массива. И перед массивом вполне может быть пробел в памяти -> memory corruption.
2) если хочешь пыщьпыщь производительности, то следи за позицией последнего введенного непробельного символа еще в цикле чтения, где getchar
3) не ну я понимаю, что это хтонический Си, но опускать возвращаемый тип в определении тела функции - некрасиво
2) Да, пожалуй, так и надо делать.
3) Спасибо за замечание. Пока что только учусь, поэтому правильное оформление ещё не вошло в привычку.
Какой-то частной программе, работающей на нескольких платформах, возможное различие в поведении текстовых потоков может мешать. Вот для того, чтобы изолироваться от таких различий и создать некий platform abstraction layer, может и пригодиться функция, которая и читает стоку из файла, и выкидиывает из нее лишние пробелы на конце. Т.е. это просто функция чтения строки, которая ведет себя на всех платформах одинаково.
(Понятно, что имя лучше дать другое. И внутренне можно реализовать через подфункции.)
По поводу подфункций: а если надо будет еще сделать функцию, которая делает то же самое, но еще и в начале пробельные символы удаляет? И что, копипастить? Так что только подфункции, тут без вариантов.
Конечно, с ООП это решалось бы проще, через цепочку классов-фильтров. Се ля ви.
Если абстрагироваться от исходного ГК, насколько оправдана такая замена?
на такое:
ну и !0 это даа, хаккир!
Я как-то вообще не понял, о чем это.
> !0 это даа, хаккир
Дело вкуса, мне так очевиднее. Нужно значение, которое «не ноль», вот я и пишу !(не) 0(ноль)
Теоретически, это должно быть ровно 4294967295 значений, полагая инт в 4 байта.
Кстати, любопытно, что !0 = 0!. Это что-нибудь, да значит.
* нормального булева типа нет,
* под False подразумевается 0,
* True := !False
Совершенно не проверяет на выход за пределы массива. И перед массивом вполне может быть пробел в памяти -> memory corruption.