char和通常的算术转换规则

标签 c types type-conversion

我知道这个问题已经被问过无数次,而且似乎已经回答了无数次,但我似乎无法将答案与我自己的经历相匹配。

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。

最佳答案

C 2011 (n1570) 6.3.1.8(“通常的算术转换”)1 声明整数提升在考虑类型是否相同之前执行::

Otherwise, the integer promotions are performed on both operands. Then the following rules are applied to the promoted operands:

If both operands have the same type, then no further conversion is needed…

因此,在 C 抽象机中,unsigned char 值必须在执行算术运算之前提升为 int。 (对于 unsigned charint 具有相同大小的反常机器有一个异常(exception)。在这种情况下,unsigned char 值被提升为 unsigned int 而不是 int。这是深奥的,在正常情况下不需要考虑。)

在实际机器中,操作的执行方式必须如同在抽象机器中执行操作时获得相同的结果。因为只有结果才重要,实际的中间操作不需要与抽象机完全匹配。

当将两个 unsigned char 值的总和分配给 unsigned char 对象时,总和将转换为 unsigned char。这种转换实质上会丢弃超出适合 unsigned char 的位的位。

这意味着 C 实现无论是否这样做都会得到相同的结果:

  • 将值转换为 int
  • 使用 int 算法添加值。
  • 将结果转换为unsigned char

或者这个:

  • 使用 unsigned char 算法添加值。

因为结果相同,C 实现可以使用任何一种方法。

为了比较,我们可以考虑这个语句:int c = a + b;。此外,假设编译器不知道 ab 的值。在这种情况下,使用 unsigned char 算术进行加法运算可能会产生与将值转换为 int 并使用 int 算术不同的结果。例如,如果 a 是 250 而 b 是 200,那么它们作为 unsigned char 值的总和是 194 (250 + 200 % 256),但是它们在 int 算术中的总和是 450。由于存在差异,C 实现必须使用获得正确总和 450 的指令。

(如果编译器确实知道 ab 的值,或者可以证明总和适合 unsigned char,那么编译器可以再次使用 unsigned char 算法。)

关于char和通常的算术转换规则,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12841278/

相关文章:

c - 链接 pthread 库问题

c - 信号量无法正常工作

java - 将 C 指针转换为 Java

haskell - 我试图破解 Haskell,并从 GHC 收到 "Inaccessiable code"错误。这是什么意思?

c++ - lambda 函数的类型,使用 auto

rust - 如何用重复的 u16 值填充 [u8] 数组?

c - 如何将 uint8_t 显示为整数或字符串?

使用函数时无法获取小数?

database - 来自 db 的 codeigniter 值类型

c++ - 将 const char* 转换为 vector<double> 更好的方法