Buscar..


Introducción

Una aserción es un predicado de que la condición presentada debe ser verdadera en el momento en que el software encuentra la aserción. Las más comunes son las aserciones simples , que se validan en el momento de la ejecución. Sin embargo, las afirmaciones estáticas se verifican en el momento de la compilación.

Sintaxis

  • afirmar (expresión)
  • static_assert (expresión, mensaje)
  • _Static_assert (expresión, mensaje)

Parámetros

Parámetro Detalles
expresión Expresión de tipo escalar.
mensaje cadena literal que se incluirá en el mensaje de diagnóstico.

Observaciones

Tanto assert como static_assert son macros definidas en assert.h .

La definición de assert depende de la macro NDEBUG que no se define por la biblioteca estándar. Si se define NDEBUG , assert es un no-op:

#ifdef NDEBUG
#  define assert(condition) ((void) 0)
#else
#  define assert(condition) /* implementation defined */
#endif

La opinión varía sobre si NDEBUG siempre debe usarse para compilaciones de producción.

  • El pro-campo argumenta que assert llamadas abort y los mensajes de aserción no son útiles para los usuarios finales, por lo que el resultado no es útil para el usuario. Si tiene condiciones fatales para verificar el código de producción, debe usar las condiciones ordinarias if/else y exit o quick_exit para finalizar el programa. En contraste con la abort , estos permiten que el programa realice una limpieza (a través de las funciones registradas con atexit o at_quick_exit ).
  • La con-campamento sostiene que assert llamadas nunca deben disparar en el código de producción, pero si lo hacen, la condición que se comprueba significa que hay algo radicalmente equivocado y el programa se comportará mal peor si la ejecución continúa. Por lo tanto, es mejor tener las afirmaciones activas en el código de producción porque si se disparan, el infierno ya se ha desatado.
  • Otra opción es usar un sistema casero de aserciones que siempre realice la verificación pero que maneje los errores de manera diferente entre el desarrollo (cuando sea apropiado abort ) y la producción (donde un "error interno inesperado (contacte al Soporte Técnico" puede ser más apropiado).

static_assert expande a _Static_assert que es una palabra clave. La condición se verifica en el momento de la compilación, por lo que la condition debe ser una expresión constante. No hay necesidad de que esto se maneje de manera diferente entre el desarrollo y la producción.

Precondición y postcondicionamiento.

Un caso de uso para la afirmación es la condición previa y la condición posterior. Esto puede ser muy útil para mantener invariable y diseño por contrato . Para un ejemplo, una longitud siempre es cero o positiva, por lo que esta función debe devolver un valor cero o positivo.

#include <stdio.h>
/* Uncomment to disable `assert()` */
/* #define NDEBUG */
#include <assert.h>

int length2 (int *a, int count)
{
    int i, result = 0;

    /* Precondition: */
    /* NULL is an invalid vector */
    assert (a != NULL);
    /* Number of dimensions can not be negative.*/ 
    assert (count >= 0);

    /* Calculation */
    for (i = 0; i < count; ++i) 
    {
        result = result + (a[i] * a[i]);
    }

    /* Postcondition: */
    /* Resulting length can not be negative. */
    assert (result >= 0);
    return result;
}

#define COUNT 3

int main (void)
{
    int a[COUNT] = {1, 2, 3};
    int *b = NULL;
    int r;
    r = length2 (a, COUNT);
    printf ("r = %i\n", r);
    r = length2 (b, COUNT);
    printf ("r = %i\n", r);
    return 0;
}

Aserción simple

Una afirmación es una declaración utilizada para afirmar que un hecho debe ser verdadero cuando se alcanza esa línea de código. Las afirmaciones son útiles para asegurar que se cumplan las condiciones esperadas. Cuando la condición pasada a una aserción es verdadera, no hay acción. El comportamiento en condiciones falsas depende de las banderas del compilador. Cuando las aserciones están habilitadas, una entrada falsa provoca una detención inmediata del programa. Cuando están desactivados, no se realiza ninguna acción. Es una práctica común habilitar aserciones en compilaciones internas y de depuración, y deshabilitarlas en compilaciones de versión, aunque las aserciones a menudo se habilitan en la versión. (El hecho de que la terminación sea mejor o peor que los errores depende del programa). Las afirmaciones se deben usar solo para detectar errores de programación internos, lo que generalmente significa que se pasan parámetros incorrectos.

#include <stdio.h>
/* Uncomment to disable `assert()` */
/* #define NDEBUG */
#include <assert.h>

int main(void)
{
    int x = -1;
    assert(x >= 0);

    printf("x = %d\n", x);   
    return 0;
}

Posible salida con NDEBUG indefinido:

