按位“;不是";C中的运算符返回带符号的结果

按位“;不是";C中的运算符返回带符号的结果,c,linux,gcc,gnu99,C,Linux,Gcc,Gnu99,考虑以下代码: uint16_t a = ~ ( uint16_t ) 0; int16_t b = ~ ( int16_t ) 0; printf ( "%d %d %d %d\n", a == ~ ( uint16_t ) 0, a == ( uint16_t ) ( ~ ( uint16_t ) 0 ), b == ~ ( int16_t ) 0, b == ( int16_t ) ( ~ ( int16_t ) 0 ) ); 输出为:

考虑以下代码:

uint16_t a = ~ ( uint16_t ) 0;
int16_t  b = ~ ( int16_t ) 0;
printf (
    "%d %d %d %d\n",
    a  == ~ ( uint16_t ) 0,
    a  == ( uint16_t ) ( ~ ( uint16_t ) 0 ),
    b  == ~ ( int16_t ) 0,
    b  == ( int16_t ) ( ~ ( int16_t ) 0 )
);
输出为:

01

GCC抛出关于
a==~(uint16\u t)0的警告:

由于数据类型[-Wtype limits]的范围有限,比较总是错误的

为什么按位“not”运算符试图返回有符号值?我怎样才能防止呢

为什么按位“not”运算符尝试返回有符号值

因为没有运算符处理小于
int
的类型;较小的类型(包括
uint16\u t
if
int
超过16位)在用作操作数时升级。如果原始类型的所有值都可以用
int
表示,如此处所示,则升级为
int

我怎样才能防止呢

你不能;这就是语言的工作原理。您必须将运算符的结果转换为所需的类型,要么像初始化
a
时那样隐式转换,要么显式转换。请注意,在应用
~
之前不需要强制转换;但为了实现最大的可移植性,在执行按位逻辑时,应坚持使用无符号类型:

uint16_t a = ~0u;
printf("%d\n", a == (uint16_t)~0u);  // Should print 1

符号是一个位于位模式之上的概念。按位not(~)只涉及位模式,而不涉及值的符号。将有符号值notting为无符号值的结果是相同的

话虽如此,看看C标准:(草稿版免费提供)。第51页第6.3.1.4节:

如果一个int可以表示原始类型的所有值(受宽度限制,对于一个位字段),则该值将转换为int;否则,它将转换为无符号整数。这些称为整数。(58)所有其他类型不受整数的影响

我的意思是,当我们实际操作char和short类型时,它们将被提升为int(或unsigned int,取决于大小)。这是有意义的,因为我们希望操作尽可能快地完成,所以我们应该以机器的本机大小为目标

鉴于此,我们可以看到实际发生的情况。机器将以“int”大小执行所有操作,因为“==”和“~”的操作数都可以放入一个int字段中,我假设您的机器中的int字段为32位

现在首先要看的是‘a’的值。我们取0,而不是它,得到0xFFFFFFFF。我们将其分配给一个uint16_t值并得到0xFFFF。当我们准备好进行比较时,我们加载0xFFFF,意识到该值是无符号的,并将其扩展为0x0000FFFF。对于“b”的值,除了读取0xFFFF进行比较时,我们将其扩展到0xFFFFFF之外,所有内容都是相同的。现在,对于您的案例:

  • Notting零表示0xFFFFFF,与0x0000FFFF比较将失败
  • 我们取0xFFFFFF,将其切碎为0xFFFF,然后零将其扩展为0x0000FFFF,得到与“a”相同的值

  • 等等。

    波浪号
    ~
    运算符不是一元not。它是一个(一元)
    翻转所有位
    逐位运算符。
    ~
    是一元的补码运算符。我现在知道这个运算符的作用=)。我不知道该如何命名这个操作符。维基百科说“按位'不'”@puchu
    ~
    是按位不,
    不是布尔值…请阅读以下答案:这不清楚为什么
    printf
    应该打印“1”。是否打算使用此处所示的表达式
    (uint16_t)~0
    或原始代码中的表达式
    ~(uint16_t)0
    初始化
    a
    ?如果是后者,则不必打印“1”。后一个表达式总是在
    a
    中生成65535,但前者的值可能为0、1或65535,具体取决于C实现。最好使用
    ~0u
    而不是
    ~0
    @EricPostpischil:当然,你是对的;很容易忘记,当我有生之年(或多或少)不存在有符号整数时,这种语言支持有符号整数的奇异表示。我已经添加了一个注释,未签名类型应该是首选的,以防有人在编写UNIVAC时盲目地遵循我的建议。