C语言中的操作符

发布于:2024-05-23 ⋅ 阅读:(144) ⋅ 点赞:(0)

        C语言中会用到很多操作符,下面来简单介绍一下:

算术操作符(+ - * / %)

        前三个没什么好说的,主要注意一下后两个,/是得到两个数相除的,如果两边的数都是整数就进行整数除法,如果有一个浮点数就进行浮点数除法。%是得到两个整数相除的余数,也叫取模,它的两个操作数必须都是整数。

移位操作符(<< >>)

        移位操作符的操作数只能是整数。左移操作符相当于乘2,右移不一定相当于除2,如果是负数就不是除2,对于移位运算符,不要移动负数位,这个是标准未定义的

<<                    //左移操作符,左边丢弃,右边补0,相当于乘2
int main()
{
    int a = 7;
    int b = -7;
    //00000000000000000000000000000111------>7的补码
    //00000000000000000000000000001110------>左边丢弃,右边补0,得到14
    printf("%d",a << 1);//14
    //11111111111111111111111111111001------>-7的补码
    //11111111111111111111111111110010------>左边丢弃,右边补0后的补码
    //10000000000000000000000000001110------>左移后的原码,-14
    printf("%d",b << 1);//-14
}
>>                        //右移操作符,分为算数右移和逻辑右移,vs编译器用的是算术右移
//算数右移:右边丢弃,左边补符号位
//逻辑右移:右边丢弃,左边补0
int main()
{
    int a = 7;
    int b = -7;
    //00000000000000000000000000000111--->7的补码
    //00000000000000000000000000000011--->右边丢弃,左边补符号位,得到3
    printf("%d",a >> 1);//3
    //11111111111111111111111111111001--->-7的补码
    //11111111111111111111111111111100--->右边丢弃,左边补符号位,得到的补码
    //10000000000000000000000000000100--->右移后的原码,得到-4
    printf("%d",b >> 1);//-4
}

位操作符(& | ^)

&                        //按位(二进制位)与,有0为0,全1为1
|                        //按位(二进制位)或,有1为1,全0为0
^                        //按位(二进制位)异或,相同为0,不同为1
int main()
{
    int a = 3; 
    int b = -5;
    //000000000000000000000000000011--->3的补码
    //111111111111111111111111111011--->-5的补码
    //000000000000000000000000000011--->按位与后,得到3
    printf("%d\n",a & b);//3
    //000000000000000000000000000011--->3的补码
    //111111111111111111111111111011--->-5的补码
    //111111111111111111111111111011--->按位或后得到的补码
    //100000000000000000000000000101--->按位或后的原码,得到-5
    printf("%d\n",a | b);//-5
    //000000000000000000000000000011--->3的补码
    //111111111111111111111111111011--->-5的补码
    //111111111111111111111111111000--->按位异或后得到的补码
    //100000000000000000000000001000--->按位异或后得到的原码,得到-8
    printf("%d\n",a ^ b);//-8
}

        不能创建临时变量,实现两个数交换,先说几个异或的性质:

①a ^ a = 0;

②a ^ 0 = a;

③异或支持交换律,a ^ b ^ a = a ^ a ^ b = b

//方法1,问题是可能会溢出,如果a+b溢出了就会出错
int main()
{
    int a = 10;
    int b = 20;
    a = a + b;//a = a + b
    b = a - b;//b = a + b - b = a
    a = a - b;//a = a + b - a = b
    printf("a = %d, b = %d\n",a,b);
}
//方法2,问题是只能用于整数
int main()
{
    int a = 10;
    int b = 20;
    a = a ^ b;//a = a ^ b
    b = a ^ b;//b = a ^ b ^ b = a ^ 0 = a---->b = a;
    a = a ^ b;//a = a ^ b ^ a = 0 ^ b = b---->a = b;
    printf("a = %d, b = %d\n",a,b);
}

         虽然有以上两种方法,但最常用的方法还是增加一个临时变量。

        求一个整数存储在内存中二进制位1的个数:

