C 编译器无法识别 unsigned long

C compiler not recognizing unsigned long

在我的电脑上,long 取最大值 9223372036854775807。但是,当我用更大的值厌倦 unsigned long 时,编译器会发出警告,说它需要被解释为 unsigned long,而我已经将它定义为 unsigned long。为什么会这样?

//assigning maximum value of a long integer. (No error)
long max_l = 9223372036854775807L;

//assigning an unsigned long integer.
unsigned long max_ul = 9223372036854775808L; //warning: integer literal is too large to be represented in a signed
                                             //integer type, interpreting as unsigned [-Wimplicitly-unsigned-literal]

cc --version
Apple LLVM version 10.0.1 (clang-1001.0.46.4)

这个整数常量:

9223372036854775808L

太大,无法存储在 long 中。

改为使用:

9223372036854775808UL

这通过附加后缀 UL

来指定常量具有类型 unsigned long

或者直接使用后缀U:

unsigned long max_ul = 9223372036854775808U;

当整数常量具有后缀 L(或 l)时,编译器将按以下顺序确定其类型:第一个类型

signed long
signed long long

其中可以体现它的价值。似乎 signed long 类型与编译器建立的 signed long long 类型具有相同的整数表示形式。所以类型 signed long 和类型 signed long long 都不能表示常量。对于这些类型,常量太大。但是与编译器建立的类型unsigned long long具有相同内部表示的类型unsigned long可以表示常量

还要注意C中没有负整数常量。 例如,如果您将写

int x = -1;

然后编译器将构造 -1 拆分为两个标记:整数常量 1 和一元运算符 -.

考虑以下演示程序

#include <stdio.h>

int main(void) 
{
    int a[] = { 0, 1, 2 };
    int *p = a + 1;

    printf( "p[-1] = %d\n", p[-1] );
    printf( "-1[p] = %d\n", -1[p] );

    return 0;
}

程序输出为

p[-1] = 0
-1[p] = -2

表达式 -1[p] 与表达式 (-1)[p] 不同。它被处理为 -(1[p] ) 相当于 -p[1].

每个整型常量(例如 9223372036854775808)都有自己的类型。对于编译器在程序中为整数常量选择什么类型有一些规则。对于普通十进制常量(没有任何 U、L 后缀),它是这样的:

  • 试试它是否适合 int
  • 如果不是,请尝试它是否适合 long
  • 如果不是,请尝试它是否适合 long long

(详见C17 6.4.4.1中的table)

显然,对于您系统上的值 2^63,所有这些检查都失败了,这是预料之中的,因为带符号的 64 位只能达到 2^63 - 1。

添加 L 并不能解决任何问题,因为这只会告诉编译器进行上述检查但从 long.

开始

将整数常量的存储类型声明为无符号并不能解决任何问题,因为 assignment/initialization 左侧的类型与整数常量的类型无关。你可以写 my_custom_type x = 9223372036854775808L; 并且你会得到同样的警告。

显而易见的解决方案是使用无符号后缀 U。否则,如果您出于某种原因需要 >63 位签名的值,则需要找到一些 128 位签名的 "big int" 库,这会更加痛苦。