我正在努力确定每个变量的边缘大小。我无法理解以下问题。
为了获得char的最大值,我使用:~ 0 >> 1
哪个应该这样工作:
现在我想使用printf
函数显示此结果。
为什么我必须像这样使用强制转换:
printf("%d\n", (unsigned char)(~0) >> 1)
?
我只是不明白。当我离开char范围时,我认为它与第2点有关,但我不确定。
如果你向我提出更复杂的解释,我将不胜感激。
答案 0 :(得分:5)
请不要使用这些技巧。它们可能在普通机器上工作,但它们可能不可移植且难以理解。而是使用头文件limits.h
中的符号常量,其中包含每种基本类型的大小限制。例如,CHAR_MAX
是char
的上限,CHAR_MIN
是下限。可以在stddef.h
中找到stdint.h
和stdint.h
中声明的数字类型的进一步限制。
现在提出您的问题:默认情况下,对int
类型的值进行算术运算,除非您使所涉及的操作数具有不同的类型。这种情况由于各种原因而发生,例如涉及具有不同类型的变量之一或您使用不同类型的迭代(例如1.0
或1L
或1U
)。更重要的是,算术表达式的类型从内到外都有所提升。因此,在声明中
char c = 1 + 2 + 3;
表达式1 + 2 + 3
被评估为类型int
,并且仅在分配之前立即转换为char
。更重要的是,在C语言中,您不能对小于int
的类型进行算术运算。例如,在表达式c + 1
中,c
的类型为char
,编译器会将{em>隐式转换从char
插入int
在向c
添加一个之前。因此,像
c = c + 1;
实际上在C中表现得像这样:
c = (char)((int)c + 1);
因此,~0 >> 1
实际上在通常的32位架构上求值为0xffffffff
( - 1),因为类型int
通常具有32位,并且有符号类型的右移通常会移位符号位所以最重要的一位变成了一位。转换为unsigned char
导致截断,结果为0xff
(255)。除printf
之外的所有参数都是变量参数列表的一部分,这有点复杂,但基本上意味着所有类型小于而不是int
转换为int
,float
转换为double
,所有其他类型保持不变。
现在,我们怎样才能做到这一点?在具有二进制补码和无填充位的普通机器上,可以使用这些表达式来计算最大和最小char
,假设为sizeof (char) < sizeof (int)
:
(1 << CHAR_BIT - 1) - 1; /* largest char */
-(1 << CHAR_BIT - 1); /* smallest char */
对于其他类型,由于我们需要避免溢出,因此会稍微困难一些。这是一个适用于普通机器上所有有符号整数类型的表达式,其中type
是您希望具有以下限制的类型:
(type)(((uintmax_t)1 << sizeof (type) * CHAR_BIT - 1) - 1) /* largest */
(type)-((uintmax_t)1 << sizeof (type) * CHAR_BIT - 1) /* smallest */
对于无符号类型type
,您可以使用它来获得最大值:
~(type)0
请注意,所有这些技巧都应 出现在便携式代码中。
答案 1 :(得分:2)
您的行为的确切效果与您的假设不同。
0
不是0000 0000
。 0
类型为int
,这意味着它很可能是0000 0000 0000 0000 0000 0000 0000 0000
,具体取决于您的平台上有多少位int
。 (我将假设32位int
。)
现在,~0
,1111 1111 1111 1111 1111 1111 1111 1111
,int
,其类型0
仍然是负值。
当您将其向右移动时,结果是实现定义的。 C中的右移负有符号整数值并不能保证您在符号位中获得~0 >> 1
。恰恰相反,大多数平台实际上会在右移时复制符号位。这意味着1111 1111 1111 1111 1111 1111 1111 1111
仍然会为您提供0
。
请注意,即使您在平移负值时将0111 1111 1111 1111 1111 1111 1111 1111
移入符号位的平台上执行此操作,您仍将获得char
,这在一般情况下不是您试图获得的0
的最大值。
如果要确保右移操作从左侧移入1
位,则必须1)移位无符号位模式或2)移动签名但正面位模式。对于负位模式,您可能会遇到符号扩展行为,这意味着对于负值0
,位将从左侧而不是[unsigned/signed] char
位移入。
由于C语言没有在int
类型的域中起作用的转换(在转换之前操作数被提升为int
),你可以做的就是确保你是移动正1
值并确保初始位掩码中包含正确的(unsigned char) ~0
个数。这正是您使用(unsigned char) ~0
作为初始掩码所实现的目标。 int
将0000 0000 0000 0000 0000 0000 1111 1111
类型的值等于char
(假设为8位0000 0000 0000 0000 0000 0000 0111 1111
),参与此次转变。在班次之后,您将获得Animal
,这正是您想要获得的。
答案 2 :(得分:1)
仅适用于无符号整数。对于有符号整数,右移一个负数和逐位反转的行为是实现定义。它不仅取决于负值的表示,还取决于编译器用于执行右移的CPU指令(例如,某些CPU没有算术(右)移位。
因此,除非您为实现创建其他约束,否则无法确定有符号整数的限制。这意味着没有完全可移植的方式(对于有符号整数)。
请注意,char
是签名还是无符号也是实现定义的,(unsigned char)(~0) >> 1
受整数促销的约束,因此它不会产生字符结果,而是int
。 (这使格式说明符正确 - 尽管可能是无意的)。
使用limits.h
获取实现的整数限制的宏。此文件必须由任何符合标准的C编译器提供。