LINUX.ORG.RU

Литература, пример проекта обработки ошибок на С

 


0

3

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

Как насчёт принципа «что падает — должно упасть как можно раньше»? Если тебе нужна обработка ошибок, её лучше вынести куда-нибудь во вне, а в основном коде выставляй errno, когда надо

XMs ★★★★★
()

Можешь попробовать скрыть часть кода за макросами, но большое количество проверок — это норма для C, привыкай. В особо хардкорных случаях встречал даже аналоги исключений на setjmp/longjmp, но лучше так не делай.

Gvidon ★★★★
()
Последнее исправление: Gvidon (всего исправлений: 1)

пример какого проекта можно посмотреть, чтобы поучиться делать это правильно

Linux kernel
/thread

redgremlin ★★★★★
()

Без RAII это нельзя делать правильно, всегда будет месиво. Переходи на нормальные языки, на plain C писать в XXI веке глупо.

slovazap ★★★★★
()

проект на С

месиво

Всё правильно, принципиально лучше не будет. Где можно - очевидные обертки с проверками, как у Стивенса, кажется в «Сетевом программировании».

anonymous
()

Используй goto.

anonymous
()
Ответ на: комментарий от slovazap

на plain C писать в XXI веке глупо

Не правда. Преимущество разработки на C89 заключается в поразительной кроссплатформенности, до которой ни Java, ни C# сильно не дотягивают. Более того, например, plain C + OpenMP очень даже прогрессивен.

DarkAmateur ★★★★
()
Последнее исправление: DarkAmateur (всего исправлений: 2)
Ответ на: комментарий от slovazap

C++ как раз не отличаются особой кроссплатформенностью т.к. далеко не под все устройства/процессоры есть компилятор плюсов, а еще стандартная либа плюсов очень жирная, и не во всякий контроллер влезет

SZT ★★★★★
()
Последнее исправление: SZT (всего исправлений: 1)

программирование - оно такое. это нормально для кода на C, когда много проверок.

dzidzitop ★★
()
Ответ на: комментарий от SZT

C++ как раз не отличаются особой кроссплатформенностью т.к. далеко не под все устройства/процессоры есть компилятор плюсов

Людей, которые пишут программы «под все устройства/процессоры», не существует.

tailgunner ★★★★★
()
Ответ на: комментарий от slovazap

Я имел в виду прежде всего C++. Java и C# вообще не языки.

Как по мне, то с точностью, да наоборот. Когда возникает непреодолимая потребность в объектах или в других «сложных конструкциях», то проще уже обмазаться Java или Python, приделав обмен с кодом на C через штатные механизмы IPC, чем корячиться с языком, который и в ногу стреляет, и конструкциями/краткостью не блещет.

DarkAmateur ★★★★
()
Ответ на: комментарий от beastie

Людей, которые пишут программы «под все устройства/процессоры», не существует.

Но есть люди, которые чтут POSIX. (Но их всё меньше и меньше.)

Не понял, к чему ты это сказал, но на всякий случай замечу, что POSIX есть еще для меньшего подмножества «всех устройств/процессоров», чем Си++.

tailgunner ★★★★★
()
Ответ на: комментарий от DarkAmateur

Когда возникает непреодолимая потребность в объектах или в других «сложных конструкциях»

...то лучше обратится к врачу. И побыстрее.

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

К тому, что, если придеживаться стандартов (а не всякие там пробежки со sscanf по /proc и компании), то портабелный код писать не так уж и сложно. Как минимум для тех устройств/процессоров, где posix есть.

beastie ★★★★★
()
Последнее исправление: beastie (всего исправлений: 1)
Ответ на: комментарий от beastie

Про кросс-платформенность начал ты.

Нет. Но ты можешь поговорить о ней с SZT.

tailgunner ★★★★★
()
Ответ на: комментарий от DarkAmateur

и конструкциями/краткостью не блещет.

Уже есть C++14. Несколько лет как.

