【C和指针】学习笔记(二)——操作符与表达式

本文详细解释了C语言中的移位操作(包括算术和逻辑)、赋值规则,以及如何计算一个值中位为1的个数。还讨论了++、--、逻辑与、或运算符的优先级,以及左值和右值的区别,以帮助理解C语言中的运算符和表达式概念。
摘要由CSDN通过智能技术生成

操作符与表达式

一、移位

在左移位中,值最左边的几位被丢弃,右边多出来的儿个空位则由0 补齐。

从左边移入新位时,可以选择两种方案。一种是逻辑移位,左边移入的位用0 填充;另一种是算术移位,左边移入的位由原先该值的符号位决定。如果值10010110 右移两位,逻辑移位的结果00100101, 但算术移位的结果是11100101 。算术左移和逻辑左移是相同的,它们只在右移时不同,而且只有当操作数是负值时才不一样。

二、赋值

在下面的语句中,认为a 和x 被赋子相同的值的说法是不正确的:
a = x = y + 3;
如果x 是一个字符型变量,那么y+3 的值就会被截去一段,以便容纳于宇符类型的变量中。那
么a 所赋的值就是这个被截短后的值。在下面这个常见的错误中,这种截短正是问题的根源所在:

// 一段错误的代码
char ch;
...
while ((ch = getchar()) ! = EOF)...

EOF 需要的位数比字符型值所能提供的位数要多,这也是getchar 返回一个整型值而不是字符
值的原因。然而,把getchar 的返回值首先存储于ch 中将导致它被截短。然后这个被截短的值被提升为整型并与EOF 进行比较。当这段存在错误的代码在使用有符号宇符集的机器上运行时,如果读取了一个值为\377的字节时,循环将会终止,因为这个值截短再提升之后与EOF 相等。当这段代码在使用无符号字符集的机器上运行时,这个循环将永远不会终止!

三、计算一个值中位为1的个数

方法一:

#include <stdio.h>

int count_ones(unsigned int value) {
    int count = 0;
    while (value) {
        if (value & 1) {  // 检查最低位是否为1
            count++;
        }
        value >>= 1;  // 将值向右移动一位
    }
    return count;
}

int main() {
    unsigned int value = 123;  // 一个示例值
    int count = count_ones(value);
    printf("Number of ones: %d\n", count);
    return 0;
}

方法二:关键在于 value &= (value - 1) 这一行代码。它通过将 valuevalue - 1 进行按位与操作,将最右侧的1变为0。对于具有较少位为1的值,它的性能非常高,因为每次循环迭代都会消除一个1,从而减少了循环次数。

#include <stdio.h>

int count_ones(unsigned int value) {
    int count = 0;
    while (value) {
        value &= (value - 1);  // 清除最右侧的1
        count++;
    }
    return count;
}

int main() {
    unsigned int value = 123;  // 一个示例值
    int count = count_ones(value);
    printf("Number of ones: %d\n", count);
    return 0;
}

/*
value = 1010
  1010
& 1001
---------
  1000

  1000
& 0111
---------
  0000

count = 2
*/

四、++与–

操作数之前的操作符在变量值被使用之前增加它的值;在操作数之后的操作符在变量值被使用之后才增加它的值。

五、&& 与 ||

&& 操作符的左操作数总是首先进行求值,如果它的值为真,再对右操作数进行求值。如果左操作数的值为假,那么右操作数便不再进行求值。

|| 操作符的左操作数总是首先进行求值,如果它的值为假,再对右操作数进行求值。如果左操作数的值为真,那么右操作数便不再进行求值。

六、左值与右值

有如下语句:

a = b + 25;

a 是个左值,因为它标识了一个可以存储结果值的地点, b+ 25 是个右值,因为它指定了一个值。它们可以互换吗?

b + 25 = a;

不可以。原先用作左值的a 此时也可以当作右值,因为每个位置都包含一个值。当计算机计算b+25 时,它的结果必然保存于机器的某个地方。但是,程序员并没有办法预测该结果会存储在什么地方,也无法保证这个表达式的值下次还会存储千那个地方。其结果是,这个表达式不是一个左值。基于同样的理由,字面值常量也都不是左值。

听上去似乎是变量可以作为左值而表达式不能作为左值,但这个推断并不准确。在下面的赋值
语句中,左值便是一个表达式。

int a [30];

a[ b + 10 ] = O;

下标引用实际上是一个操作符,所以表达式的左边实际上是个表达式,但它却是一个合法的左
值,因为它标识了一个特定的位置,我们以后可以在程序中引用它。这里有另外一个例子:

int a, *pi;

pi = &a;
*pi= 20;

第2 条赋值语句左边的那个值显然是一个表达式,但它却是一个合法的左值。因为指针pi 的值是内存中某个特定位置的地址,*操作符使机器指向那个位置。当它作为左值使用时,这个表达式指定需要进行修改的位置。当它作为右值使用时,它就提取当前存储千这个位置的值。

七、优先级

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值