Alweer geen Excel

Undefined behavior

Arnout van Kempen schrijft in deze rubriek over pret maken met computers. Hij gooit het na een reeks over Pascal over een andere boeg en gaat aan de slag met C.

We eindigden vorige keer met een gevaarlijk programma.

#include <stdio.h> 

int main() {
   int teller = 1;
   printf(“%d+1=%d\n”,teller++, teller);
   return 0;
}

Waarom gevaarlijk? Je ziet hier twee typische C-verschijnselen. Laten we met de makkelijke, veilige, normale beginnen. Als je een variabele hebt van een type als int, dan kan je de waarde met 1 verhogen of verlagen, door er ++ of - - voor of achter te zetten. De variabele kan in een expressie staan. Zet je de ++ er achter dan wordt de expressie eerst geëvalueerd en daarna wordt de variabele met 1 verhoogd. Zet je ++ er voor, dan krijg je eerst de verhoging met 1 en daarna de evaluatie. Je ziet dit verschil als je deze code uitprobeert:

#include <stdio.h>

int main() {
   int teller = 1;
   printf(“%d\n”,teller++);
   printf(“%d\n”,++teller);
   return 0;
}

De uitkomst is:
1
3

Immers, eerst werd teller (1) afgedrukt, daarna verhoogd. En bij de volgende printf eerst verhoogd (dus van 2 naar 3) en daarna afgedrukt.

Je had ook de teller kunnen verhogen met
teller = teller + 1;

Maar dat betekent een extra regel code. De ++ werkt gewoon compacter. In C zal je deze constructie veel zien.
Maar wat was er nu zo gevaarlijk aan de code van vorige keer? Dat zit in een verschijnsel dat typisch C is: undefined behavior. C accepteert code waarvan de C-standaard niet expliciet maakt welk gedrag de code heeft. En dat betekent dat je als programmeur nooit zeker kan weten wat je programma gaat doen.

printf(“%d+1=%d\n”,teller++, teller);

lijkt wel voor de hand te liggen en zowel de clang-compiler als de gcc-compiler verwerken de code ook meestal zoals je misschien verwacht. Maar de C-standaard legt niet vast in welke volgorde de laatste twee argumenten van de printf functie moeten worden geëvalueerd. Je verwacht wellicht dat je eerst teller in de eerste %d krijgt, vervolgens de ++ wordt verwerkt en je dan de volgende %d gevuld ziet worden met de nieuwe waarde van teller.
Kortom, eerst een 1, dan wordt deze verhoogd naar 2 en die wordt vervolgens afgedrukt. Maar de compiler had net zo goed mogen werken in omgekeerde volgorde. Eerst de 1 in de laatste %d, dan in de eerste %d en dan de ++. En die uitkomst zou voor ons idee fout zijn, maar omdat het undefined behavior is, is het begrip goed of fout betekenisloos geworden.

En let op, het is nog erger, de compiler mag eigenlijk alles doen. Dus in plaats van een 1 of een 2, een andere volgorde, is een uitkomst als 2365 ook mogelijk. Undefined behavior (UB) is echt een bizar vervelend verschijnsel. De compiler staat het wel toe, maar wat er uitkomt kan letterlijk alle kanten op. Gelukkig is de compiler meestal wel zo vriendelijk een warning af te geven. Maar een warning verhindert de compilatie niet.

Arnout van Kempen is naast computernerd ook directeur compliance & risk bij aaff. Hij schrijft op persoonlijke titel.

Gerelateerd

reacties

Reageer op dit artikel

Spelregels debat

    Aanmelden nieuwsbrief

    Ontvang elke werkdag (maandag t/m vrijdag) de laatste nieuwsberichten, opinies en artikelen in uw mailbox.

    Bent u NBA-lid? Dan kunt u zich ook aanmelden via uw ledenprofiel op MijnNBA.nl.