Як закинути виняток у C?


100

Я набрав це в google, але знайшов лише інструкції на C ++,

як це зробити на C?


6
C не підтримує обробку винятків. Щоб викинути виняток на мові C, вам потрібно використовувати щось конкретне для платформи, наприклад, структуровану обробку винятків Win32, - але щоб надати допомогу в цьому, нам потрібно буде знати платформу, яка вам важлива.
Джеррі Коффін

18
... і не використовуйте структуровану обробку винятків Win32.
Брайан Р. Бонді

4
Використання setjmp () та longjmp () теоретично мало би працювати, але я не думаю, що це варте клопоту.
Джозеф Квінсі,

Відповіді:


77

У C. немає винятків. У C помилки повідомляються повернутим значенням функції, значенням виходу процесу, сигналами до процесу ( сигнали про помилки програми (GNU libc) ) або апаратним перериванням процесора (або іншим повідомленням помилка у формі центрального процесора (якщо є) ( Як процесор обробляє випадок ділення на нуль ).

Винятки визначені в C ++ та інших мовах. Обробка винятків у C ++ вказана у стандарті C ++ "S.15 Обробка винятків", у стандарті C немає еквівалентного розділу.


4
Тож у C гарантовано винятків не буде, як?
httpinterpret

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

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

61
@ Стів: Будь ласка, дайте мені знати, якщо ви знайдете мову з єдинорогами, я чекав такого на цілі роки.
Брайан Р. Бонді

5
@ BrianR.Bondy Ось мова з єдинорогами (я гарантую це так само, як це гарантовано в C)
Tofandel

37

У 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 ++, у будь-якому випадку має винятки. У будь-якому випадку, важливо, щоб операційна програма знала, що для того, щоб реалізація setjmp / longjmp не відстрілювала вашу ногу, завжди майте на увазі, що спочатку потрібно відвідати обробник помилок (щоб його налаштувати) і цей обробник помилок потрібно повернутися двічі.

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

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

21

Звичайний старий C фактично не підтримує виключення.

Ви можете використовувати альтернативні стратегії обробки помилок, такі як:

  • повернення коду помилки
  • повернення FALSEта використання last_errorзмінної або функції.

Див. Http://en.wikibooks.org/wiki/C_Programming/Error_handling .


Також windows api має той самий метод для обробки помилок. наприклад, GetLastError()в API Windows.
BattleTested

20

У C немає вбудованого механізму винятків; потрібно імітувати винятки та їх семантику. Зазвичай цього досягають, покладаючись на setjmpі longjmp.

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


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

Тепер посилання на альтернативи повинно вказувати на github.com/guillermocalvo/exceptions4c/wiki/alternatives
Марсель Вальдвогель,

Чи знаєте ви (чи хтось інший) про порівняння між різними пакетами винятків?
Марсель Вальдвогель

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
Що за біса "_ZTIl" ??? Я ніде не можу знайти жодного посилання на нього, і повна загадка, як це могло б зробити для коду C доступ до std :: type_info. Будь ласка, допоможи мені!
AreusAstarte

1
_ZTIIзасоби typeinfo for long, напр echo '_ZTIl' | c++filt . Це схема керування g ++.
wcy

і для доброї міри обов’язково зателефонуйте символу змінного струму в блоці catch, щоб якомога більше уникати c ++: P (PS, дякую, що поділилися чудовим реальним прикладом!)
ThorSummoner

8

Це запитання дуже старе, але я просто наткнувся на нього і подумав, що поділюсь технікою: ділити на нуль або відмінювати нульовий покажчик.

Питання просто в тому, "як кинути", а не в тому, як зловити, або навіть у тому, як кинути певний тип винятку. У мене була ситуація багато років тому, коли нам потрібно було викликати виняток із С, щоб потрапити в С ++. Зокрема, ми випадково повідомляли про помилки "чистого виклику віртуальної функції", і нам потрібно було переконати функцію _purecall середовища виконання C щось кинути. Отже, ми додали власну функцію _purecall, поділену на нуль, і бум, отримали виняток, який ми могли вловити на C ++, і навіть використати деякі розваги для стека, щоб побачити, де щось пішло не так.


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

7

У Win with MSVC є, __try ... __except ...але це дійсно жахливо, і ви не хочете використовувати його, якщо ви можете уникнути цього. Краще сказати, що винятків немає.


1
Насправді винятки C ++ побудовані на верхній частині SEH теж під капотом.
Calmarius

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

1
@MarcelWaldvogel Структурована обробка винятків (спосіб Windows для обробки винятків центрального процесора).
Кальмарій


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;
}

Насправді це не обробник винятків, але він допоможе вам обробити помилку при виході з fucntion.

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.