value vs type:确定变量是否签名的代码

时间:2011-09-19 11:03:38

标签: c unsigned signed

我在论坛中遇到过这个问题。答案是这样的:

#define ISUNSIGNED(a) (a >= 0 && ~a >= 0) 

//Alternatively, assuming the argument is to be a type, one answer would use type casts: 

#define ISUNSIGNED(type) ((type)0 - 1 > 0)

我对此有几个问题。为什么我们需要检查~a >= 0?第二个解决方案是什么?我不明白这句话:“论证是一种类型”。更重要的是,作者指出,第一个#define在ANSI C中不起作用(但在K& R C中有效)。为什么不呢?

6 个答案:

答案 0 :(得分:8)

#define ISUNSIGNED(a) (a >= 0 && ~a >= 0) 

对于带正号的有符号值,a >= 0将为真(显然),~a >= 0将为假,因为我们已经翻转了位,因此现在设置了符号位,从而产生负值值。因此整个表达都是错误的。

对于带符号的否定值,a >= 0将为false(显然),并且不会计算表达式的其余部分;表达式的整体结果为false。

对于无符号值,a >= 0 始终为真(显然,因为无符号值不能为负)。如果我们翻转位,那么~a >= 0也是如此,因为即使最高有效位(符号位)设置为1,它仍然被视为正值。

因此,如果原始值及其按位逆都是正数,则表达式返回true,即它是无符号值。

#define ISUNSIGNED(type) ((type)0 - 1 > 0)

例如,使用类型而不是值来调用它:ISUNSIGNED(int)ISUNSIGNED(unsigned int)

对于int,代码会扩展为

((int)0 - 1 > 0)  

这是假的,因为-1不大于0

对于unsigned int,代码会扩展为

((unsigned int)0 - 1 > 0) 

表达式中已签名的10文字将提升为unsigned以匹配第一个0,因此整个表达式将被评估为无符号比较。无符号算术中的0 - 1将包围,导致最大可能无符号值(所有位设置为1),大于0,因此结果为真。

至于为什么它适用于K& R C,而不是ANSI C,也许this article可以解释一下:

  

当加宽unsigned char或unsigned short时,结果类型为   int如果int足够大以表示所有的值   较小的类型。否则,结果类型为unsigned int。价值   保留规则为大多数人产生最少的惊喜算术结果   表达式。

我想这意味着在比较unsigned short0时,无符号值会转换为signed int,从而破坏宏的行为。

您可以通过让(a-a)根据需要评估为有符号或无符号零而不是始终签名的文字0来解决此问题。

答案 1 :(得分:3)

对于第一个宏:如果一个值为正(>= 0),则其在2补码中的按位否定必须为负,如果它是有问题的。无符号值将保持正值:

~64 == -65 (signed)
~64 == 191 (unsigneD)

对于第二种情况:如果将类型传递给marco,它会检查此类型是否可以包含烧结值。对于有问题的类型0-1 == -1,对于无符号类型,它是一个正值:

(char) 0-1 == -1
(unsigned char) 0-1 == 255

答案 2 :(得分:3)

~a >= 0的检查依赖于翻转已签名的位。逻辑如下:

  • 如果数字为负数,则无法无符号,因此a >= 0返回false。
  • 如果是肯定的,则是签名或未签名。
  • 如果这是未签名的,则应用~ a将导致最大无符号值 - a,仍为正无符号数。
  • 如果签名,则符号位将被否定,产生负值。

事实上,我认为a >= 0 && ~a >= 0并且数字仍然可以签名。这是因为可能存在负零。特别是,对于一个补码,全零的值表示0,而表示否定(全1)将为负0(或陷阱表示)。

还有整数促销的问题:

  

如果int可以表示原始类型的所有值,则该值将转换为int;否则,它将转换为unsigned int。这些被称为整数促销。所有其他类型都不会被整数促销更改。

整数提升适用于“整数转换等级小于或等于int和unsigned int等级的整数类型的对象或表达式”和“_Bool,int,signed int类型的[a]位字段”或unsigned int。“它们与一元运算符~和“通常的算术转换”一起使用,它包含第一个比较,因此它们适用于此处。

因此,unsigned char将被提升为int,因此在最初未签名时会被签名。

例如,这会产生错误的结果:

#include<stdio.h>
#define ISUNSIGNED(a) (a >= 0 && ~a >= 0)
void main(void) {
    unsigned char uc = 8;
    printf("%d", ISUNSIGNED(uc));
}

答案 3 :(得分:2)

有点翻转的技巧很可爱,但是如果你想知道某些东西的签名,那么有更简单的方法可以做到。签名是类型的属性,而不是值。要确定某个变量是否已签名,您可以询问您的编译器是否-1转换为该类型,是否小于0,如下所示:

#define issigned(t) (((t)(-1)) < 0)

如果您想了解特定的变量,可以向编译器询问该变量的类型:

issigned(typeof(v))

答案 4 :(得分:1)

问题中提出的答案(a >= 0 && ~a >= 0)是错误的,因为它取决于整数表示,特别是它不适用于那些&#39;如果a为0,则为补码。这是一种正确,便携的做事方式。

首先,如果T是整数类型,则可以使用以下宏:

#define ISUNSIGNED(T) ((T) -1 > 0)

因此,对于具有值作为参数的版本,如果值a的类型至少为int(以避免整数提升,这可能会更改类型的签名) ,可以使用以下宏:

#define ISUNSIGNED(a) (0*(a) - 1 > 0)

它不依赖于整数的表示,因此它与ISO C标准允许的所有整数表示兼容(两个补码,一个补码和符号幅度)。

答案 5 :(得分:0)

第一个#define适用于变量名称。第二个#define适用于类型名称。 (显然,如果你打算在同一个程序中使用它们,你必须给它们不同的名字。)

我不会随便知道为什么要么不能使用ANSI C。