eao197 ★★★★★
()
Ответ на: комментарий от SZT

Ну если под устройство нет компилятора плюсов, то считай что этого устройства не существует. А что до стандартной либы, никто не заставляет её использовать, и более того - что касается bare metal я лично не видел проектов которые её бы использовали. На плюсах можно писать и без стандартной либы, и получается быстрее, проще и безопаснее чем на plain C.

slovazap ★★★★★
()
Ответ на: комментарий от slovazap

Ну если под устройство нет компилятора плюсов, то считай что этого устройства не существует.

Только вот они существуют.

На плюсах можно писать и без стандартной либы, и получается быстрее, проще и безопаснее чем на plain C.

Если писать на плюсах под микроконтроллер с 1 килобайтом флеш памяти, то дополнительные возможности плюсов там нужны как собаке пятая нога. А устраивать в 100500-й раз холивар на тему C vs C++ у меня нет ни времени ни желания.

SZT ★★★★★
()
Ответ на: комментарий от DarkAmateur

Преимущество разработки на C89 заключается в поразительной кроссплатформенности

омг, а зачем сейчас юзать C89, когда уже давно есть прекрасный и намного более удобный C99?

reprimand ★★★★★
()
Ответ на: комментарий от SZT

Только вот они существуют.

Ну это как-бы личные проблемы тех кто в них верит. Используете несуществующее железо - мучайтесь с C.

Если писать на плюсах под микроконтроллер с 1 килобайтом флеш памяти, то дополнительные возможности плюсов там нужны как собаке пятая нога.

Да, только код уменьшается в разы и читабельнее становится, и ошибиться становится невозможно, подумаешь какая ерунда.

slovazap ★★★★★
()
Ответ на: комментарий от slovazap

Ну это как-бы личные проблемы тех кто в них верит.

В них не надо верить, они есть. Найди-ка компилятор плюсов под Zilog Z80

Да, только код уменьшается в разы

Не всегда

и читабельнее становится

Не всегда

и ошибиться становится невозможно

А вот это уже 4.2.

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

SZT ★★★★★
()
Последнее исправление: SZT (всего исправлений: 1)
Ответ на: комментарий от reprimand

зачем делать рассчет на вантузов

Вы уже определитесь, что Вам нужно? Стандарт или «правильный» компилятор? Я выбрал стандарт.

DarkAmateur ★★★★
()
Ответ на: комментарий от SZT

В плюсовом коде ошибок как раз хватает, притом там есть те же ошибки, что и в Си (выходы за пределы массива, разыменование нулевого указателя и далее по списку)

Их меньше, чем в программах на Си, и гораздо меньше, когда структуры данных сложные.

еще добавляется новых граблей со всякими шаблонами, классами итп

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

tailgunner ★★★★★
()
Ответ на: комментарий от DarkAmateur

при чем тут «правильный» компилятор? что подразумевается под «правильный» компилятор?

Я хорошо знаю, что C89 намеренно используют во многих проектах как раз из-за рассчета на windows порт (т.е. они прогнулись), т.к. C99 компилятором visual studio не поддерживается.

Просто я лично не рассчитываю на windows никоим образом. Вы всё-таки выбрали компилятор, а я - стандарт.

reprimand ★★★★★
()
Ответ на: комментарий от SZT

в Си (выходы за пределы массива, разыменование нулевого указателя и далее по списку)

как же задолбали эти теоретики про выход за пределы массива и разыменование нулевого указателя. Сколько можно жевать одно и то же? Откуда вы беретесь?

Любые ошибки, когда ты выходишь за пределы выделенной памяти обычно происходят в следствии невнимательности или недостаточной продуманности алгоритма (бывает что человек «завтыкал»), но они легко ловятся valgrind-ом и ты их УСПЕШНО фиксишь в минимальный срок.

Самая жесть, связанная с этим при разработке ПО на этом ЯП это спроектировать архитектуру таким образом, чтобы не обосраться где-нибудь посреди разработки и не превратить код в нечитабельное месиво.

