¿Cómo lanzar una excepción en C?


100

Escribí esto en Google pero solo encontré howtos en C ++,

¿cómo hacerlo en C?


6
C no admite el manejo de excepciones. Para lanzar una excepción en C, debe usar algo específico de la plataforma, como el manejo estructurado de excepciones de Win32, pero para brindar ayuda con eso, necesitaremos saber la plataforma que le interesa.
Jerry Coffin

18
... y no use el manejo estructurado de excepciones de Win32.
Brian R. Bondy

4
Usar setjmp () y longjmp () debería, en teoría, funcionar, pero no creo que valga la pena.
Joseph Quinsey

Respuestas:


77

No hay excepciones en C. En C, los errores se notifican mediante el valor devuelto de la función, el valor de salida del proceso, las señales al proceso ( señales de error del programa (GNU libc) ) o la interrupción del hardware de la CPU (u otra notificación error de la CPU si lo hay) ( Cómo el procesador maneja el caso de división por cero ).

Sin embargo, las excepciones se definen en C ++ y otros lenguajes. El manejo de excepciones en C ++ se especifica en el estándar C ++ "Manejo de excepciones S.15", no hay una sección equivalente en el estándar C.


4
Entonces en C está garantizado que no habrá excepciones, ¿cómo?
httpinterpret

62
@httpinterpret: en C está "garantizado" que no hay excepciones de la misma forma que está "garantizado" que no hay plantillas, ni reflejos, ni unicornios. La especificación del lenguaje simplemente no define tal cosa. Sin embargo, existe setjmp / longjmp, que se puede usar para salir de una función sin regresar. Por lo tanto, los programas podrían crear sus propios mecanismos similares a excepciones si lo desearan, o una implementación en C podría definir extensiones al estándar.
Steve Jessop

2
Un programa maindentro de un .carchivo puede incluir algo de C ++ y, por lo tanto, las excepciones podrían ser lanzadas y atrapadas en el programa, pero las partes del código C permanecerán ignorantes de todo esto, excepto que el lanzamiento y la captura de excepciones a menudo se basan en funciones escritas en C que residen en las bibliotecas de C ++. C se usa porque no puede arriesgarse a que la función llamada a hacer tenga throwque lanzar una excepción. Sin embargo, puede haber una forma específica del compilador / biblioteca / objetivo para lanzar / capturar excepciones. Pero lanzar una instancia de clase tendrá sus propios problemas.
nategoose

61
@Steve: Por favor, avíseme si encuentra un idioma con unicornios, he estado esperando tal cosa durante años.
Brian R. Bondy

5
@ BrianR.Bondy Aquí hay un idioma con unicornios (lo garantizo de la misma manera que está garantizado en C)
Tofandel

37

En C, podría usar la combinación de las funciones setjmp()y longjmp(), definidas en setjmp.h. Ejemplo de Wikipedia

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

Nota: De hecho, le aconsejaría que no los use, ya que funcionan mal con C ++ (no se llamaría a los destructores de objetos locales) y es muy difícil entender qué está pasando. En su lugar, devuelve algún tipo de error.


He visto que setjump / longjump no funciona correctamente en programas C ++ incluso cuando no hay objetos que necesiten ser destruidos cuando se usaron excepciones. Rara vez los uso en programas C.
nategoose

Este fue un enfoque interesante usando setjmp. on-time.com/ddj0011.htm Pero sí, básicamente tienes que inventarlos tú mismo si quieres la ejecución de código fuera de banda sin desenrollar la pila.
Dwayne Robinson

No estoy seguro de por qué la advertencia sobre su uso en C ++ cuando la pregunta es sobre C y C ++ tiene excepciones de todos modos. En cualquier caso, es importante que el OP sepa que para evitar que la implementación de setjmp / longjmp se dispare, siempre tenga en cuenta que primero debe visitar el controlador de errores (para configurarlo) y ese controlador de errores necesita regresar dos veces.

1
Por cierto, el manejo de excepciones era algo que realmente esperaba que hubiera terminado en C11. A pesar de la creencia común, no requiere OOP para funcionar correctamente, y C sufre interminablemente por cada llamada de función que requiere un if(). No veo peligro en eso; ¿Alguien más puede aquí?

@ user4229245 Tengo la impresión (anecdótica) de que cualquier cosa "hecha para usted" se mantiene fuera de la especificación del lenguaje c tanto como sea posible específicamente para mantener c relevante para la programación de máquinas y bare metal donde incluso los fundamentos del status quo como bytes de ocho bits no están no es universal, solo mis pensamientos, aunque no soy un autor de especificaciones
ThorSummoner


