使用位移位计算C中的有符号长最大值

使用位移位计算C中的有符号长最大值,c,bit-shift,C,Bit Shift,昨天刚开始学习C,这会让我在元旦疯狂。。。尝试使用位移位操作打印不同的整数范围。除了签名的长最大/最小值外,其他一切正常。无法理解为什么(1请记住,编程语言是一种规范,在某些技术报告中用英语编写。它不是软件。有关详细信息,请参阅 像1这样的文字常量不是long,而是int 要对文字常量long1进行编码,您需要编写1L(或者您可以编写(long)1…)。要对文字常量无符号long1进行编码,您应该编写1all(或code(无符号long)1,这是一个常量表达式 文字常量适合足够大的“最小”整数类

昨天刚开始学习C,这会让我在元旦疯狂。。。尝试使用位移位操作打印不同的整数范围。除了签名的长最大/最小值外,其他一切正常。无法理解为什么
(1请记住,编程语言是一种规范,在某些技术报告中用英语编写。它不是软件。有关详细信息,请参阅

1
这样的文字常量不是
long
,而是
int

要对文字常量
long
1进行编码,您需要编写
1L
(或者您可以编写
(long)1
…)。要对文字常量
无符号long
1进行编码,您应该编写
1all
(或code
(无符号long)1
,这是一个常量表达式

文字常量适合足够大的“最小”整数类型。因此
1
int
,在64位计算机上(实际上,是像我的Linux/x86-64那样的C实现)
1000000000
(即1010)是
长的
(因为它不适合
int
),因为在这样的计算机上,
int
-s有32位,
long
-s有64位

请注意,
int
的大小或范围不是由C99或C11标准精确定义的,并且可能因实现的不同而有所不同。您可能希望包括和使用类型,如
int32\t


因此
1表达式计算为
int
,因为两个操作数都是
int
s。您需要将它们设置为
long
s:

((1LL << 63) - 1)
(((long long)1 << 63) -1)

((1LL您的代码多次调用未定义的行为:

  • 左移位
    1
    ,一个
    int
    值,移位量大于类型中的位数减1,调用未定义的行为
  • 将有符号整数左移任意量,使结果值超出类型的范围将调用未定义的行为,就像所有其他有符号算术溢出一样
用移位计算这些最大值是不方便的。如果您可以假设2s个补码而不使用填充位,则可以使用按位补码来获得最大无符号值,并将其向右移位一次以获得最大有符号值,然后对其求反并减去一以获得最小有符号值

以下是更正后的代码:

#include <limits.h>
#include <stdio.h>

int main(void) {
    // char
    unsigned char ucmin = 0;
    unsigned char ucmax = ~ucmin;
    signed char scmax = ucmax >> 1;
    signed char scmin = -scmax - 1;
    char cmax = ((char)(-1)) < 0 ? scmax : ucmax;
    char cmin = ((char)(-1)) < 0 ? scmin : ucmin;
    printf("signed char min: %d = %d, signed char max: %d = %d\n",
           scmin, SCHAR_MIN, scmax, SCHAR_MAX);
    printf("unsigned char min: %d, unsigned char max: %u = %u\n",
           ucmin, ucmax, UCHAR_MAX);
    printf("char min: %d = %d, char max: %d = %d\n",
           cmin, CHAR_MIN, cmax, CHAR_MAX);

    // short
    unsigned short usmin = 0;
    unsigned short usmax = ~usmin;
    signed short smax = usmax >> 1;
    signed short smin = -smax - 1;
    printf("short min: %d = %d, short max: %d = %d\n",
           smin, SHRT_MIN, smax, SHRT_MAX);
    printf("unsigned short min: %d, unsigned sort max: %u = %u\n",
           usmin, usmax, USHRT_MAX);

    // int
    unsigned int umin = 0;
    unsigned int umax = ~umin;
    signed int imax = umax >> 1;
    signed int imin = -imax - 1;
    printf("int min: %d = %d, int max: %d = %d\n",
           imin, INT_MIN, imax, INT_MAX);
    printf("unsigned int min: %u, unsigned int max: %u = %u\n",
           umin, umax, UINT_MAX);

    // long int
    unsigned long ulmin = 0;
    unsigned long ulmax = ~ulmin;
    signed long lmax = ulmax >> 1;
    signed long lmin = -lmax - 1;
    printf("long int min: %ld = %ld, long int max: %ld = %ld\n",
           lmin, LONG_MIN, lmax, LONG_MAX);
    printf("unsigned long int min: %lu, unsigned long int max: %lu = %lu\n",
           ulmin, ulmax, ULONG_MAX);

    // long long int
    unsigned long long ullmin = 0;
    unsigned long long ullmax = ~ullmin;
    signed long long llmax = ullmax >> 1;
    signed long long llmin = -llmax - 1;
    printf("long long int min: %lld = %lld, long long int max: %lld = %lld\n",
           llmin, LLONG_MIN, llmax, LLONG_MAX);
    printf("unsigned long long int min: %llu, unsigned long long int max: %llu = %llu\n",
           ullmin, ullmax, ULLONG_MAX);

    return 0;
}
#包括
#包括
内部主(空){
//煤焦
无符号字符ucmin=0;
无符号字符ucmax=~ucmin;
有符号字符scmax=ucmax>>1;
有符号字符scmin=-scmax-1;
字符cmax=((字符)(-1))<0?scmax:ucmax;
char cmin=((char)(-1))<0?scmin:ucmin;
printf(“签名字符最小值:%d=%d,签名字符最大值:%d=%d\n”,
scmin、SCHAR_MIN、scmax、SCHAR_MAX);
printf(“未签名字符最小值:%d,未签名字符最大值:%u=%u\n”,
ucmin、ucmax、UCHAR_MAX);
printf(“字符最小值:%d=%d,字符最大值:%d=%d\n”,
cmin、CHAR_MIN、cmax、CHAR_MAX);
//短
无符号短usmin=0;
无符号短usmax=~usmin;
有符号短smax=usmax>>1;
符号短smin=-smax-1;
printf(“短最小值:%d=%d,短最大值:%d=%d\n”,
smin、SHRT_最小值、smax、SHRT_最大值);
printf(“无符号短最小值:%d,无符号排序最大值:%u=%u\n”,
usmin、usmax、USHRT_MAX);
//int
无符号整数umin=0;
无符号int-umax=~umin;
签名的int-imax=umax>>1;
有符号整数imin=-imax-1;
printf(“最小整数:%d=%d,最大整数:%d=%d\n”,
imin、INT_MIN、imax、INT_MAX);
printf(“未签名整数最小值:%u,未签名整数最大值:%u=%u\n”,
umin、umax、UINT_MAX);
//长整型
无符号长ulmin=0;
无符号长ulmax=~ulmin;
有符号长lmax=ulmax>>1;
有符号长lmin=-lmax-1;
printf(“长整数最小值:%ld=%ld,长整数最大值:%ld=%ld\n”,
lmin、LONG_MIN、lmax、LONG_MAX);
printf(“无符号长整数最小值:%lu,无符号长整数最大值:%lu=%lu\n”,
ulmin、ulmax、ULONG_MAX);
//长整型
无符号长ullmin=0;
无符号长ullmax=~ullmin;
有符号长llmax=ullmax>>1;
有符号长llmin=-llmax-1;
printf(“长整型最小值:%lld=%lld,长整型最大值:%lld=%lld\n”,
llmin,LLONG_MIN,llmax,LLONG_MAX);
printf(“无符号长整型最小值:%llu,无符号长整型最大值:%llu=%llu\n”,
ullmin、ullmax、ULLONG_MAX);
返回0;
}
此行:

long long lmax = (1 << 63) - 1L; // WHY DOES THIS NOT WORK???
类似地,对于您遇到问题的另一个表达式

编译器没有告诉您这个问题,这意味着您的编译没有启用警告

对于
gcc
,至少应使用:

-Wall -Wextra -pedantic  
我还发现这些参数非常有用:

-Wconversion -std=gnu99

(1LL不必检查代码,也许你应该尝试用十六进制打印。你可以使用
%x
%llx
。你应该得到
0x7FFFFFFF
long-long-lmax=(1
long-long-lmax=(1如果
int
的长度不超过64位,则
1将
int
常量强制转换为
long
不是一个好主意。这就是后缀的作用。但是OP似乎需要固定宽度的类型,所以
UINT64\u C(1)
将是正确的形式。另外,
long
在64位体系结构上不保证为64位,这是ABI的问题,请参阅Windows,即IL32LLP64。为什么
unsigned long ulmax=(1顺便说一句,谢谢关于字符可以是有符号的或无符号的部分;我知道这一点,我正在使用Microsoft C/C++编译器,它将字符视为有符号的。为什么
无符号长ulmax=(1)您的意思是当
-1LL
转换为无符号时,它会导致max
无符号长
,因为max-long-long与max-unsigned-long-long不一样。超级!这非常有用!还有一个问题,为什么
warning: integer overflow in exression [-Woverflow]
-Wall -Wextra -pedantic  
-Wconversion -std=gnu99
((1LL << 62) - 1) | (1LL << 62)