//方法1,这种方法的输入参数必须是unsigned int,如果是int的话,输入-1,其补码都是1,应该输出32,但是按照上述代码,会输出0.
int main()
{
    int a = 10;
    int num = 0;
    while(a)
    {
        if(a % 2 == 1)
            num++;
        a /= 2;
    }
    printf("%d\n",num);
    return 0;
}
//方法2,这种方法需要循环32次
int main()
{
     int num = -1;
     int i = 0;
     int count = 0;//计数
     for(i=0; i<32; i++)
     {
         if( num & (1 << i) )//如果该位与上1不为0,就说明该位是1
         count++; 
     }
     printf("二进制中1的个数 = %d\n",count);
     return 0;
}
//方法3,最好的方法
int Num_of_1(int a)
{
    int count = 0;
    int i = 0;
    while (a)
    {
        a = a & (a - 1);
        count++;
    }
    return count;
}
//以a = 15为例
//1111---->a的补码
//1110---->a - 1
//1110---->a = a & (a - 1)
//1101---->a - 1
//1100---->a = a & (a - 1)
//1011---->a - 1
//1000---->a = a & (a - 1)
//0111---->a - 1
//0000---->a = a & (a - 1),循环结束
//可以得出结论,a & (a - 1)就是消掉了最右边的那个1,直到a中的1全部消失,即a = 0时循环了几次就有几个1.
//上述技巧还可以用来判断一个数是不是2的n次方,如果一个数是2的n次方,那么它只有最高位是1,其他位都是0,所以用a = a & (a-1)如果==0,则说明该数是2的n次方。


赋值操作符(= += -= *= /= %= &= |= ^= <<= >>=)

        赋值操作符用于对变量赋值:

int a = 0;
int b = 0;
a = 10;//赋值
a += 10;//相当于a = a + 10;,其他的同理
a = b = 10;//相当于b = 10; a = b;

  单目操作符(只有一个操作数)   

!            //逻辑取反,将真变成假,假变成真,c语言中0为假,非零为真
int main()
{
    int flag = 0;
    if(!flag)//如果flag是假,取反变为真,才会执行printf
    {
        printf("hello world");
    }
    return 0;
}
-                        //取相反数
+                        //符号不变,没啥卵用
int main()
{    
    int a = -10;
    int b = -a;
    printf("%d",b);//会得到10
}    
&                //取地址
sizeof            //计算大小,单位是字节
int main()
{
    int a = 10;
    printf("%d",sizeof(a));
    printf("%d",sizeof(int));//前两种是常用用法
    printf("%d",sizeof a);//这样用也可以,因为sizeof是操作符,而不是函数,()可以省略
    printf("%d",sizeof int);//如果操作数是数据类型,()不能省略,所以这种写法不对
}
~                //对一个数的二进制位取反,符号位也会取反
--                //前置--是先--再操作,后置是先操作再--
++                //同上
//把一个数的某一位清零或置一的写法如下
//把13的bit1置1
int main()
{
    int a = 13;
    a |= 1<<1;
    //00000000000000000000000000001101--->13的补码
    //00000000000000000000000000000010--->1<<1的补码
    //00000000000000000000000000001111--->a |= 1<<1,得到15
    printf("%d",a);//15
    
}
//把13的bit3清零
int main()
{
    int a = 13;
    a &=~ 1<<3;
    //00000000000000000000000000001101--->13的补码
    //00000000000000000000000000001000--->1<<3的补码
    //11111111111111111111111111110111--->~1<<3的补码
    //00000000000000000000000000000101--->a &=~ 1<<3,得到5
    printf("%d",a);//5
    
}
int main()
{
    int a = 10;
    int b = a--;
    printf("%d",a);//9,因为--了
    printf("%d",b);//10,因为是先操作后--,b = a, a = a - 1
}
int main()
{
    int a = 10;
    int b = --a;
    printf("%d",a);//9,因为--了
    printf("%d",b);//9,因为是先--后操作,a = a - 1,b = a
}
*                //解引用操作符,获取地址中存放的值
(类型)            //强制类型转换,把变量转换成()中的类型

关系操作符(> >= < <= != ==) 

        主要用与判断两个操作数的关系,需要注意不要把==写成赋值操作符=。

逻辑操作符(&& ||)

        &&是逻辑与,全真才为真,有一个假就是假。||是逻辑或,全假才为假,有一个真就是真。

如果&&左边的操作数为假,结果就肯定为假,右边的操作数就不算了。如果||左边的操作数为真,结果就肯定为真,右边的操作数就不算了。

#include <stdio.h>
int main()
{
    int i = 0,a=0,b=2,c =3,d=4;
    i = a++ && ++b && d++;//这里a为0,由于是后置++,先使用再++,a为0,&&的结果肯定是0,后边就不算了,所以只有a会++
    //i = a++||++b||d++;
    printf("a = %d\n b = %d\n c = %d\nd = %d\n", a, b, c, d);//a = 1,b = 2,c = 3,d = 4
    return 0;
}

三目操作符

exp1 ? exp2 : exp3//如果exp1为真就执行exp2,如果exp1为假就执行exp3
//常用于求两个数中的最大值
int main()
{
    int a = 10;
    int b = 20;
    int max = a > b ? a : b;//由于a < b ,第一个表达式为假,执行第三个表达式,max = b
}

