我知道这个问题已经被问过并且似乎回答了无数次,但我似乎无法将答案与我自己的经历相匹配。
C 标准规定,对于加法,“两个操作数都应具有算术类型”(6.5.6.1)。算术类型包括整数和浮点类型(6.2.5.18),最后整数类型是 char、short、int、long 和 long long,它们以有符号和无符号类型存在(6.2.5.4 和 6.2.5.6)。根据通常的算术转换规则“如果两个操作数具有相同的类型,则不需要进一步转换。” 到目前为止,一切都很好。
我的理解是,正如“The C Book”中所举例说明的那样,“[n]o 算术是由 C 以比 int 更短的精度完成的”,这是应用积分提升的地方。我在标准中找不到对此的参考,因为我似乎已经看过很多次了。
由于 unsigned char 是一种算术类型,并且通常算术转换的规则规定相同类型的操作数不需要转换,为什么需要整数提升?
我使用两种不同的编译器对此进行了测试。我写了一个简单的程序来添加字符:
unsigned char a = 1;
unsigned char b = 2;
unsigned char c = a + b;
目标平台是使用 8 位架构的 Atmel Mega8 uC。因此,如果操作数应进行整数提升,则整数加法将需要使用两个寄存器。
使用没有优化的 imagecraft avr 编译器编译它并启用严格和 ANSI C 可移植性选项会产生以下汇编代码:
mov R16, R20
add R16, R18
使用 avr-gcc(我不知道类似于 gcc 的 -strict 的 ANSI 开关):
$ avr-gcc -O0 -mmcu=atmega8 -S -c main.c
生成的程序集:
ldd r25,Y+1
ldd r24,Y+2
add r24,r25
std Y+3,r24
两种情况下的结果代码都在单个字节上运行。对于按位 | 我得到了类似的结果 和 & 和逻辑 || 和 &&。这是否意味着该标准允许在没有整体提升的情况下对字符类型进行算术运算,还是仅仅意味着这些编译器不符合标准?
额外的:
原来这一切都取决于存储结果的类型。上面显示的示例仅在结果存储在 char 时才成立,它不依赖于加法的结果。将 a 设置为 0xFF 并将 b 设置为 1 会产生完全相同的汇编代码。
如果 c 的类型更改为 unsigned int,则生成的程序集如下所示:
mov R2,R20
clr R3
mov R16,R18
clr R17
add R16,R2
adc R17,R3
即使在结果可以保存在单个字节中的情况下,即a=1 和b=2。