unsigned int fun1 ( unsigned int a, unsigned int b )
{
return(a+b);
}
unsigned char fun2 ( unsigned int a, unsigned int b )
{
return(a+b);
}
unsigned int fun3 ( unsigned char a, unsigned char b )
{
return(a+b);
}
unsigned char fun4 ( unsigned char a, unsigned char b )
{
return(a+b);
}
как и ожидалось, fun1 - это целые числа, так же как и 16-битная математика.
00000000 <fun1>:
0: 86 0f add r24, r22
2: 97 1f adc r25, r23
4: 08 95 ret
Хотя это технически неверно, так как это 16-битное дополнение, вызываемое кодом, даже неоптимизированный этот компилятор удалил adc из-за размера результата.
00000006 <fun2>:
6: 86 0f add r24, r22
8: 08 95 ret
не очень удивился, что здесь происходит продвижение, компиляторы раньше не делали этого, не зная, с какой версией это начало происходить, столкнулся с этим в начале моей карьеры и, несмотря на то, что компиляторы продвигались не по порядку (как выше), делал продвижение, хотя я сказал это делать учар математику, не удивился.
0000000a <fun3>:
a: 70 e0 ldi r23, 0x00 ; 0
c: 26 2f mov r18, r22
e: 37 2f mov r19, r23
10: 28 0f add r18, r24
12: 31 1d adc r19, r1
14: 82 2f mov r24, r18
16: 93 2f mov r25, r19
18: 08 95 ret
и идеал, я знаю, что это 8-битный, хочу 8-битный результат, поэтому я просто сказал, чтобы он делал 8-битный полностью.
0000001a <fun4>:
1a: 86 0f add r24, r22
1c: 08 95 ret
Так что в общем случае лучше стремиться к размеру регистра, который в идеале равен размеру (u) int, для 8-битного mcu, подобного этому, авторам компилятора приходилось идти на компромисс ... Дело в том, что привычка использование uchar для математики, которое, как вы знаете, не требует более 8 бит, так как когда вы перемещаете этот код или пишете новый код на процессоре с большими регистрами, теперь компилятор должен начать маскировать и подписывать расширение, что некоторые делают изначально в некоторых инструкциях, а другие нет.
00000000 <fun1>:
0: e0800001 add r0, r0, r1
4: e12fff1e bx lr
00000008 <fun2>:
8: e0800001 add r0, r0, r1
c: e20000ff and r0, r0, #255 ; 0xff
10: e12fff1e bx lr
форсирование 8 бит стоит дороже. Я немного обманул / много, мне нужно немного более сложные примеры, чтобы увидеть больше этого на справедливой основе.
РЕДАКТИРОВАТЬ на основе обсуждения комментариев
unsigned int fun ( unsigned char a, unsigned char b )
{
unsigned int c;
c = (a<<8)|b;
return(c);
}
00000000 <fun>:
0: 70 e0 ldi r23, 0x00 ; 0
2: 26 2f mov r18, r22
4: 37 2f mov r19, r23
6: 38 2b or r19, r24
8: 82 2f mov r24, r18
a: 93 2f mov r25, r19
c: 08 95 ret
00000000 <fun>:
0: e1810400 orr r0, r1, r0, lsl #8
4: e12fff1e bx lr
не удивительно. Хотя почему оптимизатор оставил эту дополнительную инструкцию, нельзя ли использовать ldi на r19? (Я знал ответ, когда спросил).
EDIT2
для авр
avr-gcc --version
avr-gcc (GCC) 4.9.2
Copyright (C) 2014 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
чтобы избежать вредной привычки или нет 8-битное сравнение
arm-none-eabi-gcc --version
arm-none-eabi-gcc (GCC) 7.2.0
Copyright (C) 2017 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
очевидно, что оптимизация была включена всего за секунду, чтобы попробовать с вашим собственным компилятором, чтобы увидеть, как он сравнивается с моим выводом, но в любом случае:
whatever-gcc -O2 -c so.c -o so.o
whatever-objdump -D so.o
И да, использование байтов для переменных байтового размера, безусловно, на avr, pic и т. Д., Сэкономит вашу память, и вы действительно захотите сохранить ее ... если вы на самом деле ее используете, но, как показано здесь, как можно меньше будет в памяти, как можно больше в регистрах, поэтому экономия флэш-памяти достигается за счет отсутствия дополнительных переменных, а оперативная экономия может быть или не быть реальной ..