C语言运算符与表达式
文章目录
一、运算符和表达式概念
运算符:对内存中的数字进行各种运算的符号
表达式:运算符和数字结合起来的式子,称之为表达式
二、运算符
1.算数运算符
运算符 | 名 |
---|---|
+ | 加 |
- | 减 |
* | 乘 |
/ | 除 |
% | 取余 |
注意:
1.参与除法运算如果是两个整数其结果只保留整数部分
2./和%不能用整数0进行操作,否则程序崩溃
3.%不能对浮点数使用,否则程序崩溃
4.%的结果与左边数的符号一致
5./如果除数为浮点数结果将为int的无穷大
例如
#include <stdio.h>
int main(void){
signed int a=0,b=0;
printf("输入两个数进行算数运算:");
scanf("%d%d",&a,&b);
printf("加法运算结果为:%d\n",a+b);
printf("减法运算结果为:%d\n",a-b);
printf("乘法运算结果为:%d\n",a*b);
printf("除法运算结果为:%d\n",a/b);
printf("取余运算结果为:%d\n",a%b);
return 0;
}
#root@root:~$
#如果输入1 2
加法:3
减法:-1
乘法:2
除法:0
取余:1
#如果输入1 0
+: 1
-:1
*:0
浮点数例外 (核心已转储) #程序崩溃
#如果输入0 1
+:1
-:-1
*:0
/:0
%:0
2.赋值运算符和复合赋值运算符
运算符 | 名 |
---|---|
= | 赋值 |
/= | 除赋值 |
*= | 乘赋值 |
%= | 取余赋值 |
+= | 加赋值 |
-= | 减赋值 |
&= | 位与赋值 |
|= | 位或赋值 |
^= | 位异或赋值 |
<<= | 左移赋值 |
>>= | 右移赋值 |
1.赋值运算符:就是把右边的值赋值给左边的变量
2.复合运算符:赋值运算符和其他运算符结合起来使用
3.不能对表达式和常量赋值运算及复合运算
#include <stdio.h>
int main(void){
signed int a=200,b=100;
printf("%d\n",a += b); //300
printf("%d\n",a -= b); //100
printf("%d\n",a *= b); //20000
printf("%d\n",a /= b); //2
printf("d\n",a %= b); //100
signed char c='A',d='B';
printf("%c\n",c += b); // 131
printf("%hhd\n",c -= b); //-1
return 0;
}
3自增和自减运算符
运算符 | 名 |
---|---|
++ | 自增 |
- - | 自减 |
自增:就是让变量对应的内存空间数值加1
自减:就是让变量对应的内存空间数值减1
形式:
前++:先对变量的值加1,后算表达式的值
后++:先算表达式的值,后对变量的值加1
前–:先对变量的值减1,后算表达式的值
后–:先算表达式的值,后对变量的值减1
例如
#include <stdio.h>
int main(void){
signed int a=0,b=0;
b = ++a;
printf("b=%d a=%d\n",b,a);
b =a++;
printf("b=%d a=%d\n",b,a);
b = --a;
printf("b=%d a=%d\n",b,a);
b= a--;
printf("b=%d a=%d\n",b,a);
return 0;
}
4.关系运算符
1.关系运算符的运算结果是整数:1(真)或者 0(假)
2.不要进行连续的逻辑判断
运算符 | 名 |
---|---|
== | 等于 |
!= | 非等于 |
> | 大于 |
>= | 大于等于 |
< | 小于 |
<= | 小于等于 |
顾名思义就是比大小
例如
#include <stdio.h>
int main(void){
signed char a='A',char b='a';
if(a == b){
printf("a等于b.\n");
}else if(a != b){
printf("a和b不相等\n");
}else if(a >= b){
printf("a大于等于b\n");
}else if(a <= b){
printf("a小于等于b\n");
}
return 0;
}
5.逻辑运算符
运算符 | 名 |
---|---|
&& | 逻辑与 |
|| | 逻辑或 |
! | 逻辑非 |
a.逻辑与使用语法:
表达式A && 表达式B
只有当表达式A和B都为真时,整个表达式才为真,当表达式A为假时,表达式B不做运算
b.逻辑或使用语法:
表达式A || 表达式B
只有当表达式A和B都为假时,整个表达式才为假,当表达式A为真时,表达式B不做运算
c.逻辑非使用语法:
!表达式A
当表达式A为真,整个表达式为假;当表达式A为假,整个表达式为真
d.短路运算:
表达式A && 表达式B
表达式A || 表达式B
逻辑与:当表达式A为假时,表达式B不做运算;当表达式A为真时,
表达式B才做运算。
逻辑或:当表达式A为真时,表达式B不做运算;当表达式A为假时,
表达式B才做运算。
<font color例如:
#include <stdio.h>
int main(void){
signed int a=1,b=2,c=3,d=0;//连续定义并初始化
//短路运算
printf("表达式结果:%d d=%d\n",(a<b && d++),d);//0 1
printf("表达式结果:%d d=%d\n",(a>b && d++),d);//0 1
printf("表达式结果:%d d=%d\n",(a<b || d++),d);//1 1
printf("表达式结果:%d d=%d\n",(a>b || d++),d);//1 2
//逻辑与,并且
printf("表达式结果:%d\n",(a<b && b<c));//1
printf("表达式结果:%d\n",(a>c && a<b));//0
printf("表达式结果:%d\n",(a<b && a>c));//0
//逻辑或,或者
printf("表达式结果:%d\n",(a>b || a>c));//0
printf("表达式结果:%d\n",(a<b || a>c));//1
printf("表达式结果:%d\n",(a>c || a<b));//1
//逻辑非,相反
printf("表达式结果:%d\n",!(a<b));//0
printf("表达式结果:%d\n",!(a>b));//1
return 0;
}
6.位运算符
运算符 | 名 |
---|---|
& | 位与 |
| | 位或 |
^ | 位异或 |
~ | 位取反 |
位运算就是对内存中的二进制进行运算
1.位与运算符特点:
任何数跟0做位与运算结果都为0,任何数和1做位与运算结果保持原值
应用场景:一般用于将某个数的某个bit位清0,并且保持其它bit位不变
2.位或运算符特点:
任何数跟1做位或运算结果都为1,任何数和0做位与运算结果保持原值
应用场景:一般用于将某个数的某个bit位置1,并且保持其它bit位不变
3.位异或运算符特点:
相同为0,不同为1
应用场景:一般用于将某个数的某几个bit为取反,并且保持一些
bit位不变
4.位反运算符特点:
1为0,0为1
应用场景:一般用于将某个数全部bit位取反
5.位反运算符一般和位与结合起来实现将某个数的某个bit位清0
例如
#include <stdio.h>
int main(void){
signed char a=0xAF,b=0x56;
printf("%#X\n",(a &= b)); //6
// 1010 1111
// &0101 0110
// 0000 0110 = 0x6
printf("%#X\n",(a |= b)); //0XFF
// 1010 1111
// |0101 0110
// 1111 1111 0xFF
printf("%#X\n",(a ^= b));//0XF9
// 1010 1111
// ^0101 0110
// 1111 1001 0xF9
printf("%#X,%#x\n",(~a),(~b));//a=0X50 b=0x59
// ~1010 1111
// 0101 0000 0x50
// ~0101 0110
// 1010 1001 0xa9
return 0;
}
7.移位运算符
运算符 | 名 |
---|---|
<< | 左位移 |
>> | 右位移 |
1.左位移运算符:将二进制统一向左移动N位,左移时右边空出来的位用0填充
2.右位移运算符:将二进制统一向右移动N位,需要注意无符号和有符号,无符号右移时左边空出来的用0来填充,有符号右移时左边多出来的用符
号位来填充
3.乘或者除2的N次方,可以使用左移或者右移N位,代码执行效率高
4.移位运算符可以结合位运算符(位与 & 位或 |)使用,将其某几位清0或者置1
例如
#include <stdio.h>
int main(void){
signed char a=0x1F;
printf("%#x\n",(a << 2)); //0x7c
//0001 1111 << 2
//0111 1100 = 0x7c
printf("%#x\n",(a >> 2)); //0x7
//0001 1111 << 2 该变量为有符号,符号位为0(正数),所以用0填充
//0000 0111 = 0x7
signed char b=0x1;
//b *= 8; --> b<<3;
//b <<= 3;代码执行效率高于b *=8;
printf("1*8=%d 1<<3=%d\n",b *= 8,b <<= 3);//8 8
//0x1*8=1*8=8=0x8;
//2的3次方 = 8 --> 0x1<<3 = 0x8
//0000 0001 << 3
//0000 1000 = 0x8
signed char c=0x10;
//c /= 8; --> b>>3;
//b >> 3;代码执行效率高于b /= 8;
printf("16/8=%d 16>>3=%d\n",(b *= 8),(b >> 3));//2 2
//0x10/8 = 16/8=2=0x2;
//2的3次方 = 8 --> 0x10>>3 = 0x2;
//0001 0000 >> 3
//0000 0010 == 0x2
//该变量为有符号,符号位为0(正数),所以用0填充
signed short int var1=100; //十六进制:0x64 short为2字节,16bit位
printf("%#x\n",(var1<<8)); //0x6400
//0000 0000 0110 0100 << 8
//0110 0100 0000 0000 == 0x6400 == 25600
signed int var2=2000;//十六进制:0x7D0 int为4字节,32bit位
printf("%#X\n",(var2 << 3)); //0x3E80
//0000 0111 1101 0000 << 3 //前面16位都是0 书写时省略
//0011 1110 1000 0000 == 0x3E80
//将某个数的N位清0或者置1
// 清0:a &= ~(1 << N);
// 置1:a |= (1 << N);
signed char clear_bit=0xFD;
//将0xFD的第0位清0
clear_bit &= ~(0x1 << 1);
printf("将第0位清0:%#x\n",clear_bit); //0xFD
// 1111 1110
//&~1111 1110
// 1111 1110 == 0xFD
//将0xFD的第3,4,5位清0
clear_bit &= ~(0x7 << 3);
printf("%#x\n",clear_bit); //0xC6
// 1111 1110
//&~1100 0111
// 1100 0110 == 0xC6
signed char set_bit=0x5A;
//将0x5A的第2,4位置1
set_bit |= (5 << 2);
printf("%#x\n",set_bit); //0x5C
// 0101 1010
// |0001 0100
// 0101 1100 == 0x5C
return 0;
}
8.取地址运算符和解引用运算符
运算符 | 名 |
---|---|
& | 取地址 |
* | 解引用 |
%p | 地址占位符 |
1.32位操作系统中地址是由32位二级制组成,也就是一个地址有32位,4字节
2.64位操作系统中地址是由63位二进制组成,也就是一个地址有64位,8字节
3.取地址运算符作用:获取一个变量在内存中对应的首地址
4.取地址运算符的语法格式:&变量名
5.解引用运算符作用:根据变量的首地址获取内存中的数据或者根据变量的首地址向内存写入新数据
6.解引用运算符的语法格式:*地址
例如
/*x86*/
#include <stdio.h>
int main(void){
//定义并初始化
signed int a=0;
//打印
printf("a的地址:%p a的值:%d\n",&a,*&a);
//获取变量a在内存中对应的首地址,通过该地址解引用修改内存存储的数据
*&a=100;
//打印修改后的数据
printf("a的值:%d\n",*&a);
//定义并初始化指针变量,指向变量a在内存中对应的首地址
signed int *pa=&a;
printf("%d\n",*pa);
*pa=200;
return 0;
}
9.条件运算符
运算符 | 名 |
---|---|
?: | 条件运算符(三目运算) |
条件运算符语法格式:表达式A ? 表达式B : 表达式C
当表达式A为真时,整个表达式的结果为表达式B的运算结果,否则整个表达式的结果为表达式C的运算结果
例如
#include <stdio.h>
int main(void){
signed int a=-10;
//求一个数的绝对值
printf("%d\n",(a >=0 ? a : -a));
return 0;
}
10.数据类型转换
1.隐式数据类型转换
1.数据类型转换分为两种形式,隐式数据类型转换和强制数据类型转换
2.隐式数据类型转换特点:如果表达式中不同数字的数据类型不同,gcc编译器先将不同数据类型转换成相同的数据类型之后再做运算
3.隐式数据类型转换过程中必须把占内存小的数转换成占内存大的数
4.如果既有整型数据类型又有浮点型数据类型,gcc编译器自动将整型数据类型转换成浮点型数据类型
5.如果既有有符号数据类型又有无符号数据类型,gcc编译器自动将有符号数据类型转换成无符号数据类型
2.强制数据类型转换
1.强制数据类型转换语法格式:目标数据类型变量=(目标数据类型)源数据类型变量;
2.强制数据类型转换可能会造成数据的丢失,所以强制数据类型转换都是小转大或者相等转换
3.建议:为了代码的可读性高,建议使用强制数据类型转换
例如
#include <stdio.h>
int main(void){
//定义并初始化
signed int var1=100;
signed char var2=20;
//隐式数据类型转换
//gcc编译器,自动将signed char类型换成成signed int类型再进行计算
printf("%d\n",(var1+var2)); //120
//强制数据类型转换
//代码阅读性高
printf("%d\n",(var1 += (int)var2));// 120
return 0;
}
注意:不管是隐式数据类型转换还是强制数据类型转换,变量的值都不会随着数据类型改变而改变
11.运算符的优先级
运算符 | 名称或含义 |
---|---|
[] | 数组下标 |
() | 圆括号 |
. | 成员选择(对象) |
-> | 成员选择(指针) |
- | 负数运算符 |
(类型) | 强制类型转换 |
++ | 前自增运算符 |
++ | 后自增运算符 |
– | 前自减运算符 |
– | 后自减运算符 |
* | 取值运算符 |
& | 取地址运算符 |
! | 逻辑非运算符 |
~ | 位取反运算符 |
sizeof | 长度运算符 |
/ | 除 |
* | 乘 |
% | 余数(取模) |
+ | 加 |
- | 减 |
<< | 左移 |
>> | 右移 |
> | 大于 |
>= | 大于等于 |
< | 小于 |
<= | 小于等于 |
== | 等于 |
!= | 非等于 |
& | 按位与 |
^ | 按位异或 |
| | 按位或 |
&& | 逻辑与 |
|| | 逻辑或 |
?: | 条件运算符 |
= | 赋值运算符 |
/= | 除赋值 |
*= | 乘赋值 |
%= | 取模赋值 |
+= | 加赋值 |
-= | 减赋值 |
<<= | 左移赋值 |
>>= | 右移赋值 |
&= | 位与赋值 |
^= | 位异或赋值 |
|= | 按位或后赋值 |
, | 逗号运算符 |