C 语言代码判断一个数的符号位?

老师们好
下面的代码,我怎么想也没有想明白会返回-1
0|0 = 0, 0|1 =1, 1|1 =1, 1|0 = 1,

怎么会出现-1呢,

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

int main(){
    int sign;
    int v = -10;
    sign = (v != 0) | (v >> (sizeof(int) * CHAR_BIT - 1)); // CHAR_BIT 宏定义在头文件limits.h中
    printf("%d\n", sign);
    return 0;
}

image.png

阅读 3.2k
3 个回答

为了更好的理解这个问题,我们先讲讲一个基础的知识点,我在曾经的笔记里也写过:
小凯15天快速讲完c语言-简单学习第二课

根据C语言的规范,在做位运算时,整型的符号位是以补码的形式存在的。对于负数,它的符号位是1。对于正数和零,符号位是0。
好了,明白了这个知识点,我们再代入到代码里一起看看,在这个代码中,首先执行了 (v != 0) 的比较,这里是判断 v 是否等于0,结果是真,即1。然后执行 (v >> (sizeof(int) * CHAR_BIT - 1)),这里进行右移操作。

这里第二个知识点来了:在C语言中,对于带符号的整数,右移运算会将最高位的符号位进行复制,填充左侧的空位。

所以对于负数 v = -10,右移后符号位的值是1。对于正数和零,符号位的值是0。接下来进行按位或运算,(v != 0) | (v >> (sizeof(int) * CHAR_BIT - 1))。对于负数,第一个表达式 (v != 0) 的结果是1,而第二个表达式 (v >> (sizeof(int) * CHAR_BIT - 1)) 的结果是1。按位或运算后,结果是1。

最后,将结果1打印出来,即 -1。因为 %d 是用来打印有符号整数的,所以将1作为有符号整数表示的话,它的补码形式是负数,因此打印出来的结果是-1。

所以,对于输入的 -10,代码将返回-1表示其符号位是负数。

希望我的回答能帮到你!

这个要看一下深入理解操作系统, 有符号整数的实现, 补码.

这个语句是个UB, 编译器的实现不一定遵从符号位为1时右移补1

 sign = (v != 0) | (v >> (sizeof(int) * CHAR_BIT - 1));

对于遵从符号位为1时右移补1的编译器

第一个表达式返回1, 第二个表达式返回-1

1|-1 返回-1

二进制:
0b0000 0000 0000 0000 0000 0000 0000 0001
按位或
0b1111 1111 1111 1111 1111 1111 1111 1111
结果为
0b1111 1111 1111 1111 1111 1111 1111 1111

这个问题严格来说是UB,因为C语言没有指明有符号整数的移位到底是逻辑移位(无视符号位)还是算数移位(负数会以1填充)。但实际中,我见过的编译器对有符号数使用的都是算术移位,因此,负数向右移位时,左侧会使用1填充。题主这里用-10向右移位了31位,所以补了31个1,再加上原来的最高位,即现在的第0位也是1,故得到了32个1,即-1的补码;此时,不管怎么位或,还是-1。
题主问的是“怎么判断符号位”,那就直接用大于号/小于号判断即可,如果一定要用位运算,可以使用N & 0x80000000

推荐问题
宣传栏