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);
}
como esperado fun1 é tudo ints o mesmo acontece com a matemática de 16 bits
00000000 <fun1>:
0: 86 0f add r24, r22
2: 97 1f adc r25, r23
4: 08 95 ret
Embora tecnicamente incorreto, pois é uma adição de 16 bits chamada pelo código, mesmo não otimizado, esse compilador removeu o adc devido ao tamanho do resultado.
00000006 <fun2>:
6: 86 0f add r24, r22
8: 08 95 ret
Não é surpresa que aqui a promoção aconteça, os compiladores não costumavam fazer isso, não tinham certeza de qual versão fazia com que isso acontecesse, entraram nessa no início da minha carreira e, apesar dos compiladores promoverem fora de ordem (como acima), fazendo a promoção mesmo que eu disse para fazer matemática uchar, não surpreso.
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
e o ideal, eu sei que é 8 bits, quero um resultado de 8 bits, então eu simplesmente disse para ele fazer 8 bits por todo o caminho.
0000001a <fun4>:
1a: 86 0f add r24, r22
1c: 08 95 ret
Portanto, em geral, é melhor apontar para o tamanho do registro, que é idealmente o tamanho de um (u) int, para um mcu de 8 bits como este, os autores do compilador tiveram que fazer um compromisso ... Ponto de não criar o hábito de usando uchar para matemática que você sabe que não precisa de mais de 8 bits, como quando você move esse código ou escreve um novo código em um processador com registros maiores agora o compilador precisa começar a mascarar e a estender o sinal, o que alguns são nativos em algumas instruções, e outros não.
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
forçar 8 bits custa mais. Eu trapacei um pouco / muito, precisaria de exemplos um pouco mais complicados para ver mais disso de uma maneira justa.
EDIT com base na discussão dos comentários
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
sem surpresa. Embora por que o otimizador tenha deixado essa instrução extra, você não pode usar o ldi na r19? (Eu sabia a resposta quando perguntei).
EDIT2
para avr
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.
para evitar o mau hábito ou não comparação de 8 bits
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.
claramente a otimização estava ligada leva apenas um segundo para tentar com seu próprio compilador para ver como ele se compara à minha saída, mas de qualquer maneira:
whatever-gcc -O2 -c so.c -o so.o
whatever-objdump -D so.o
E sim, o uso de bytes para variáveis de tamanho de bytes, certamente em um avr, pic, etc, economizará memória e você deseja realmente tentar conservá-lo ... se você estiver realmente usando, mas, como mostrado aqui, o mínimo possível é ficará na memória, tanto quanto possível nos registros, de modo que a economia de flash ocorre por não ter variáveis extras, a economia de memória ram pode ou não ser real.