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 біт коштує дорожче. Я обдурив трохи / багато, знадобиться трохи складніші приклади, щоб побачити більше цього на справедливому шляху.
EDIT на основі обговорення коментарів
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 і т. Д., Заощадить вам пам’ять, і ви хочете по-справжньому спробувати зберегти його ... якщо ви насправді використовуєте його, але, як показано тут, як можна менше буде в пам’яті, наскільки це можливо в регістрах, так що економія спалаху виникає не маючи зайвих змінних, економія оперативної пам'яті може бути, а може і не бути реальною ..