逗号表达式

        逗号表达式就是用逗号隔开的多个表达式。 逗号表达式从左向右依次执行,整个表达式的结果是最后一个表达式的结果。
//代码1
int a = 1;
int b = 2;
//        0, a = 12, 12, b = 13
int c = (a>b, a=b+10, a, b=a+1);//逗号表达式
c是多少?
//c = b = 13;

下标引用,函数调用和结构成员

        []是下标引用操作符,用于引用数组下标,有两个操作数:数组名和下标索引值,两个操作数的位置可以交换。

int arr[10] = {0};
arr[7] = 10;
7[arr] = 9;
*(arr + 7) = 8;
*(7 + arr) = 7;
//以上四种写法都可以改变arr中下标为7的数字,都是对的,第一种和第三种常用

        ()是函数调用操作符,操作数是函数名和函数参数。

        访问一个结构体的成员可以使用结构体对象.成员名或者结构体指针->成员名

表达式求值

        表达式求值的顺序一般由操作符的优先级和结合性来决定,有些表达式操作数求值过程中也会进行一些类型转换。

隐式类型转换

        C语言的整形算术运算总是以int精度来进行,为了达到精度,char或者short类型运算时会发生整型提升。整型提升的原则如下:

//负数的整形提升
char c1 = -1;
变量c1的二进制位(补码)中只有8个比特位:
1111111
因为 char 为有符号的 char
所以整形提升的时候,高位补充符号位,即为1
提升之后的结果是:
11111111111111111111111111111111
//正数的整形提升
char c2 = 1;
变量c2的二进制位(补码)中只有8个比特位:
00000001
因为 char 为有符号的 char
所以整形提升的时候,高位补充符号位,即为0
提升之后的结果是:
00000000000000000000000000000001
//无符号整形提升,高位补0

        下面举一个例子:

int main()
{
    //00000000000000000000000000000101--->5的补码
    //00001001--->截断
    char a = 5;
    //00000000000000000000000001111110--->126的补码
    //01111110--->截断
    char b = 126;
    //00000000000000000000000000000101--->5整型提升后的补码,因为是char类型,前面加符号位0
    //00000000000000000000000001111110--->126整型提升后的补码,因为是char类型,前面加符号位0
    //00000000000000000000000010000011--->相加后的补码
    //10000011--->截断,得到c
    char c = a + b;
    //%d打印需要整型提升
    //11111111111111111111111110000011--->因为是char,整型提升加符号位1,得到整型提升后的补码
    //10000000000000000000000001111101--->提升后的原码,-125
    printf("%d\n",c);//-125
}
int main()
{
 char c = 1;
 printf("%u\n", sizeof(c));//c是char类型,sizeof是1
 printf("%u\n", sizeof(+c));//+c是一个表达式,会发生整型提升,提升成int,sizeof是4
 printf("%u\n", sizeof(-c));//同上
 return 0;
}

算数转换

        如果某个操作符的操作数类型不同,需要转换成相同类型再运算,如果某个操作数的类型在下面的表中排名靠下,需要转换成另一个操作数的类型,比如,int和float中的int就需要转换成float。

long double
double
float
unsigned long int
long int
unsigned int
int

操作符优先级

        各个操作符的优先级及结合性如下表,从上往下优先级降低:

操作符 描述 结合性
() 函数调用 从左到右
[] 下标引用 从左到右
. 访问结构体成员 从左到右
-> 访问结构体指针成员 从左到右
++ 后置++ 从左到右
-- 后置-- 从左到右
逻辑反 从右到左
~ 按位取反 从右到左
+ 单目操作符,正 从右到左
- 单目操作符,负 从右到左
++ 前置++ 从右到左
-- 前置-- 从右到左
* 地址解引用 从右到左
& 取地址 从右到左
sizeof 求大小,单位是字节 从右到左
(类型) 强制类型转换 从右到左
* 乘法 从左到右
/ 除法 从左到右
% 取余 从左到右
+ 加法 从左到右
- 减法 从左到右
<< 左移位 从左到右
>> 右移位 从左到右
> 大于 从左到右
>= 大于等于 从左到右
< 小于 从左到右
<= 小于等于 从左到右
== 等于 从左到右
!= 不等于 从左到右
& 按位与 从左到右
^ 按位异或 从左到右
| 按位或 从左到右
&& 逻辑与 从左到右
|| 逻辑或 从左到右
?: 三目操作符
= 各种赋值,+=,-=之类的 从右到左
逗号表达式 从右到左

        可以看出,优先级最低的不是赋值,而是逗号表达式


网站公告

今日签到

点亮在社区的每一天
去签到