C语言中的操作符

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

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

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

移位操作符(<< >>)

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

cpp 复制代码
<<                    //左移操作符,左边丢弃,右边补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
}
cpp 复制代码
>>                        //右移操作符,分为算数右移和逻辑右移,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
}

位操作符(& | ^)

cpp 复制代码
&                        //按位(二进制位)与,有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

cpp 复制代码
//方法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的个数:

cpp 复制代码
//方法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次方。

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

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

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

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

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

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

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

逻辑操作符(&& ||)

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

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

cpp 复制代码
#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;
}

三目操作符

cpp 复制代码
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
}

逗号表达式

逗号表达式就是用逗号隔开的多个表达式。 逗号表达式从左向右依次执行,整个表达式的结果是最后一个表达式的结果。

cpp 复制代码
//代码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;

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

\]是下标引用操作符,用于引用数组下标,有两个操作数:数组名和下标索引值,两个操作数的位置可以交换。 ```cpp int arr[10] = {0}; arr[7] = 10; 7[arr] = 9; *(arr + 7) = 8; *(7 + arr) = 7; //以上四种写法都可以改变arr中下标为7的数字,都是对的,第一种和第三种常用 ``` ()是函数调用操作符,操作数是函数名和函数参数。 访问一个结构体的成员可以使用**结构体对象.成员名** 或者**结构体指针-\>成员名**。 ### 表达式求值 表达式求值的顺序一般由操作符的优先级和结合性来决定,有些表达式操作数求值过程中也会进行一些类型转换。 #### 隐式类型转换 C语言的整形算术运算总是以**int精度** 来进行,为了达到精度,char或者short类型运算时会发生**整型提升**。整型提升的原则如下: ```cpp //负数的整形提升 char c1 = -1; 变量c1的二进制位(补码)中只有8个比特位: 1111111 因为 char 为有符号的 char 所以整形提升的时候,高位补充符号位,即为1 提升之后的结果是: 11111111111111111111111111111111 //正数的整形提升 char c2 = 1; 变量c2的二进制位(补码)中只有8个比特位: 00000001 因为 char 为有符号的 char 所以整形提升的时候,高位补充符号位,即为0 提升之后的结果是: 00000000000000000000000000000001 //无符号整形提升,高位补0 ``` 下面举一个例子: ```cpp 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 } ``` ```cpp 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。 ```cpp long double double float unsigned long int long int unsigned int int ``` ### 操作符优先级 各个操作符的优先级及结合性如下表,从上往下优先级降低: |--------|---------------|------| | 操作符 | 描述 | 结合性 | | () | 函数调用 | 从左到右 | | \[\] | 下标引用 | 从左到右 | | . | 访问结构体成员 | 从左到右 | | -\> | 访问结构体指针成员 | 从左到右 | | ++ | 后置++ | 从左到右 | | -- | 后置-- | 从左到右 | | ! | 逻辑反 | 从右到左 | | \~ | 按位取反 | 从右到左 | | + | 单目操作符,正 | 从右到左 | | - | 单目操作符,负 | 从右到左 | | ++ | 前置++ | 从右到左 | | -- | 前置-- | 从右到左 | | \* | 地址解引用 | 从右到左 | | \& | 取地址 | 从右到左 | | sizeof | 求大小,单位是字节 | 从右到左 | | (类型) | 强制类型转换 | 从右到左 | | \* | 乘法 | 从左到右 | | / | 除法 | 从左到右 | | % | 取余 | 从左到右 | | + | 加法 | 从左到右 | | - | 减法 | 从左到右 | | \<\< | 左移位 | 从左到右 | | \>\> | 右移位 | 从左到右 | | \> | 大于 | 从左到右 | | \>= | 大于等于 | 从左到右 | | \< | 小于 | 从左到右 | | \<= | 小于等于 | 从左到右 | | == | 等于 | 从左到右 | | != | 不等于 | 从左到右 | | \& | 按位与 | 从左到右 | | \^ | 按位异或 | 从左到右 | | \| | 按位或 | 从左到右 | | \&\& | 逻辑与 | 从左到右 | | \|\| | 逻辑或 | 从左到右 | | ?: | 三目操作符 | 无 | | = | 各种赋值,+=,-=之类的 | 从右到左 | | , | 逗号表达式 | 从右到左 | 可以看出,优先级最低的不是赋值,而是**逗号表达式**。

相关推荐
m0_6873998414 分钟前
写一个Ununtu C++ 程序,调用ffmpeg API, 来判断一个数字电影的视频文件mxf 是不是Jpeg2000?
开发语言·c++·ffmpeg
Natsume171018 分钟前
嵌入式开发:GPIO、UART、SPI、I2C 驱动开发详解与实战案例
c语言·驱动开发·stm32·嵌入式硬件·mcu·架构·github
爱上语文25 分钟前
Redis基础(5):Redis的Java客户端
java·开发语言·数据库·redis·后端
A~taoker30 分钟前
taoker的项目维护(ng服务器)
java·开发语言
萧曵 丶33 分钟前
Rust 中的返回类型
开发语言·后端·rust
shaun20011 小时前
华为c编程规范
c语言
hi星尘1 小时前
深度解析:Java内部类与外部类的交互机制
java·开发语言·交互
看到我,请让我去学习1 小时前
Qt编程-qml操作(js,c++,canvas)
开发语言·qt
橘子编程1 小时前
Python-Word文档、PPT、PDF以及Pillow处理图像详解
开发语言·python
MeshddY1 小时前
(超详细)数据库项目初体验:使用C语言连接数据库完成短地址服务(本地运行版)
c语言·数据库·单片机