一. 算术运算符
Include: + - * / %
/——除法,得到的是商,%——取模(取余),得到的是余数。
对于 / 操作符:如果两个操作数都为整数,执行整数除法。而只要有浮点数执行的就是浮点数除法。且打印格式要对应匹配。
对于%操作符:两个操作数必须为整数。 除了 % 操作符之外,其他的几个操作符可以作用于整数和浮点数。
二. 移位操作符
Include: << 左移操作符
>> 右移操作符
移位操作符移动的是2进制的位。
移位操作符的操作数只能是整数。
整数在内存中存储的是补码,计算时也是用补码计算,因为计算是拿内存中的值进行计算的。
补码是2进制的,而vs为了表达更简洁,用16进制显示内存中存储的值。e.g.: -5在内存中实际存入的应是其补码——1111 1111 1111 1111 1111 1111 1111 1011,而在vs内存中看到的其转换为16进制后的值——0xFF FF FF FB.
左移操作符:
移位规则:左边丢弃,右边补零。(不关心符号位)
区分:b=a<<1和a=a<<1,前者b为a左移一位产生的结果,但a自身的值仍为a、未变化;后者是赋值运算,a发生变化,结果为a左移一位产生的值。
右移操作符:
移位规则:(移n位也一样)
首先右移运算分两种:
逻辑移位
左边用0填充,右边丢弃
算术移位
左边用原该值的符号位填充,右边丢弃
警告⚠ :
对于移位运算符,不要移动负数位,这个是标准未定义的。(e.g.num>>-1//err)
右移时,采用i还是ii,是取决于编译器的,vs采用算术移位,一般情况下也是算术移位较常见。(只能通过负数来测试)
三. 位操作符
Include: & 按位与
| 按位或 ^ 按位异或
操作数必须是整数,操作的位是2进制位
规则:
& 按位与:0&0=0,0&1=0,1&1=1;
| 按位或:0 | 0=0,0 | 1=1,1 | 1=1;
^ 按位异或:
对应2进制位,相同为0,相异为1;
异或支持交换律;
0^a=a,a^a=0;
异或的应用: 不能创建临时变量(第三个变量),实现两个数的交换。
//F1:异或
//优:不可能进位导致溢出;
//缺:1.局限性(异或只能用于整数)
// 2.效率低(相较于创建临时变量)
#include <stdio.h>
int main()
{
int a = 10;
int b = 20;
a = a^b;
b = a^b;//<=>b=(a^b)^b=a^(b^b)=a^0=a;
a = a^b;//<=>a=(a^a)^b=b;
printf("a = %d b = %d\n", a, b);
return 0;
}
//F2:
//缺:存在潜在问题——溢出
#include <stdio.h>
int main()
{
int a = 10;
int b = 20;
a = a+b;
b = a-b;
a = a-b;
printf("a = %d b = %d\n", a, b);
return 0;
}
四. 赋值操作符
连续赋值:a=x=y+1; 从右往左结合;可读性差,尽量少用。
复合赋值:
复合赋值符 +=
-= *= /= %= >>= <<= &= |= ^=
a+3=15,这样的赋值是不可取的,因为左边的表达式无法代表一块空间。
区分:初始化(创建变量的同时赋值)和赋值(对已有变量赋值)。
五. 单目操作符
逻辑反操作符 !的应用:与标记变量结合使用
int main()
{
int flag=5;
if(flag)//flag为真时,就做什么
{
}
if(!flag)//flag为假时,就做什么
{
}
return 0;
}
取地址操作符 & 注意点:&arr——取出整个数组的地址
sizeof:
规范表达形式:1. sizeof(类型),括号不可省;2. sizeof(变量),括号可省;
printf("%d\n", sizeof(a));
printf("%d\n", sizeof(int));
printf("%d\n", sizeof a);//这样写行
printf("%d\n", sizeof int);//这样写不行
1. 不关心放的是什么类型,只关心所占空间大小。
2. sizeof 和 数组传参
#include <stdio.h>
void test1(int arr[])
{
printf("%d\n", sizeof(arr));//(2)
}
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));//(1)40——因为此处arr表示整个数组,类型为int[10];
printf("%d\n", sizeof(ch));//(3)10——因为此处ch表示整个数组,类型为char[10];
test1(arr);
test2(ch);
return 0;
}
//(2),(4)都为4/8
//因为传过去的是数组首元素的地址,形参相当于用对应的指针变量接收,
//所以此时的arr本质是指针变量,永远为4/8
对一个数的2进制按位取反 ~:是对补码进行取反!!(e.g.a = 0,则~a = -1)
对指定的2进制位进行操作:灵活应用 & | ^ ~ >> <<(下例为将某指定位改成0/1)
++,--前后置区别
强制类型转换 (类型):
是一种临时的状态,本质类型不会被改变(强扭的的瓜不甜,只能解渴)
float a = 3.14f;
int b = (int)a;//a只是在赋值时短暂地转换为了int型,但a本质仍为float型;
六. 关系操作符
=
<
<=
!= 用于测试“不相等”
== 用于测试“相等”
区分:赋值 = 和 关系操作符 == ,前者是赋值用的,后者是用于判断相等的。
七. 逻辑操作符
&& 逻辑与 ——并且——同为真才为真
|| 逻辑或 ——或者——同为假才为假
逻辑操作符只关心真or假,其中0为假,非0为真,但一般用1表示真。
int a = 3 && 5;
printf("%d\n",a);//1
区分:
逻辑与和或的特点:(短路)
&&——左边为假,右边不再计算;
| | ——左边为真,右边不再计算;
#include <stdio.h>
int main()
{
int i = 0,a=0,b=2,c =3,d=4;
i = a++ && ++b && d++;//(1)
//i = a++||++b||d++;//(2)
printf("a = %d\n b = %d\n c = %d\nd = %d\n", a, b, c, d);
return 0;
}
//程序输出的结果为
//(1) 1,2,3,4
//(2) 1,3,3,4
八. 条件操作符
exp1 ? exp2 : exp3
exp1为真,则不会计算exp3,整个表达式的结果为exp2的结果;
exp1为假,则不会计算exp2,整个表达式的结果为exp3的结果;
九. 逗号表达式
exp1, exp2, exp3, …expN
逗号表达式,就是用逗号隔开的多个表达式。
逗号表达式,从左向右依次执行。整个表达式的结果是最后一个表达式的结果。
逗号表达式的应用:
//代码1
int a = 1;
int b = 2;
int c = (a>b, a=b+10, a, b=a+1);//c=13
//代码2
if (a =b + 1, c=a / 2, d > 0)
//代码3
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。
( ) 函数调用操作符
接受一个或者多个操作数:第一个操作数是函数名,剩余的操作数就是传递给函数的参数。
访问一个结构的成员(详见结构体note)
. —— 结构体.成员名
->—— 结构体指针->成员名