The hazards of C

What does the following C program print:

{% c-block language="c" %}
#include <stdio.h>
#include <stdint.h>

int main(void) {
   uint16_t x = 45000;
   uint16_t y = 50000;
   uint32_t z = x * y;  // 45,000 * 50,000 is 2,250,000,000
   printf("%" PRIu32 "\n", z);
   return 0;
{% c-block-end %}

({% c-line %}PRIu32{% c-line-end %} is just the platform-specific {% c-line %}printf{% c-line-end %} format string argument for an unsigned 32-bit int, there's no trickery or magic there)

Go ahead and compile it with {% c-line %}gcc -Wall -Wextra{% c-line-end %} and then run it. No warnings, and I get {% c-line %}2250000000{% c-line-end %}, which matches the mathematical definition of {% c-line %}40,000 * 50,000{% c-line-end %} - no big deal, right? Wrong! You may have just invoked undefined behavior.

If you did, the compiler is legally allowed to make demons fly out of your nose.

But how? All we did was multiply 2 unsigned integers into an unsigned integer which is wide enough to hold the result. The problem is C's integer promotion rules.

If you are on a platform where {% c-line %}int{% c-line-end %} is 4 bytes, then an {% c-line %}int{% c-line-end %} can represent the entire range of values of a {% c-line %}uint16_t{% c-line-end %}, so {% c-line %}x{% c-line-end %} and {% c-line %}y{% c-line-end %} are both promoted to {% c-line %}int{% c-line-end %} before multiplying. Multiplying the two promoted {% c-line %}int{% c-line-end %} values results in a value of {% c-line %}2,250,000,000{% c-line-end %}, which is larger than the largest {% c-line %}int{% c-line-end %} value of {% c-line %}2,147,483,647{% c-line-end %}. Signed integer overflow! That's undefined behavior. Luckily my compiler chose to use wrapping arithmetic, and not emit demons, which means I get the right result and my nose remains free of demons. However, it's not required to do so. Try recompiling with the {% c-line %}-ftrapv{% c-line-end %} flag:

{% c-block language="console" %}
➜  ~ gcc -ftrapv test.c
➜  ~ ./a.out
[1]    97121 illegal hardware instruction  ./a.out
{% c-block-end %}

Want to stay up to date on the future of firmware? Join our mailing list.