ProgrammatieSysteemprogrammeur in C

Leg de kenmerken uit van typeconversie tussen getallen met verschillende tekens (signed/unsigned) in C, welke problemen kunnen ontstaan, hoe kunnen onvoorziene gevolgen bij de rekenkunde en vergelijking van signed/unsigned types worden vermeden, en hoe beïnvloedt dit de portabiliteit van programma's?

Slaag voor sollicitatiegesprekken met de Hintsage AI-assistent

Antwoord

In C werkt automatische typeconversie volgens het principe van 'usual arithmetic conversions'. Wanneer getallen met verschillende tekens (signed/unsigned) bij een expressie betrokken zijn, worden de volgende regels toegepast:

  • Als een van de operandens unsigned is en de andere signed, wordt signed automatisch omgezet naar unsigned.
  • Dit kan leiden tot onverwachte overloop, vooral bij vergelijkingen of rekenkundige bewerkingen.
  • De grootte van de types heeft ook invloed: als unsigned groter is in bits, wordt signed omgezet naar unsigned.

Voorbeeld van gevaarlijke rekenkunde:

int a = -1; // signed unsigned int b = 1; printf("%d\n", a < b); // altijd false, omdat a wordt omgezet naar een zeer groot unsigned

Resultaat: -1, wanneer omgezet naar unsigned, wordt een zeer groot positief getal.

Wat belangrijk is om te onthouden:

  • Altijd expliciet de types omzetten als er verwarring kan ontstaan over het teken.
  • Let op de afmetingen van typen (int, long, uint32_t, enz.) zodat conversies voorspelbaar plaatsvinden.
  • Scheid de logica voor het werken met signed en unsigned variabelen, vooral bij grenscontroles en rekenkunde.

Vraag met een valstrik

Vraag: Wat is het resultaat van de expressie (int)(unsigned)-1?

Verwacht onjuist antwoord: "-1, want -1 en we zetten het om naar int."

Juiste antwoord:
In de expressie (unsigned)-1 vindt eerst de conversie van -1 naar unsigned plaats (op een 32-bits platform is dit 0xFFFFFFFF), vervolgens weer terug naar signed int, wat ook afhankelijk is van de implementatie, maar vaak resulteert in -1 (als two's complement wordt gebruikt). Het is echter correcter te zeggen: Het resultaat hangt af van de standaarden voor de weergave van signed getallen, maar in de meeste implementaties zal het -1 zijn.

Voorbeeld:

int x = (int)(unsigned)-1; // x == -1 op de meeste platforms

Voorbeelden van echte fouten door gebrek aan kennis over de nuances van het onderwerp


Verhaal
In de stringverwerker werd een functie voor het vergelijken van lengtes gebruikt: als de lengte van de string negatief kon zijn, gaf het programma een foutmelding. Echter, de lengte was van het type size_t (unsigned), en de vergelijking if(length < 0) gaf altijd false, wat leidde tot een oneindige lus en een geheugenoverloop.


Verhaal
Bij het parseren van het protocol bevatten netwerkkaves velden als unsigned, terwijl lokale variabelen signed waren. Door overloop van unsigned bij het verwerken van sommige waarden ontstonden er onjuiste berekeningen van de pakketlengte, wat resulteerde in een buffer-overloop kwetsbaarheid.


Verhaal
De module voor het vergelijken van datums in logs slaat de datum op als unsigned int, terwijl het een datumbereik in int zocht. Sommige grenswaarden gaven, in plaats van de verwachte uitzondering, onjuiste filtering van records en verlies van belangrijke logs.