目录
前言
一、算数操作符
+、-、*、/、%
1.%操作符只能作用于整数
2.除了%操作符之外,其他的几个操作符可以作用于整数和浮点数
3./操作符 无浮点数,执行整数除法,有浮点数,执行浮点数除法
int main()
{
double a = 5 / 2; 无浮点数,执行整数除法
printf("%lf",a);
return 0;
}
打印结果为2.000000
int main()
{
double a = 5 / 2.0; 浮点数,执行浮点数除法
printf("%lf",a);
return 0;
}
打印结果为2.500000
二、移位操作符
源码反码补码
printf打印出来的值都是原码
正整数在内存中的存储是补码,正整数的源码反码补码都一样
负整数在内存中的存储是补码,负整数的源码反码补码不相同
负整数的第一位为符号位,1代表负数,0代表正数
a = -1
a的二进制序列
源码 10000000000000000000000000000001
除了符号位其他都反过来
反码 11111111111111111111111111111110
补码=反码+1
补码 11111111111111111111111111111111
打印结果为 -1
左移位<<、右移位>>
移动的是二进制位
都是以算数移动为主
以右移位为主,a = -1为例子
算数移动
算数左移:低位补0
算数右移:低位丢弃,高位补原符号位
printf(“%d”,a >> 1);
原码 10000000 00000000 00000000 00000001
反码 11111111 11111111 11111111 11111110
补码 11111111 11111111 11111111 11111111
a >> 1向右移动一位
11111111 11111111 11111111 111111111
打印结果仍为-1
逻辑移动
逻辑左移:低位补0
逻辑右移:高位补0
printf(“%d”,a >> 1);
原码 10000000 00000000 00000000 00000001
反码 11111111 11111111 11111111 11111110
补码 11111111 11111111 11111111 11111111
a >> 1向右移动一位
补码 01111111 11111111 11111111 11111111
反码 01111111 11111111 11111111 11111110
原码 00000000 00000000 00000000 00000001
打印结果为1
三、位操作符
它的操作数必须是整数
比较的是二进制位
按位与 &
两个同时为1才为1
int main()
{
int a = 3;
int b = 5;
int c = a & b;
printf("%d",c);
return 0;
}
a的二进制序列 00000000000000000000000000000011 3
b的二进制序列 00000000000000000000000000000101 5
c的二进制序列 00000000000000000000000000000001 1
打印结果为1
使用技巧
1.任何变量 & 1 可以保留原本的数
int a = 20;
int b = a & 1;
b和a 的值相等
2.求一个数在内存中二进制中 1 的个数
if ((a >> i) & 1 == 1)
count++;
按位异 |
两个有一个为1 就为 1
int main()
{
int a = 3;
int b = 5;
int c = a | b;
printf("%c",c);
return 0;
}
a的二进制序列 00000000000000000000000000000011 3
b的二进制序列 00000000000000000000000000000101 5
c的二进制序列 00000000000000000000000000000111 7
打印结果为7
按位异或 ^
相同为0,相异为1
相同为0,相异为1
int main()
{
int a = 3;
int b = 5;
int c = a ^ b;
printf("%c",c);
return 0;
}
a的二进制序列 00000000000000000000000000000011 3
b的二进制序列 00000000000000000000000000000101 5
c的二进制序列 00000000000000000000000000000110 6
打印结果为6
使用技巧
不创造第三个变量,实现两个数的交换
int main()
{
int a = 3;
int b = 5;
a = a ^ b;
b = a ^ b;
a = a ^ b;
return 0;
}
a 011 110 110
b 101 101 011
^ 110 011 101
三组数据可以互相推导
按位取反 ~
对一个数的二进制按位取反
int main()
{
int a = 0;
int b = ~a;
printf("%d",b);
return 0;
}
a的二进制序列 00000000000000000000000000000000
b的二进制序列 11111111111111111111111111111111
在内存中的存储是补码
反码 = 补码 -1 11111111111111111111111111111110
原码=反码按位取反 10000000000000000000000000000001
b = -1
打印结果为 -1
四、赋值操作符 =
赋值操作符可以连续使用
int a = 10;
int x = 0;
int y = 20;
a = x = y+1
五、复合操作符
+=、-=、*=、/=、%=、>>=、<<=、&=、|=、^=
六、单目操作符
!(逻辑反操作)
真变假,假变真,假变真有固定值0变成1
int main()
{
int a = 0;
if(!a) 一般情况下表达为a为真则打印hhh
printf("hhh");
if(!a) 一般情况下表达为a为假则打印lll
printf("lll");
return 0;
}
if(真)括号里是真就会执行,if(!真)括号里不是真的就会执行
&(取地址)
int main()
{
int a =10;
int* p = &a;
&p = 20;
return 0;
}
sizeof
1.计算变量所占空间大小
int main()
{
int a = 10;
char c = 'c';
char* p = &c;
int arr[10] = {0};
printf("%d\n",sizeof(a)); int 4个字节
printf("%d\n",sizeof(int)); int 4个字节
printf("%d\n",sizeof(c)); char 1个字节
printf("%d\n",sizeof(char)); char 1个字节
printf("%d\n",sizeof(p)); char*8个字节
printf("%d\n",sizeof(char*)); char*8个字节
printf("%d\n",sizeof(arr)); int[10]40个字节
printf("%d\n",sizeof(int [10]) int[10]40个字节
return 0;
}
2.sizeof内的表达式不会直接发生运算,只会做好预处理的准备工作
int main()
{
short s = 0;
int a = 10;
int sz = sizeof(s = a + 5);
printf("%d\n", sz);
printf("%d\n", s);
return 0;
}
打印结果sz是2,s是0
3.sizeof内部的表达式不进行运算
int main()
{
short s = 0;
int a = 0;
int sz = sizeof( s = a + 5); s占两个字节
printf("%d\n",sz); sizeof内部的表达式不进行运算
printf("%d\n",s);
return 0;
}
打印结果为 0 2
前置/后置++/–
前置为先算再用
后置为先用再算
int main()
{
int a = 10;
printf("%d\n",++a); 前置++,先++再使用
11
printf("%d\n",a++); 后置++,先使用再++
11
return 0;
}
*(间接访问操作符)
int a = 5;
int* p = &a;
printf("%d",*p);
打印结果为5
(类型)强制类型转换
可能造成精度丢失(谨慎使用)
int main()
{
int a = (int)3.14;
printf("%d",a);
return 0;
}
强行转换成int,打印结果为3
七、关系操作符
>、>=、<、<=、!=、==
八、逻辑操作符
比较的是数本身,不为 0 就是真
&&
两个数都为真则为1
int main()
{
int a = 5;
int b =3;
int c = a && b; a && b 两个都为真则为1
printf("%d",c);
return 0;
}
打印结果为 1真
||
有一个为真就为1
int main()
{
int a = 0;
int b = 3;
int c = a || b; a || b其中一个为真就为真1
printf("%d",c);
return 0;
}
打印结果为1真
特性
1.对于&&,表达式中最左边的式子为假,后面的将不会进行计算
int main()
{
int i = 0,a = 0, b = 2, c = 3,d = 4;
i = a++ && ++b && d++;
printf("a = %d\nb= %d\nc = %d\nd = %d\n",a,b,c,d);
return 0;
}
打印结果:a=1,b=2,c=3,d=4
2.对于||,表达式中最左边的式子为真,后面的将不会进行计算
int main()
{
int i = 0,a = 0, b = 2, c = 3,d = 4;
i = a++ || ++b || d++;
printf("a = %d\nb= %d\nc = %d\nd = %d\n",a,b,c,d);
return 0;
}
打印结果:a=2,b=2,c=3,d=4
九、条件操作符
exp1?exp2:exp3
1为真,执行2的结果 1为假,执行3的结果
练习
if(a > 5)
b = 3;
else
b = -3;
转换成表达式
b = (a>5?3:-3)
练习
if(a>b)
max = a;
else
max = b
转换成表达式
max = (a>b?a:b);
十、逗号表达式
从左向右依次执行,整个表达式的结果是最后一个表达式的结果
int a = 1;
int b = 2;
int c = (a>b,a=b+10,a,b = a+1)
c是13
if(a = b+1,c=a/2,d>0)
只看d>0是否为真
a = get_val();
count_val(a)
while(a>0)
{
业务处理
a = get_val();
count_val(a);
}
用逗号表达式改写
while(a = get_val,count_val(a),a>0)
{
业务处理
}
十一、下标引用、函数调用和结构成员
下标引用操作符
操作数:一个数组名+一个索引值
int arr[10]; 创建数组
arr[9] = 10; 实用下标索引值
[ ]的两个操作数是 arr 和 9
函数调用操作符
操作数:一个函数名,()内部的参数
操作符:()
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);
printf("max = %d\n",max);
return 0;
}
访问一个结构的成员
图纸:不占用空间
struct Stu(名称)
{
成员变量名
char name[20];
int age;
char id[20]
}; 有分号
通过图纸建房子:占用空间了
int main()
{
int a = 10;
使用struct Stu这个类型创建了一个学生对象s1,并初始化
struct Stu s1 = {"张三",20,"20220101"};
指针类型名称struct Stu
struct Stu*ps = &s1;
三种方式都等价
结构体指针->成员名
printf("%s\n",(*ps).name); printf("%s\n",ps->name);
printf("%d\n",(*ps).age); printf("%d\n",ps->age);
printf("%s\n",(*ps).id); printf("%s\n",ps->id);
printf("%s\n",s1.name);
printf("%s\n",s1.age);
printf("%s\n",s1.id);
return 0;
}