reprimand ★★★★★
()

Если код идёт по принципу: «делаем одно за другим, при любой ошибке останавливаемся» (довольно часто так и есть), то можно делать так:

int ok = 1;
ok = ok && do1();
ok = ok && do2();
...
if (!ok) {
  return;
}
Пока ok = 1 и твои выражения выдают 1 в случае успеха, твой код выполняется. На сбойнувшем месте за счёт укороченного вычисления булевых выражений твои выражения перестают выполняться, и ты проваливаешься до самого низа.

uuwaan ★★
()
Ответ на: комментарий от uuwaan

обычно ok=0, а error=~0 потому что обрабатывать проще

        int result=0;

        do {
            if( (result=do1()) )
                break;
                
            if( (result=do2()) )
                break;

        } while (0);

        switch(result)
        {
            case -1:
            case -2:
            default:
                fprintf(stderr, "error: %i\n", result);
                break;

            case 0:
                fprintf(stderr, "ok!");
                break;
        }

zudwa
()

goto error — сишный ответ исключениям.

https://www.kernel.org/doc/Documentation/CodingStyle

Ну или в других сишных проектах посмотри, коих много: mpv, gstreamer, ffmpeg, openbsd.

Есть еще такой вариант, удобно, если кто-то будет реюзать библиотеку на крестах или питоне — оно автоматом транслируется в исключение при биндинге gi:

https://developer.gnome.org/glib/stable/glib-Error-Reporting.html

Freyr69 ★★★
()
Последнее исправление: Freyr69 (всего исправлений: 1)
Ответ на: комментарий от reprimand

Любые ошибки, когда ты выходишь за пределы выделенной памяти обычно происходят в следствии невнимательности или недостаточной продуманности алгоритма (бывает что человек «завтыкал»), но они легко ловятся valgrind-ом и ты их УСПЕШНО фиксишь в минимальный срок.

Поймай-ка что-то по типу hearthbleed через valgrind, не зная о его наличии. Или например CVE-2014-1303 - https://www.blackhat.com/docs/eu-14/materials/eu-14-Chen-WebKit-Everywhere-Se...

Ошибки типа hearthbleed и всякие там heap buffer overflow можно словить через valgrind только если кто-то через эти ошибки хакает твою программу, а ты в этот момент предусмотрительно держишь ее запущенной в valgrind. Чтобы зафиксить подобную ошибку, надо для начала о ней знать, а узнать через valgrind о ней можно только если постоянно все программы им запускать, а это будет чудовищная просадка по производительности

SZT ★★★★★
()
Последнее исправление: SZT (всего исправлений: 2)
Ответ на: комментарий от SZT

Ошибки типа hearthbleed и всякие там heap buffer overflow можно словить через valgrind только если кто-то через эти ошибки хакает твою программу

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

Чтобы зафиксить подобную ошибку, надо для начала о ней знать

Когда у тебя большое по объему кода ПО с откровенно хреновой архитектурой и читабельностью кода, неважно Си это или нет, у тебя всегда будут грабли с отладкой и поиском багов. На моей текущей работе с ярким представителем такого софта приходится иметь дело, к сожалению.

-------Лирическое отступление-------

Кто-то трахается с MVVM/MVC, объектами, классами, паттернами, замыканиями, и прочими вещами, сишники будут трахаться с другими проблемами.

Идеального языка без граблей не бывает, думать за программиста компьютер еще не умеет.

reprimand ★★★★★
()
Ответ на: комментарий от dzidzitop

Ага, только еще раз - как это поможет с мессивом проверок на ошибки? Их останется ровно столько же. Даже может добавиться if'ов, бгг.

anonymous
()
Ответ на: комментарий от SZT

микроконтроллер с 1 килобайтом флеш памяти

-nodefaultlibs -no-rtti -no-exceptions и вперёд. Зато можно получить шаблоны, строгую типизацию и прочие плюшки.