20

No hay ningún mecanismo de excepción incorporado en C; necesita simular excepciones y su semántica. Por lo general, esto se logra confiando en setjmpy longjmp.

Hay bastantes bibliotecas y estoy implementando otra más. Se llama exceptions4c ; es portátil y gratuito. Puede echarle un vistazo y compararlo con otras alternativas para ver cuál le queda más.


Leí su ejemplo de código, comencé un proyecto similar con una estructura, pero usa un uuid en lugar de una cadena para identificar cada "excepción". Tu proyecto parece muy prometedor.
umlcat

El enlace de alternativas ahora debería apuntar a github.com/guillermocalvo/exceptions4c/wiki/alternatives
Marcel Waldvogel

¿Conoce usted (o alguien más) acerca de una comparación entre los diferentes paquetes de excepción?
Marcel Waldvogel

11

C es capaz de lanzar una excepción de C ++, de todos modos son códigos de máquina. Por ejemplo, en 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

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

para compilarlo

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

salida

good 1976

http://mentorembedded.github.io/cxx-abi/abi-eh.html tiene más información detallada sobre __cxa_throw.

No estoy seguro de si es portátil o no, y lo pruebo con 'gcc-4.8.2' en Linux.


2
¿Qué diablos es "_ZTIl" ??? No puedo encontrar ninguna referencia a él en ninguna parte y es un completo misterio cómo haría posible que el código C tuviera acceso a std :: type_info. ¡Por favor, ayúdame!
AreusAstarte

1
_ZTIIsignifica typeinfo for long, por ejemplo echo '_ZTIl' | c++filt . Es el esquema de manipulación de g ++.
wcy

y solo por si acaso, asegúrese de llamar al símbolo ac en el bloque de captura para evitar c ++ tanto como sea posible: P (PD, ¡gracias por compartir un excelente ejemplo real!)
ThorSummoner

8

Esta pregunta es muy antigua, pero me encontré con ella y pensé en compartir una técnica: dividir por cero o eliminar la referencia a un puntero nulo.

La pregunta es simplemente "cómo lanzar", no cómo atrapar, o incluso cómo lanzar un tipo específico de excepción. Tuve una situación hace mucho tiempo en la que necesitábamos activar una excepción de C para quedar atrapados en C ++. Específicamente, teníamos informes ocasionales de errores de "llamada de función virtual pura" y necesitábamos convencer a la función _purecall del tiempo de ejecución de C para que arrojara algo. Así que agregamos nuestra propia función _purecall que dividida por cero, y boom, obtuvimos una excepción que pudimos detectar en C ++, e incluso usar un poco de diversión de pila para ver dónde salieron las cosas.


@AreusAstarte en caso de que alguien realmente necesite que se le muestre una división por cero en C:int div_by_nil(int x) { return x/0; }

7

En Win con MSVC hay, __try ... __except ...pero es realmente horrible y no querrás usarlo si puedes evitarlo. Mejor decir que no hay excepciones.


1
En realidad, las excepciones de C ++ están construidas en la parte superior de SEH también bajo el capó.
Calmarius

@Calmarius ¿Qué es SEH ?
Marcel Waldvogel

1
@MarcelWaldvogel Manejo estructurado de excepciones (la forma en que Windows maneja las excepciones de CPU).
Calmarius


3

Como se menciona en numerosos hilos, la forma "estándar" de hacer esto es usando setjmp / longjmp. Publiqué otra solución de este tipo en https://github.com/psevon/exceptions-and-raii-in-c. Según mi conocimiento, esta es la única solución que se basa en la limpieza automática de los recursos asignados. Implementa punteros inteligentes únicos y compartidos, y permite que las funciones intermedias permitan que las excepciones pasen sin detectarse y aún así tener sus recursos asignados localmente limpiados correctamente.


2

C no admite excepciones. Puede intentar compilar su código C como C ++ con Visual Studio o G ++ y ver si se compila como está. La mayoría de las aplicaciones C se compilarán como C ++ sin cambios importantes, y luego puede usar la sintaxis try ... catch.


0

Si escribe código con el patrón de diseño Happy path (es decir, para el dispositivo integrado), puede simular el procesamiento de errores de excepción (también conocido como diferir o finalmente emular) con el operador "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;
}

En realidad, no es un controlador de excepciones, pero le brinda una manera de manejar el error en la salida de la función.

PD: debe tener cuidado de usar goto solo desde el mismo ámbito o más profundo y nunca saltar la declaración de variable.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.