Давайте посмотрим, что будет делать «тупой» компилятор, если ему дан следующий код:
#include <stdio.h>
#include <stdint.h>
int main(int argc, char **argv) {
uint16_t test1 = 0x1122;
uint32_t test2 = 0x11223344;
if (test1 & 0x0100)
printf("yay1.\n");
if (test2 & 0x00010000)
printf("yay2.\n");
}
Это похоже на вероятный вариант использования значений типа mode_t
, проверяющих, установлен ли флаг. Теперь мы скомпилируем его с gcc -O0
и проверим сгенерированную сборку:
0000000000000000 <main>:
...
f: 66 c7 45 fe 22 11 movw $0x1122,-0x2(%rbp)
15: c7 45 f8 44 33 22 11 movl $0x11223344,-0x8(%rbp)
1c: 0f b7 45 fe movzwl -0x2(%rbp),%eax ; load test1 into %eax
20: 25 00 01 00 00 and $0x100,%eax
25: 85 c0 test %eax,%eax
...
33: 8b 45 f8 mov -0x8(%rbp),%eax ; load test2 into %eax
36: 25 00 00 01 00 and $0x10000,%eax
3b: 85 c0 test %eax,%eax
...
Посмотрите, как специальная инструкция movzwl
необходима для загрузки 16-битного значения? Это связано с тем, что он должен быть расширен до двух дополнительных байтов для размещения в регистре. Очевидно, что эта инструкция более сложна, чем простая mov
. Это может незначительно повлиять на производительность и увеличить размер исполняемого файла на несколько байтов, что само по себе не так уж и плохо.
Однако, если учесть, что использование 16-битного значения не дает никаких преимуществ, поскольку в любом случае из-за выравнивания оно обычно занимает 32 бита, в любом случае понятно, почему разработчики решили использовать собственный размер слова: процессор здесь.