RazrFalcon ★★★★★
()
Ответ на: комментарий от reprimand

но они легко ловятся valgrind-ом и ты их УСПЕШНО фиксишь в минимальный срок.

Только в ваших влажных фантазиях.

Выход за пределы массива ловится только фазинг-тестированием, и то есть повезёт.

Если ваша прога, на вашем наборе данных, работает верно - это не значит, что там нет выхода за границы.

RazrFalcon ★★★★★
()
Ответ на: комментарий от SZT

В них не надо верить, они есть. Найди-ка компилятор плюсов под Zilog Z80

Сразу после того как найду этот антиквариат и придумаю зафига он мне нужен.

Не всегда

Ну если криворук пишет, то конечно нет. А у нормальных людей с C++ будет как минимум не хуже чем с C.

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

Вы неосилятор плюсов? Просто вы рассуждаете на уровне детского сада - «там шаблоны, там классы». В C++ как минимум любой массив и указатель можно обернуть в класс, проверяющий границы и инициализированность, и использовать его прозрачно, типобезопасно и без оверхеда помимо самой проверки, можно использовать обёртки с проверкой диапазона в compile time без оверхеда вообще, можно (и нужно) писать без работы с указателями вовсе. В то же время на C даже общую реализацию min() не написать без макросов с сайд-эффектами.

А так и неосилятору должно быть понятно что понятный и безопасный код выглядит как-то так

I2C(pindata, pinclock).Send(header).Send(data)

а ни в коем случае не так:

struct i2c_t i2c;

if (i2c_create(&i2c, pindata, pinclock) == -1) {
    goto error;
}

if (i2c_send(&i2c, header, hsize) == -1) {
    goto error;
}

if (i2c_send(&i2c, data, hsize) == 1) {
    i2c_destroy(&i2c);
    goto error;
}

i2c_destroy(&i2c);

error:

Рекомендую поискать тут ошибки и прикусить язык про 4.2.

slovazap ★★★★★
()
Последнее исправление: slovazap (всего исправлений: 4)
Ответ на: комментарий от slovazap
struct i2c_t i2c;
bool i2c_created = false;
bool err = true;

if(i2c_create(&i2c, pindata, pinclock) < 0) {
    goto done;
}
i2c_created = true;

if(i2c_send(&i2c, header, hsize) < 0) {
    goto done;
}

if(i2c_send(&i2c, data, dsize) == 1) { // кто так API проектирует-то? То ошибка — это -1, то +1…
    goto done;
}

err = false;

done:
if(i2c_created) {
    i2c_destroy(&i2c);
}
// check err
anonymous
()
Ответ на: комментарий от RazrFalcon

Только в ваших влажных фантазиях.

мы живем в параллельных мирах?

фазинг-тестированием

вау, новое хипстерское словечко! вообще, такие тесты должны быть по дефолту если тесты вообще пишутся

Если ваша прога, на вашем наборе данных, работает верно - это не значит, что там нет выхода за границы.

именно для этого я и говорю что тесты писать когда на вход идут левые данные

reprimand ★★★★★
()
Ответ на: комментарий от RazrFalcon

В языках, отличных от сишки, есть bound-checking

...и они занимают свою нишу, языки эти. Не думаешь почему сишка всё еще используется людьми, ведь есть так много других!

Это лучше любых тестов.

Тесты нельзя заменить. Будь у тебя хоть 10k велосипедов с проверками типа bound-checking, тесты нужны.

Ну и статические анализаторы НЕ использовать - ссзб.

reprimand ★★★★★
()
Ответ на: комментарий от slovazap

I2C(pindata, pinclock).Send(header).Send(data)

И как ты предлагаешь тут ошибки обрабатывать? Исключениями?

Если да, то код совсем не идентичен сишному варианту и при большом количество ошибок тормозить будет знатно.

Но выглядит проще, это да.

anonymous
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.