17 Fehlersuche mit Standard-C

Im Sprachumfang von Standard-C ist eine portable, einfache und sehr praktische Hilfe zur Fehlersuche vorgesehen: ein Makro namens assert(). assert() ist in assert.h definiert. Es wird nur expandiert, wenn NDEBUG nicht definiert ist. Mit assert() kann man Bedingungen überprüfen, die zur Laufzeit logisch wahr sein sollen und ansonsten auf Programmfehler hinweisen (sogenannte Invarianten). Falls die angegebene Bedingung entgegen aller Erwartung nicht erfüllt ist, wird das Programm abgebrochen. Nach Entfernung der Programmfehler setzt man #define NDEBUG ein und hat damit alle Überprüfungen ausgeschaltet; die assert()-Aufrufe brauchen nicht entfernt zu werden. Beispiel: Das Programm assdemof.c
/* assdemof.c 30. 7.95 kw
 */

#include <assert.h>
#include <stdio.h>

#define IMAX            10

int feld[IMAX];

/* zeige( index ) zeigt den Wert von feld[index]:
 */
void zeige( int index )
{
  assert( index<IMAX );  /* vergewissern ob index innerhalb */
  assert( index>=0   );  /* der Feldgrenzen liegt           */
  printf( "feld[%d] = %d\n", index, feld[index] );
}

main()
{
  int i;

  for( i=0; i<IMAX; i++ )
  {
    feld[i] = 2*i;
  }

  zeige( -2 );
  zeige( 5 );
  zeige( 10 );
}
enthält zwei Fehler. Sowohl mit zeige( -2 ) als auch mit zeige( 10 ) wird versucht, außerhalb der zulässigen Feldgrenzen auf feld[] zuzugreifen. Stattdessen wollte der Programmierer eigentlich den 2., den 5. und den letzten (also den 9.) Wert des Feldes wissen. Das Programm erzeugt in dieser Form nach dem Übersetzen, Linken und Starten die Ausgabe
assdemof: assdemof.c:16: zeige: Assertion `index>=0' failed.
und bleibt stehen, weil in Zeile 16 die Bedingung index>=0 verletzt wurde31. Angenommen, man kommt durch diesen Hinweis auf den Tippfehler zeige( -2 ) und korrigiert die Zeile zu zeige( 2 ), dann erhält man -nach erneutem Kompilieren- die Ausgabe
feld[2] = 4
feld[5] = 10
assdem2f: assdem2f.c:15: zeige: Assertion `index<10' failed.
und stellt vielleicht fest, daß das letzte Feldelement nicht mit zeige( 10 ), sondern vielmehr mit zeige( 9 ) oder besser mit zeige( IMAX-1 ) auszugeben ist. Nach der Korrektur hat man das Programm
/* assdemok.c 30. 7.95 kw
 */

#include <assert.h>
#include <stdio.h>

#define IMAX            10

int feld[IMAX];

/* zeige( index ) zeigt den Wert von feld[index]:
 */
void zeige( int index )
{
  assert( index<IMAX );  /* vergewissern ob index innerhalb */
  assert( index>=0   );  /* der Feldgrenzen liegt           */
  printf( "feld[%d] = %d\n", index, feld[index] );
}

main()
{
  int i;

  for( i=0; i<IMAX; i++ )
  {
    feld[i] = 2*i;
  }

  zeige( 2 );
  zeige( 5 );
  zeige( IMAX-1 );
}
und erhält die Ausgabe:
feld[2] = 4
feld[5] = 10
feld[9] = 18
Damit kann das Programm als fehlerfrei angesehen werden und durch Einfügen der ersten Zeile:
#define NDEBUG
#include <assert.h>
#include <stdio.h>
...
werden alle assert()-Aufrufe ungültig und stören nicht weiter. Insbesondere erzeugen sie nicht mehr Programmcode, als wenn sie gar nicht vorhanden wären. Bei später auftauchenden Fehlern kann man alle assert()-Aufrufe wieder aktivieren, indem man die Zeile #define NDEBUG entfernt (oder auskommentiert) und den Quelltext neu kompiliert.
AnyWare@Wachtler.de