a.out: main.c:9: main: Assertion `x >= 0' failed.

Posible salida con NDEBUG definido:

x = -1

Es una buena práctica definir NDEBUG globalmente, para que pueda compilar fácilmente su código con todas las aserciones NDEBUG o desactivadas. Una forma fácil de hacer esto es definir NDEBUG como una opción para el compilador, o definirlo en un encabezado de configuración compartido (por ejemplo, config.h ).

Afirmación estática

C11

Las aserciones estáticas se utilizan para verificar si una condición es verdadera cuando se compila el código. Si no es así, el compilador debe emitir un mensaje de error y detener el proceso de compilación.

Una aserción estática se verifica en el momento de la compilación, no en el tiempo de ejecución. La condición debe ser una expresión constante, y si es falso, se producirá un error del compilador. El primer argumento, la condición que se comprueba, debe ser una expresión constante, y el segundo es un literal de cadena.

A diferencia de aseverar, _Static_assert es una palabra clave. Una macro de conveniencia static_assert se define en <assert.h> .

#include <assert.h>

enum {N = 5};
_Static_assert(N == 5, "N does not equal 5");
static_assert(N > 10, "N is not greater than 10");  /* compiler error */
C99

Antes de C11, no había soporte directo para afirmaciones estáticas. Sin embargo, en C99, las afirmaciones estáticas se pueden emular con macros que desencadenan un error de compilación si la condición de tiempo de compilación es falsa. A diferencia de _Static_assert , el segundo parámetro debe ser un nombre de token adecuado para que se pueda crear un nombre de variable con él. Si la afirmación falla, el nombre de la variable se ve en el error del compilador, ya que esa variable se usó en una declaración de matriz sintácticamente incorrecta.

#define STATIC_MSG(msg, l) STATIC_MSG2(msg, l)
#define STATIC_MSG2(msg,l) on_line_##l##__##msg
#define STATIC_ASSERT(x, msg) extern char STATIC_MSG(msg, __LINE__) [(x)?1:-1]
 
enum { N = 5 };
STATIC_ASSERT(N == 5, N_must_equal_5);
STATIC_ASSERT(N > 5, N_must_be_greater_than_5); /* compile error */

Antes de C99, no podía declarar variables en ubicaciones arbitrarias en un bloque, por lo que tendría que ser extremadamente cuidadoso al usar esta macro, asegurándose de que solo aparezca donde una declaración de variable sería válida.

Afirmación de código inalcanzable

Durante el desarrollo, cuando ciertas rutas de código deben evitarse del alcance del flujo de control, puede usar assert(0) para indicar que tal condición es errónea:

switch (color) {
    case COLOR_RED:
    case COLOR_GREEN:
    case COLOR_BLUE:
        break;

    default:
        assert(0);
}

Cuando el argumento de la macro assert() evalúa como falso, la macro escribirá información de diagnóstico en la secuencia de error estándar y luego abortará el programa. Esta información incluye el archivo y el número de línea de la declaración assert() y puede ser muy útil para la depuración. Las afirmaciones se pueden desactivar definiendo la macro NDEBUG .

Otra forma de terminar un programa cuando se produce un error es con las funciones de biblioteca estándar exit , quick_exit o abort . exit y quick_exit toman un argumento que puede ser devuelto a su entorno. abort() (y, por lo tanto, assert ) puede ser una terminación realmente severa de su programa, y ​​ciertas limpiezas que de otra forma se realizarían al final de la ejecución, pueden no realizarse.

La principal ventaja de assert() es que imprime automáticamente la información de depuración. Llamar a abort() tiene la ventaja de que no se puede deshabilitar como una aserción, pero puede que no muestre ninguna información de depuración. En algunas situaciones, usar ambas construcciones juntas puede ser beneficioso:

if (color == COLOR_RED || color == COLOR_GREEN) {
   ...
} else if (color == COLOR_BLUE) {
   ...
} else {
   assert(0), abort();
}

Cuando las assert() están habilitadas , la llamada a assert() imprimirá información de depuración y terminará el programa. La ejecución nunca llega a la llamada abort() . Cuando las assert() están deshabilitadas , la llamada assert() no hace nada y se llama abort() . Esto asegura que el programa siempre termina por esta condición de error; habilitar y deshabilitar las afirmaciones solo afecta si se imprime o no la salida de depuración.

Nunca se debe dejar un tal assert en el código de producción, debido a que la información de depuración no es útil para los usuarios finales y debido a abort es generalmente una terminación demasiado severa que inhiben los manipuladores de limpieza que se instalan para exit o quick_exit para funcionar.

Afirmar mensajes de error

Existe un truco que puede mostrar un mensaje de error junto con una afirmación. Normalmente, escribirías código como este

void f(void *p)
{
    assert(p != NULL);
    /* more code */
}

Si la afirmación falla, un mensaje de error sería similar

Fallo en la afirmación: p! = NULL, archivo main.c, línea 5

Sin embargo, también puede usar AND ( && ) lógico para dar un mensaje de error

void f(void *p)
{
    assert(p != NULL && "function f: p cannot be NULL");
    /* more code */
}

Ahora, si la afirmación falla, un mensaje de error leerá algo como esto

Falló la aserción: p! = NULL && "la función f: p no puede ser NULL", archivo main.c, línea 5

La razón por la que esto funciona es que un literal de cadena siempre se evalúa como distinto de cero (verdadero). Agregar && 1 a una expresión booleana no tiene efecto. Por lo tanto, agregar && "error message" tampoco tiene ningún efecto, excepto que el compilador mostrará la expresión completa que falló.



Modified text is an extract of the original Stack Overflow Documentation
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow