7

给定以下代码段:

#include <stdio.h>

typedef signed long long int64;
typedef signed int int32;
typedef signed char int8;

int main()
{
    printf("%i\n", sizeof(int8));
    printf("%i\n", sizeof(int32));
    printf("%i\n", sizeof(int64));

    int8 a = 100;
    int8 b = 100;
    int32 c = a * b;
    printf("%i\n", c);

    int32 d = 1000000000;
    int32 e = 1000000000;
    int64 f = d * e;
    printf("%I64d\n", f);
}

MinGW GCC 3.4.5 的输出是 (-O0):

1
4
8
10000
-1486618624

第一个乘法在内部转换为 int32(根据汇编器输出)。第二个乘法没有强制转换。我不确定结果是否不同是因为程序在 IA32 上运行,还是因为它是在 C 标准中的某个地方定义的。不过,如果在某处定义了这种确切的行为(ISO/IEC 9899?),我很感兴趣,因为我想更好地理解为什么以及何时必须手动转换(我在从不同架构移植程序时遇到问题)。

4

4 回答 4

7

C99 标准确实指定了二元运算符,例如,*不要对小于 的整数类型进行操作int。这些类型的表达式在应用运算符之前被提升int。参见 6.3.1.4 第 2 段和多次出现的“整数提升”一词。但这与编译器生成的汇编指令有些正交,后者对ints 进行操作,因为即使允许编译器计算较短的结果,这也更快(因为结果立即存储在 short 类型的左值中, 例如)。

关于int64 f = d * e;wheredeare 的类型int,乘法是int按照相同的提升规则进行的。溢出在技术上是未定义的行为,你在这里得到了二进制补码的结果,但你可以根据标准得到任何东西。

注意:提升规则在提升时区分有符号和无符号类型。规则是将较小的类型提升为int除非int不能表示该类型的所有值,在这种情况下unsigned int使用。

于 2010-08-16T18:39:53.307 回答
5

问题是乘法是int32 * int32,它是作为int32完成的,然后将结果分配给一个int64。你会得到几乎相同的效果double d = 3 / 2;,使用整数除法将 3 除以 2,并将 1.0 分配给d.

无论何时,您都必须注意表达式或子表达式的类型。这需要确保将适当的操作计算为适当的类型,例如将被乘数之一转换为 int64,或(在我的示例中)3.0 / 2(float)3 / 2.

于 2010-08-16T18:34:43.103 回答
3

阅读 K&R(原版)。所有整数运算都使用自然整数类型完成,除非它涉及(或强制转换)更大的变量。char 上的操作被转换为 32 位,因为这是该架构上整数的自然大小。两个 32 位整数的乘法是在 32 位中完成的,因为没有任何东西将其转换为更大的值(直到您将其分配给 64 位变量,但这为时已晚)。如果您希望操作以 64 位进行,请将一个或两个整数转换为 64 位。

int64 f = (int64)d * e;
于 2010-08-16T18:39:43.623 回答
2

a * b计算为int,然后转换为接收变量类型(恰好是int)

d * e计算为int,然后转换为接收变量类型(恰好是int64)

如果任一类型变量大于 int(或浮点),则将使用该类型。但由于乘法中使用的所有类型都是 int 或更小,所以使用了 int。

于 2010-08-16T18:39:37.277 回答