当 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 个字节。
我们取 unsigned
和 long 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^32
但 LONG_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
,因此您看到的结果是。
让平台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 oflong int
, which shall be greater than the rank ofint
, which shall be greater that the rank ofshort int
, which shall be greater than the rank ofsigned char
.
这表明 long int
的 运行k 高于 int
。
另外,C99标准同段第4点说:
The rank of any
unsigned
integer type shall equal the rank of the correspondingsigned
integer type, if any.
从这里可以看出,unsigned int
类型与 signed int
类型具有相同的 运行k。同样,long unsigned int
类型与 long signed int
.
最后,在第 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 个字节。
我们取 unsigned
和 long 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^32
但 LONG_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
,因此您看到的结果是。