当 int 和 long 在 C 中具有相同的宽度时,排名有时会失败

Ranking sometimes fails when int and long have the same width in C

让平台1的宽度为int 4个字节,宽度为long 8个字节。

让平台2的宽度为int 4个字节,long的宽度与int的宽度相同。

然后给出:

unsigned int x = 2;
long signed int y = 3;
func(x * y);

在平台1上运行时,func第一个参数的有效类型为long signed int。这符合预期。根据 § 6.3.1.1.1.4,unsigned int 类型与 signed int 具有相同的 运行k。根据 § 6.3.1.1.1.3,signed int 类型的 运行k 低于 long signed int。然后,这会触发乘法结果转换为 long signed int 类型,遵循 § 6.3.1.8.1.4.4。太棒了!

在平台2上运行时,乘法结果为long unsigned int为什么?


背景

C99 标准第 6.3.1.1 节第 1 小节第 3 点说:

The rank for long long int shall be greater than the rank of long int, which shall be greater than the rank of int, which shall be greater that the rank of short int, which shall be greater than the rank of signed char.

这表明 long int 的 运行k 高于 int

另外,C99标准同段第4点说:

The rank of any unsigned integer type shall equal the rank of the corresponding signed integer type, if any.

从这里可以看出,unsigned int 类型与 signed int 类型具有相同的 运行k。同样,long unsigned int 类型与 long signed int.

具有相同的 运行k

最后,在第 6.3.1.8 节第 1 小节第 4.3 点说:

Otherwise, if the operand that has unsigned integer type has rank greater or equal to the rank of the type of the other operand, then the operand with signed integer type is converted to the type of the operand with unsigned integer type.

和第 4.4 点:

Otherwise, if the type of the operand with signed integer type can represent all values of the type of the operand with unsigned integer type, then the operand with unsigned integer type is converted to the type of the operand with signed integer type.


测试码

#include <stdio.h>

#define func(x) _Generic((x), long unsigned int: func_longunsignedint, long signed int: func_longsignedint, signed int: func_signedint, unsigned int: func_unsignedint)(x);

void func_longunsignedint (long unsigned int x)
{
    printf("%s\t%lu\n", __func__, x);
}

void func_longsignedint (long signed int x)
{
    printf("%s\t%ld\n", __func__, x);
}

void func_signedint (signed int x)
{
    printf("%s\t%d\n", __func__, x);
}

void func_unsignedint (unsigned int x)
{
    printf("%s\t%u\n", __func__, x);
}

int main(void)
{
    printf("int width %d\n", sizeof(int));
    printf("long width %d\n", sizeof(long));
    unsigned int x = 2;
    long signed int y = -3;
    func(x * y);
}

对于平台 1,使用 gcc -m64 进行编译,希望将 long 强制为 8 个字节,将 int 强制为 4 个字节。

对于平台 2,使用 gcc -m32 进行编译,希望将 long 强制为 4 个字节,将 int 强制为 4 个字节。

我们取 unsignedlong signed int 并遵循 C11 6.3.1.8:

  • Otherwise, if the type of the operand with signed integer type can represent all of the values of the type of the operand with unsigned integer type, then the operand with unsigned integer type is converted to the type of the operand with signed integer type.

在 64 位平台 #1 上,类型 long signed int 可以表示 unsigned 的所有值,因为它有 64 位,而 unsigned 有 32 位。所以 unsigned 被转换为 long signed int.

在 32 位平台 #2 上,类型 long signed int 不能表示 unsigned 的所有值(UINT_MAX=2^32LONG_MAX=2^31-1)。所以我们继续...

  • Otherwise, both operands are converted to the unsigned integer type corresponding to the type of the operand with signed integer type.

...所以在平台 #2 上,“与有符号整数类型对应的无符号整数类型”- 这是 long signed int + unsigned = long unsigned int。所以在平台 #2 上,两个操作数都转换为 long unsigned int,因此您看到的结果是。