一、 操作符分类
(1)算术操作符
+(加)-(减) *(乘) /(除) %(取模)
#include<stdio.h> int main() { // / 操作符 int a = 5 / 2;//如果两个操作数都为整数,执行整数除法 //商2余1 double a2 = 5 / 2.0;//有浮点数执行的就是浮点数除法 // %操作符 int b = 5 % 2;//两个操作数必须为整数 printf("a=%d\n", a);//a=2 printf("a2=%lf\n", a2);//a=2.500000 printf("b=%d\n", b);//b=1 return 0; }
(2) 移位操作符
<< 左移操作符
#include<stdio.h> int main() { int a = 16;//0000000010000,即2^4 //>>--右移操作符 //移动的是二进制 //算术右移--右丢弃,左补原位符号(通常使用) //逻辑右移--右丢弃,左补0 int b=a >> 1;//8--2^3 printf("%d\n", b); return 0; }
#include<stdio.h> int main() { int a = -1; //整数二进制表述:原码、反码、补码 //存储在内存的是补码 //原码100000000000000000000000001 //反码111111111111111111111111110 //补码111111111111111111111111111 int b = a >> 1; printf("%d\n", b);//-1 return 0; }
>> 右移操作符
#include<stdio.h> int main() { int a =5;//101 int b = a << 1;//1010=2^3+2=10 //<<--左边丢弃,右边补零 printf("%d\n", b);//10 return 0; }
注:
移位操作符的操作数只能是整数。
对于移位运算符,不要移动负数位,这个是标准未定义的
(3)位操作符
& 按位与
#include<stdio.h> int main() { //& 按二进制位与 //两个为1,则为1 //一个为0,则为0 int a = 3;//011 int b = 5;//101 int c = a & b;//001 printf("%d\n", c); return 0; }
| 按位或
#include<stdio.h> int main() { //| 按二进制位或 //两个为0,则为0 //一个为1,则为1 int a = 3;//011 int b = 5;//101 int c = a |b;//111 printf("%d\n", c);//7 return 0; }
^ 按位异或
#include<stdio.h> int main() { //^ 按位异或 //相异为1 //相同为0 int a = 3;//011 int b = 5;//101 int c = a^b;//110 printf("%d\n", c);//6 return 0; }
注:位操作符的操作数只能是整数。
//不能创建临时变量(第三个变量) //实现两个数的交换。 #include <stdio.h> int main() { int a =3;//011 int b =5;//101 //创建临时变量 //int tmp = 0; //printf("before:a=%d\n b=%d\n", a, b); //tmp = a; //a = b; //a = tmp; //printf("after:a=%d\n b=%d\n", a, b); //加减法 //缺陷:过于大,超出表达范围,会丢失,可能会溢出 //a = a + b; //b = a - b; //a = a - b; //printf("a = %d b = %d\n", a, b); //异或 //不会溢出 a = a ^ b;//110 b = a ^ b;//011 a = a ^ b;//101 printf("a = %d b = %d\n", a, b); return 0; }
//编写代码实现 //求一个整数存储在内存中的二进制中1的个数 #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> int main() { int num = 0; scanf("%d", &num); //统计num的补码中有几个1 int count = 0; while (num) { if (num % 2 == 1)//余数 count++; num = num / 2;//去个位数 } printf("二进制中1的个数 = %d\n", count); return 0; }
//优化,可以计算负数 #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> int main() { int num = 0; scanf("%d", &num); int i = 0; int count = 0;//计数 for (i = 0; i < 32; i++) { //if (num & (1 <<i)) if (1==((num >> i)&1)) //32bit //num & 1==1即第一位为1 count++; } printf("二进制中1的个数 = %d\n", count); return 0; }
(4) 赋值操作符
初始化:变量创建给一个值
赋值:变量有一个值,再给一个值
复合赋值符
+=( a = a + 2 / a + = 2 )
-= *= /= %=
>>= ( a >> = 1 / a = a >> 1)
<<= &= |= ^=
(5)单目操作符
只有一个操作数
! 逻辑反操作(假变成真,真变成假)
#include <stdio.h> int main() { int a = 0; if (a) { printf("xi\n"); } if (!a) { printf("hehe\n"); } return 0; }
- 负值( a = - 5 / a = - a )
+ 正值
& 取地址(与指针搭配)
* 间接访问操作符(解引用操作符)
#include <stdio.h> int main() { int a = 10; int* p = &a; *p = 20; return 0; }
sizeof 操作数的类型长度(以字节为单位)
为类型,不能去掉括号
#include <stdio.h> int main() { int a = 10; char c = 'r'; char* p = &c; int arr[10] = { 0 }; //内存所占空间的大小,单位是字节 printf("%d\n", sizeof(a));//4(a为整型,占4字节 printf("%d\n", sizeof(int));//4 printf("%d\n", sizeof(c));//1(c为一个字符) printf("%d\n", sizeof(char)); printf("%d\n", sizeof(p));//4(p为指针,4或8) printf("%d\n", sizeof(char*)); printf("%d\n", sizeof(arr));//40(数组为10个元素,每个元素四个字节) printf("%d\n", sizeof(int [10])); return 0; }
#include<stdio.h> int main() { short s = 0; int a = 10; printf("%d\n", sizeof(s = a + 5));//2 s是短整型 printf("%d\n", s);//0 上面表达式不直接参与运算 return 0; }
~ 对一个数的二进制按位取反
#include<stdio.h> int main() { int a = 11;//1011 a = a | (1 << 2); printf("%d\n", a);//15 a = a & (~(1 << 2)); printf("%d\n", a);//11 //int a = 0; //~按(二进制)位取反 //00000000000000000000000000000000 //11111111111111111111111111111111--补码 //11111111111111111111111111111110--反码 //10000000000000000000000000000001--原码 //printf("%d\n", ~a); return 0; }
-- 前置--(先--,后使用)、后置--(先使用,后--)
++ 前置++(先++,后使用)、后置++(先使用,后++)
(类型) 强制类型转换 // int a = ( int ) 3.14 ;
(6)sizeof 和 数组
#include <stdio.h> void test1(int arr[])//指针接收 { printf("%d\n", sizeof(arr));//4 } void test2(char ch[]) { printf("%d\n", sizeof(ch));//4 } int main() { int arr[10] = { 0 }; char ch[10] = { 0 }; printf("%d\n", sizeof(arr));//40--1个整型4个字节,10*4=40 printf("%d\n", sizeof(ch));//10 test1(arr);//传递是首元素地址 test2(ch); return 0; }
(7)关系操作符
>大于
>=大于等于
<小于
<=小于等于
!= 用于测试“不相等”
== 用于测试“相等”
(8)逻辑操作符
&& 逻辑与(都为真,则为1;一个假,则为0)
|| 逻辑或(都为假,则为0;一个真,则为1)
#include <stdio.h> int main() { int a = 0; int b = 5; int c = a && b; int d = a || b; printf("%d\n", c);//0 printf("%d\n", d);//1 return 0; }
#include <stdio.h> int main() { //int i = 0, a = 0, b = 2, c = 3, d = 4; //i = a++ && ++b && d++;//1 2 3 4 //a先使用,后++ //由于左边为假,右边不再计算 int i = 0, a = 1, b = 2, c = 3, d = 4; i = a++||++b||d++;//2 2 3 4 //a=1,即为真,后面不用计算 printf("a = %d\n b = %d\n c = %d\nd = %d\n", a, b, c, d); return 0; }
(9) 条件操作符
exp1 ? exp2 : exp3
表达式1为真,即表达式2为结果
表达式1为假,即表达式3为结果
#include <stdio.h> int main() { int a = 0; int b = 0; /*if (a > 5) b = 3; else b = -3; b = (a > 5 ? 3 : -3);*/ int max = 0; if (a > b) max = a; else max = b; max = (a > b ? a : b); return 0; }
(10)逗号表达式
逗号表达式,就是用逗号隔开的多个表达式。
逗号表达式,从左向右依次执行。整个表达式的结果是最后一个表达式的结果。
int a = 1; int b = 2; int c = (a > b, a = b + 10, a, b = a + 1); //无结果,12=2+10;12;13=12+1 printf("%d\n", c);//13 if (a = b + 1, c = a / 2, d > 0) //判断d是否大于0,大于0,前面为真,否则为假 a = get_val();//函数放于a中 count_val(a); while (a > 0) { //业务处理 a = get_val(); count_val(a); } //优化 while (a = get_val(), count_val(a), a > 0) { //业务处理 }
(11) 下标引用、函数调用和结构成员
1. [ ] 下标引用操作符
操作数:一个数组名 + 一个索引值
#include<stdio.h> int main() { int arr[10] = { 0 };//创建数组 arr[4] = 10;//实用下标引用操作符 return 0; }
2.( ) 函数调用操作符
#include <stdio.h> int get_max(int x, int y) //这个括号不是函数调用操作符 { return x > y ? x : y; } int main() { int a = 10; int b = 20; int max = get_max(a, b); //调用函数的时候的()就是函数调用操作符 //操作数为a,b,get_max,有三个 printf("max=%d\n", max); return 0; }
3. 访问一个结构的成员
. 结构体.成员名
-> 结构体指针->成员名
#include <stdio.h> //类型:结构体类型 struct Stu { //成员变量 char name[10]; int age; char id[20]; double score; }; int main() { //使用strut Stu的类型创建一个学生对象s1,并初始化 struct Stu s1 = {"曾",20,"201851520"}; struct Stu* ps = &s1; printf("%s\n",ps->name); printf("%d\n", ps->age); /*printf("%d\n", (*ps).name); printf("%s\n", s1.name); printf("%d\n", s1.age); printf("%s\n", s1.id);*/ return 0; }
二、表达式求值
表达式求值的顺序一部分是由操作符的优先级和结合性决定。
同样,有些表达式的操作数在求值的过程中可能需要转换为其他类型
(1)隐式类型转换
整型提升:C的整型算术运算总是至少以缺省整型类型的精度来进行的。为了获得这个精度,表达式中的字符和短整型操作数在使用之前被转换为普通整型。
如何进行整体提升呢? 整形提升是按照变量的数据类型的符号位来提升的
#include <stdio.h> int main() { char a = 3; //a中存放00000011 //提升加0 char b = 127; //b中存放01111111 char c = a + b; //c=a+b=10000010 //补码111111...11110000010 //反码111111...11110000001(减一) //原码100000...00001111110(取反) printf("%d\n", c);//-126 //%d,即打印整型 return 0; }
#include<stdio.h> int main() { //定义 char a = 0xb6;//char 1字节 //b6,b=10,两个十六进制的数,二进制为10110110 short b = 0xb600;//short 2字节 int c = 0xb6000000;//int 4字节 if (a == 0xb6)//会补1 printf("a"); if (b == 0xb600) printf("b"); if (c == 0xb6000000) printf("c");//c return 0; }
#include<stdio.h> int main() { char c = 1;//char是1字节 printf("%u\n", sizeof(c));//1 printf("%u\n", sizeof(+c));//4 printf("%u\n", sizeof(-c));//4 //整型提升,即计算的整型大小 printf("%u\n", sizeof(!c));//1 return 0; }
(2)算术转换
个操作符的各个操作数属于不同的类型,将其中一个操作数的转换为另一个操作数的类型
long double 8字节
double 8字节
float 4字节
unsigned long int 4字节
long int 4字节
unsigned int 4字节
int 4字节
上面这个列表中排名较低,那么首先要转换为另外一个操作数的类型后执行运 算
(3)操作符的属性
复杂表达式的求值有三个影响的因素
1. 操作符的优先级
2. 操作符的结合性
3. 是否控制求值顺序(是否继续运算)
#include<stdio.h> int main() { int a = 10; int b = 20; int c = b + a * 3; //操作符的优先级 //+和=优先级相同,比*优先级低 int d = a + b + c; //优先级一致 //操作符的结合性 return 0; }
问题表达式
a*b + c*d + e*f;
计算顺序,不知道最先开始哪个,没有唯一计算路径
c + --c ;
--优先级比+高,+操作符的左操作数c的值,可能是--c后,也可能是--c前获取,是有歧义的。
ret = (++i) + (++i) + (++i) ;
answer = fun() - fun() * fun() ;