Как выбросить исключение в C?


102

Я набрал это в Google, но нашел только в C ++,

как это сделать на C?


6
C не поддерживает обработку исключений. Чтобы создать исключение в C, вам нужно использовать что-то специфичное для платформы, например структурированную обработку исключений Win32, но чтобы помочь с этим, нам нужно знать платформу, которая вам нужна.
Джерри Коффин,

18
... и не используйте структурированную обработку исключений Win32.
Брайан Р. Бонди

4
Теоретически использование setjmp () и longjmp () должно работать, но я не думаю, что это того стоит.
Джозеф Куинси,

Ответы:


78

В C нет исключений. В C об ошибках сообщают возвращаемое значение функции, значение выхода процесса, сигналы процессу ( сигналы программных ошибок (GNU libc) ) или аппаратное прерывание ЦП (или другое уведомление. ошибка ЦП, если есть) ( Как процессор обрабатывает случай деления на ноль ).

Однако исключения определены в C ++ и других языках. Обработка исключений в C ++ описана в стандарте C ++ «S.15 Exception processing», в стандарте C нет эквивалентного раздела.


4
Итак, в C гарантированно не будет исключений, как?
httpinterpret

62
@httpinterpret: в C "гарантировано" отсутствие исключений точно так же, как "гарантировано" отсутствие шаблонов, отражения или единорогов. Спецификация языка просто не определяет ничего подобного. Однако есть setjmp / longjmp, который можно использовать для выхода из функции без возврата. Таким образом, программы могут создавать свои собственные механизмы, подобные исключениям, если они хотят, или реализация C может определять расширения к стандарту.
Стив Джессоп,

2
Программа, содержащаяся mainв .cфайле, может включать в себя некоторый C ++, и, следовательно, исключения могут быть выброшены и перехвачены в программе, но части кода C будут оставаться в неведении обо всем этом, за исключением того, что генерирование и перехват исключений часто полагаются на функции, написанные на C которые находятся в библиотеках C ++. C используется, потому что вы не можете рисковать, чтобы вызываемая функция throwсама генерировала исключение. Однако может существовать специфический для компилятора / библиотеки / целевой способ генерирования / перехвата исключений. Но бросок экземпляра класса будет иметь свои проблемы.
nategoose

61
@Steve: Пожалуйста, дайте мне знать, если вы найдете язык с единорогами, я ждал этого много лет.
Брайан Р. Бонди

5
@ BrianR.Bondy Вот язык с единорогами (я гарантирую его так же, как и в C)
Tofandel

38

В C вы можете использовать комбинацию setjmp()и longjmp()функций, определенных в setjmp.h. Пример из Википедии

#include <stdio.h>
#include <setjmp.h>

static jmp_buf buf;

void second(void) {
    printf("second\n");         // prints
    longjmp(buf,1);             // jumps back to where setjmp 
                                //   was called - making setjmp now return 1
}

void first(void) {
    second();
    printf("first\n");          // does not print
}

int main() {   
    if ( ! setjmp(buf) ) {
        first();                // when executed, setjmp returns 0
    } else {                    // when longjmp jumps back, setjmp returns 1
        printf("main");         // prints
    }

    return 0;
}

Примечание: на самом деле я бы посоветовал вам не использовать их, поскольку они ужасно работают с C ++ (деструкторы локальных объектов не вызываются), и очень сложно понять, что происходит. Вместо этого верните какую-то ошибку.


Я видел, что setjump / longjump некорректно работает в программах на C ++, даже если при использовании исключений не было объектов, требующих уничтожения. Я редко использую их в программах на C.
nategoose

Это был интересный подход с использованием setjmp. on-time.com/ddj0011.htm Но да, по сути, вы должны изобретать их сами, если хотите, чтобы код выполнялся вне диапазона без раскручивания стека.
Дуэйн Робинсон,

Я не уверен, почему предостережение об использовании их в C ++, когда речь идет о C, и C ++ в любом случае имеет исключения. В любом случае важно, чтобы OP знал, что для того, чтобы реализация setjmp / longjmp не оторвала вам ногу, всегда помните, что вам сначала нужно посетить обработчик ошибок (чтобы настроить его), и этот обработчик ошибок нужно вернуться дважды.

1
Между прочим, я действительно надеялся, что обработка исключений окажется в C11. Несмотря на распространенное мнение, для правильного функционирования ООП не требуется, и C бесконечно страдает от каждого вызова функции, требующего if(). Я не вижу в этом опасности; может кто-нибудь еще здесь?

@ user4229245 У меня сложилось (анекдотическое) впечатление, что все, что "сделано для вас", не входит в спецификацию языка c, насколько это возможно, специально для того, чтобы сохранить c актуальным для программирования на голом железе и машинного программирования, где даже такие основы статус-кво, как восьмибитные байты, не не универсален, просто мои мысли, хотя я не c автор спецификации
ThorSummoner

21

Обычный старый C на самом деле изначально не поддерживает исключения.

Вы можете использовать альтернативные стратегии обработки ошибок, например:

  • возврат кода ошибки
  • возврат FALSEи использование last_errorпеременной или функции.

См. Http://en.wikibooks.org/wiki/C_Programming/Error_handling .


Также в Windows api есть тот же метод обработки ошибок. например GetLastError()в Windows API.
BattleTested

20

В C нет встроенного механизма исключения; вам необходимо моделировать исключения и их семантику. Обычно это достигается за счет использования setjmpи longjmp.

