目录
操作符的分类:
(1)算数操作符
+(加) -(减) *(乘 ) /(除) %(取余)
1.除了%操作符之外,其他的几个操作符可以作用于整数和浮点数
2.对于/操作符如果两个操作数都为整数,执行整数除法,而只要其中有一个浮点数执行的就是浮点数除法
cpp
int main() {
/*int a = 6, b = 5;
printf("%d", a / b);*/ //打印结果为1
double a = 6.0;
int b = 5;
printf("%lf", a / b); //打印结果为1.200000
return 0;
}
3.%操作符的两个操作数必须为整数,返回的是整除之后的余数
(2)移位操作符
正数在内存中用原码(因与补码相同)表示,负数用补码
<< 左移操作符
移位规则:左边抛弃,右边补0
cpp
int a = 2;
//把a的二进制位向左移动一位(0010->0100)
int b = a << 1; //并不改变a的值
printf("%d",b); //打印结果为4
>>右移操作符
移位规则:
1.逻辑移位:左边用0补充,右边丢弃
2.算数移位:左边用原来的符号位填充,右边丢弃
cpp
int a = 10;
//把a的二进制位向右移动一位(1010->0101)
int b = a >> 1; //并不改变a的值
printf("%d", b); //打印结果为4
(3)位操作符
& //按位与
cpp
int a = 3;
int b = 5;
//& - 按(2进制)位与
int c = a & b;
//0011 -a
//0101 -b
//0001 -c(对应位上同为1才为1,否则为0)
printf("%d", c); //打印结果为1
| //按位或
cpp
int a = 3;
int b = 5;
//| -按(2进制)位或
int c = a | b;
//0011 -a
//0101 -b
//0111 -c(对应位上同为0才为0,否则为1)
printf("%d", c); //打印结果为7
^ //按位异或
cpp
int a = 3;
int b = 5;
// ^ -按(2进制)位异或
int c = a ^ b;
//0011 -a
//0101 -b
//0110 -c(对应位相同为0,相异为1)
printf("%d", c); //打印结果为6
注:他们的操作数必须是整数
异或操作的用途之一:不创建中间变量即可交换两个整型元素的值(同时保证交换过程中不发生范围溢出),例:
cpp
//不创建中间变量交换a和b的值
int a = 3;
int b = 5;
printf("a=%d, b=%d\n",a,b);
a = a ^ b;
b = a ^ b;
a = a ^ b;
printf("a=%d, b=%d\n",a,b);
(4)赋值操作符
= += -= *= /= >>= <<= %=
连续的等号赋值是从左至右依次赋值:
cpp
int a = 20;
int x = 0;
int y = 20;
a = x = y + 1;
printf("%d %d",a,x);//打印结果为21 21
复合赋值符的含义:
cpp
int a = 10;
a = 100;
a += 100;//与a = a + 100等价
//其他复合赋值符同理
注:C语言中一个=是赋值,两个=是判断
(5)单目操作符
! 逻辑反操作
cpp
int flag = 5;//为真
printf("%d\n", !flag);//打印结果为0(为假)
flag = 0;//为假
printf("%d\n", !flag);//打印结果为1(为真)
- 负值
将某数变为其相反数
cpp
int a=10;
a=-a;
printf("%d",a);//打印结果为-10
+ 正值
对操作数不做何改变
cpp
int a=10;
a=+a;
printf("%d",a);//打印结果为10
sizeof 操作数的类型长度(以字节为单位)
注: sizeof是一个操作符,不是函数
cpp
int a = 10;
int arr[10] = { 0 };
printf("%d\n", sizeof(arr));//计算arr所占空间大小,单位是字节,打印结果为40,因为int类型大小是4个字节,数组有10个元素
printf("%d\n", sizeof(int));//打印结果是4,可以直接对类型用sizeof
注 :sizeof括号内的表达式不参与运算
cpp
short s = 5;
int a = 10;
printf("%d\n", sizeof(s = a + 2));//打印结果为2(short类型大小为2个字节)
printf("%d\n", s);//打印结果为5,sizeof括号内的表达式不参与运算
~ 对一个数的二进制按位取反(包括符号位)
cpp
int a = -1;
//10000000000000000000000000000001 -原码
//11111111111111111111111111111110 -反码
//11111111111111111111111111111111 -补码
// ~ 按位取反
//00000000000000000000000000000000(补码按位取反)
int b= ~a;
printf("%d",b);//打印结果为0
++ 前置、后置++
后置++:
cpp
int a = 10;
int b = a++;//后置++,先使用,再++
printf("%d\n", a);//打印结果为11
printf("%d\n", b);//打印结果为10
前置++:
cpp
int a = 10;
int b = ++a;//前置++,先++,后使用
printf("%d\n", a);//打印结果为11
printf("%d\n", b);//打印结果为11
- - 前置、后置- -
原理同++
cpp
int a = 10;
printf("%d\n", a--); //打印结果为10
printf("%d\n", a);//打印结果为9
* 间接访问操作符(解引用操作符)、 & 取地址操作符
cpp
int a = 10;
printf("%p\n", &a);//& - 取地址操作符
int *pa = &a;//pa是用来存放地址的 - pa就是一个指针变量
*pa = 20;// * 解引用操作符 - 间接访问操作符
printf("%d",a);//打印结果为20,*pa就代表着a
(类型) 强制类型转换
cpp
int a = (int)3.14;//将3.14double类型变量强制转换为int类型
printf("%d", a);//打印结果为3
(6)关系操作符
>
>=
<
<=
!= 用于测试"不相等"
== 用于测试"相等"
常用于判断语句中,较为简单
(7)逻辑操作符
&& 逻辑与
|| 逻辑或
cpp
int a = 3;
int b = 0;
if (a && b) {//a和b同时为真时,才进入下面的语句
printf("hello");//不执行此语句
}
if (a || b) {//a和b有一个为真,就进入下面的语句
printf("world");//执行此语句
}
注 :区分逻辑与 和按位与 区分逻辑或 和按位或
1&2---------->0 (0001和0010----->0000)
1&&2-------->1
1|2----------->3 (0001和0010----->0011)
1||2---------->1
注:(表达式)1&&(表达式2)时,若(表达式1)=0,则(表达式2)不再计算
cpp
int i = 0 , a = 0, b = 2, c = 3, d = 4;
i = a++ && ++b && d++;
printf("%d\n%d\n%d\n%d\n", a, b, c, d);//打印结果为 1 2 3 4
注:(表达式)1||(表达式2)时,若(表达式1)=1,则(表达式2)不再计算
cpp
int i = 0, a = 1, b = 2, c = 3, d = 4;
i = a++ || ++b || d++;
printf("%d\n%d\n%d\n%d\n", a, b, c, d);//打印结果为 2 2 3 4
(8)条件操作符
又称作三目操作符
exp1 ?exp2 : exp3
exp1结果为真返回exp2,否则返回exp3
cpp
int a = 3;
int b = 0;
//三目操作符
b = (a > 5 ? 1 : -1);//表达式1 ?表达式2 :表达式3
printf("%d", b);//打印结果为-1
(9)逗号表达式
exp1, exp2, exp3, ...expN
逗号表达式 - 要向右依次计算,但是整个表达式的结果是最后一个表达式的结果
cpp
int a = 3;
int b = 5;
int c = 0;
//逗号表达式要向右依次计算,但是整个表达式的结果是最后一个表达式的结果
int d = (c = 5, a = c + 3, b = a - 4, c += b);
printf("%d",d);//结果为9
(10)下标引用、函数调用和结构成员
[ ] : 下标引用操作符
操作数:一个数组名+一个索引值
cpp
int arr[10];//创建数组
arr[9] = 10;//实用下标引用操作符
//[ ]的两个操作数是arr和9
( ): 函数调用操作符:接受一个或多个操作数
第一个 操作数是函数名,剩余的操作数就是传递给函数的参数
cpp
int Add(int x, int y) {//函数的定义
return x + y;
}
cpp
int a = 10;
int b = 20;
int ret = Add(a, b);//调用函数 () - 函数调用操作符
访问一个结构的成员:
. 结构体.成员名
-> 结构体指针->成员名
cpp
//结构体
//书:书名,书号,定价
//创建了一个自定义的类型
struct Book
{
//结构体的成员变量
char name[20];
char id[20];
int price;
};
int num = 10;
struct Book b = { "C语言","20191112537",55 };
//结构体变量.成员名
printf("%s\n", b.name);
printf("%s\n", b.id);
printf("%d", b.price);
cpp
//结构体指针->成员名
struct Book* pb = &b;
printf("%s\n", pb->name);
printf("%s\n", pb->id);
printf("%d", pb->price);
//输出结果与上方代码输出结果相同
表达式求值:
表达式求值的顺序一部分是由操作符的优先级和决定性决定。
同样,有些表达式的操作数在求值的过程中可能需要转换为其他类型。
隐式类型转换
C的整型算数运算总是至少以缺省整型类型的精度来进行的。
为了获得这个精度,表达式中的字符和短整型操作数在使用之前被转换为普通整型,这种转换称为
整型提升
(只有长度小于int长度(short、char)才进行整型提升)
cpp
char a, b, c;
//...
a = b + c;
b和c的值被提升为普通整型,然后再执行加法运算。
加法运算完成之后,结果被截断,然后再存储于a中。
整型提升的意义:
具体例子:
cpp
char a = 3;
//00000011 - a(char只能存8个bite位)
char b = 127;
//01111111 - b
char c = a + b;
//a和b都是char类型,都没有达到一个int的大小
//这里会发生整型提升,高位补的都是符号位(无符号统一补0)
//a整型提升后:00000000000000000000000000000011
//b整型提升后:00000000000000000000000001111111
// c=00000000000000000000000010000010
//c发生截断,只存8个bite位,c = 10000010
//此时c是char类型的变量,需要打印整型变量,再次发生整型提升
//c - 11111111111111111111111110000010 (高位补符号位1)
//但负数在内存中形式是补码,所以还要再求c的原码
//c的原码 - 10000000000000000000000001111110 = -126
printf("%d\n", c);//打印结果为-126
cpp
char c = 1;
printf("%u\n", sizeof(c));//1
printf("%u\n", sizeof(+c));//4
printf("%u\n", sizeof(-c));//4
printf("%u\n", sizeof(!c));//4
//%u是输出无符号整型数
//+c -c !c 参与了运算,发生了整型提升,所以变为int的大小
算术转换
如果某个操作符的各个操作数属于不同的类型,那么除非其中一个操作数转换位另一个操作数的类型,否则操作就无法进行。下面的层次体系称为寻常算术转换。
long double
double
float
unsigned long int
long int
unsigned int
int
上方从大至小排列
如果某个操作数的类型在上面的这个列表中排名较低,那么首先要转换为另外一个操作数的类型后执行运算
cpp
int a = 4;
float f = 4.5f;
a + f;//需要将a的类型转换为float类型去计算
注:算数转换要合理,不然存在一些潜在的问题
cpp
float f = 3.14;
int num = f;//会丢失精度
操作符的属性
复杂表达式的求值有三个影响的因素
1.操作符的优先级
2.操作符的结合性
3.是否控制求值顺序(如(表达式1)&&(表达式2):(表达式1)是0后(表达式2)不再计算))
两个相邻的操作符优先执行哪个?取决于他们的优先级。如果两者的优先级相同,取决于他们的结合性。
cpp
int a = 4;
int b = 5;
int c = a + b * 7;//优先级决定了计算顺序
int c = a + b + 7;//优先级不起作用,结合性决定
运算符优先级和结合性表格:
(引用自博主星空之路Star的博客C语言34种运算符优先级及结合性)
一些问题表达式
注:有些表达式的运算顺序可能还是不唯一的
cpp
//表达式1
//a* b + c * d + e * f
//
//表达式的计算机可能是:
//a * b
//c * d
//a * b + c * d
//e * f
//a * b + c * d + e * f
//
//或者
//a * b
//c * d
//e * f
//a * b + c * d
//a * b + c * d + e * f
//由于*比+的优先级高,只能保证*的计算是比+早,但是优先级并不能决定第三个*比第一个+早执行
cpp
//表达式2
int c=3;
c + --c;
//优先级只能保证--运算在+之前,无法得知+操作符的左操作数的获取在右操作数之前还是之后求值,所以结果不可预测,有歧义
//2+2=4 或 3+2=5
cpp
//表达式3
int i = 1;
int ret = (++i) + (++i) + (++i);
printf("%d\n", ret);// VS里打印出12 gcc打印出10
//因为VS中先算了三次++i,再将三个结果相加
//而gcc中先算了两次++i,再相加,再执行++i,再相加(与上方表达式1原理类似)
以上为C语言中操作符和表达式相关内容的详细讲解,感谢您花费宝贵的时间阅读本文章!