Вокруг довольно много библиотек, и я реализую еще одну. Это называется exceptions4c ; это портативный и бесплатный. Вы можете взглянуть на него и сравнить с другими альтернативами, чтобы увидеть, что вам больше всего подходит.


Я читал вам пример кода, я начал аналогичный проект со структурой, но использовал uuid вместо строки для идентификации каждого «исключения». Ваш проект кажется очень перспективным.
umlcat

Альтернативы Теперь ссылка должна указывать на github.com/guillermocalvo/exceptions4c/wiki/alternatives
Марсель Waldvogel

Знаете ли вы (или кто-либо еще) о сравнении различных пакетов исключений?
Марсель Вальдфогель

11

C может генерировать исключение C ++, в любом случае это машинные коды. Например, в bar.c

// begin bar.c
#include <stdlib.h>
#include <stdint.h>
extern void *__cxa_allocate_exception(size_t thrown_size);
extern void __cxa_throw (void *thrown_exception, void* *tinfo, void (*dest) (void *) );
extern void * _ZTIl; // typeinfo of long
int bar1()
{
   int64_t * p = (int64_t*)__cxa_allocate_exception(8);
   *p = 1976;
   __cxa_throw(p,&_ZTIl,0);
  return 10;
}
// end bar.c

в a.cc,

#include <stdint.h>
#include <cstdio>
extern "C" int bar1();
void foo()
{
  try{
    bar1();
  }catch(int64_t x){
    printf("good %ld",x);
  }
}
int main(int argc, char *argv[])
{
  foo();
  return 0;
}

скомпилировать это

gcc -o bar.o -c bar.c && g++ a.cc bar.o && ./a.out

вывод

good 1976

http://mentorembedded.github.io/cxx-abi/abi-eh.html содержит более подробную информацию о __cxa_throw.

Я не уверен, переносится он или нет, и я тестировал его с помощью gcc-4.8.2 в Linux.


2
Что за хрень "_ЗТИл" ??? Я нигде не могу найти на него ссылки, и это полная загадка, как это могло бы позволить C-коду получить доступ к std :: type_info. Пожалуйста, помогите мне!
AreusAstarte

1
_ZTIIозначает typeinfo for long, например echo '_ZTIl' | c++filt . Это схема искажения g ++.
wcy

и на всякий случай обязательно вызовите символ ac в блоке catch, чтобы максимально избежать использования c ++: P (PS, спасибо за отличный реальный пример!)
ThorSummoner

8

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

Вопрос просто в том, «как выбросить», а не в том, как отловить или даже как выбросить определенный тип исключения. У меня была ситуация давным-давно, когда нам нужно было вызвать исключение из C, чтобы его перехватили в C ++. В частности, у нас были случайные сообщения об ошибках «вызова чисто виртуальной функции», и нам нужно было убедить функцию _purecall среды выполнения C что-то выбросить. Итак, мы добавили нашу собственную функцию _purecall, которая делится на ноль, и бум, мы получили исключение, которое мы могли поймать на C ++ и даже использовать некоторую забаву стека, чтобы увидеть, где что-то пошло не так.


@AreusAstarte на всякий случай, если кому-то действительно нужно показать деление C на ноль:int div_by_nil(int x) { return x/0; }

7

В Win с MSVC есть, __try ... __except ...но это действительно ужасно, и вы не хотите использовать его, если можете этого избежать. Лучше сказать, что здесь нет исключений.


1
На самом деле исключения C ++ также строятся на вершине SEH под капотом.
Calmarius

@Calmarius Что такое SEH ?
Марсель Вальдфогель

1
@MarcelWaldvogel Структурированная обработка исключений (способ обработки исключений ЦП в Windows).
Calmarius

6

В C нет исключений.

Существуют различные хитрые реализации, которые пытаются это сделать (один пример на: http://adomas.org/excc/ ).


3

Как упоминалось во многих потоках, «стандартный» способ сделать это - использовать setjmp / longjmp. Я разместил еще одно такое решение на https://github.com/psevon/exceptions-and-raii-in-c. Насколько мне известно, это единственное решение, которое полагается на автоматическую очистку выделенных ресурсов. Он реализует уникальные и общие смарт-указатели и позволяет промежуточным функциям пропускать исключения без их перехвата и при этом правильно очищать свои локально выделенные ресурсы.


2

C не поддерживает исключения. Вы можете попробовать скомпилировать свой код C как C ++ с помощью Visual Studio или G ++ и посмотреть, будет ли он компилироваться как есть. Большинство приложений C компилируются как C ++ без значительных изменений, и затем вы можете использовать синтаксис try ... catch.


0

Если вы пишете код с шаблоном проектирования «Счастливый путь» (например, для встроенного устройства), вы можете имитировать обработку ошибок исключения (также известную как дефферинг или, наконец, эмуляция) с помощью оператора «goto».

int process(int port)
{
    int rc;
    int fd1;
    int fd2;

    fd1 = open("/dev/...", ...);
    if (fd1 == -1) {
      rc = -1;
      goto out;
    }

    fd2 = open("/dev/...", ...);
    if (fd2 == -1) {
      rc = -1;
      goto out;
    }

    // Do some with fd1 and fd2 for example write(f2, read(fd1))

    rc = 0;

   out:

    //if (rc != 0) {
        (void)close(fd1);
        (void)close(fd2);
    //}

    return rc;
}

На самом деле это не обработчик исключений, но он поможет вам обработать ошибку при выходе из функции.

PS Вы должны быть осторожны, используя goto только из той же или более глубокой области видимости и никогда не переходите через объявление переменных.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.