操作符
分类:
算术操作符
移位操作符
位操作符
赋值操作符
单目操作符
关系操作符
逻辑操作符
条件操作符
逗号表达式
下标引用,函数调用和结构成员
算术操作符
+ - * / %(求余数)
1.%不可以作用于浮点数,其他几个操作符可以作用于整数和操作符。
2.对于 / 操作符如果两个操作符都为整数,执行整数除法。只要有浮点数执行浮点数除法。
3.%操作符的两个操作数必须为整数,返回的是整除之后的余数。
4.不能除0,不能模0。
移位操作符
<<左移操作符
规则:二进制位左边抛弃,右边补0。
int n=7;
7的二进制序列
00000000000000000000000000000111
n<<1后
0|00000000000000000000000000001110
>>右移操作符
分类:逻辑右移和算术右移
逻辑右移:左边用0填充,右边丢弃。
int n=-1;
-1的二进制序列
11111111111111111111111111111111
n>>1后
01111111111111111111111111111111|1 //最右边1丢弃,最左边部0。
算术右移:左边用原符号位填充,右边丢弃。
int n=-1;
-1的二进制序列
11111111111111111111111111111111
n>>1后
11111111111111111111111111111111|1 //最右边1丢弃,最左边部1。
注:有符号数,采用算术右移,左边填符号位。int, char
无符号数,采用逻辑右移,左边填0。unsigned int, unsigned char
warning:1.移位操作符不能移动负数,这是c语言标准未定义的。
2.被移位数在未被赋值的情况下,自身的值不变。
位操作符------操作对象:二进制序列
& 按位与
全1则1,有0则0。
1与任何数按位与为其本身。
| 按位或
全0则0,有1则1。
0与任何数按位或为其本身。
^按位异或
相异为1,相同为0。
异或满足交换律,a^b= b ^a。
任何数与其本身异或都为0。
#include<stdio.h>
int main()
{
char a=1;
char b=2;
a&b;//00000001&00000010=00000000=0;
a|b;//00000001|00000010=00000011=3;
a^b;//00000001&00000010=00000011=3;
return 0;
}
不能创建临时变量(第三个变量),实现两个数的交换。
#include<stdio.h>
int main()
{
int a=10;
int b=20;
a=a^b;
b=a^b;//b=a^b^b=a;
a=a^b;//a=a^b^a^b^b=b;
return 0;
}
求一个整数存储在内存中的二进制中1的个数
//方法1
#include<stdio.h>
int main()
{
int a=10;
int count=0;//计数
while(a)
{
if(a%2==1)
count++;
a=a/2;
}
printf("二进制中1的个数=%d\n",count);
return 0;
}
//当a的负数时如何解呢?
//方法2
#include<stdio.h>
int main()
{
int a=-1;
int count=0;
int i=0;
for(;i<32;i++)
{
if((a>>i)&1)==1)
count++;
}
printf("二进制中1的个数=%d\n",count);
return 0;
}
//这里要循环32次,能更优化吗?
//方法3
#include<stdio.h>
int main()
{
int a=0;
int count=0;
while(a)
{
count++;
a=a&(a-1);
}
printf("二进制中1的个数=%d\n",count);
return 0;
}
赋值操作符
对不满意的值重新赋值
int height=150;
height=180;//身高不够,赋值来凑。
赋值操作符可以连续赋值
int a=0;
int b=1;
int c=2;
a=b=c+10;//连续赋值,但不便于调试。
最好写成a=b;b=c+10;—>清晰爽朗便于调试。
复合赋值符
+=
-=
*=
/=
%=
//>>=
<<=
&=
|=
^=
有些运算符可以复合使用
int a=10;
a=a+10;//a+=10
a=a%10;//a%=10
其他运算符效果一样。
单目操作符
! 逻辑反操作
// - 负值
// + 正值
& 取地址
sizeof 操作数的类型长度(以字节为单位) 不是函数
~ 对一个数的二进制取反
– 前置 后置–
++ 前置 后置++
// * 间接访问操作符(解引用操作符)//对指针解引用,代表指针所指向的对象。
(类型)强制类型转换//只改变类型,没有改变内容。
sizeof
变量在内存中存储,在CPU中运算。计算时会把数据从内存传入CPU,存放在寄存器,然后在运算器中执行,sizeof在传入过程中被处理,而函数不会被处理。
#include<stdio.h>
int main()
{
short a=10;
int b=5;
printf("%d\n",sizeof(a=b+10));//不会编译通过,因为sizeof不是函数,不会参与计算。
return 0;
}
#include<stdio.h>
void test1(int arr[])
{
printf("%d\n",sizeof(arr));//4 数组传参时会发生降维,降维成指针。指针为4字节。32位系统。
}
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));//int 4个字节*10=40
printf("%d\n",sizeof(ch));//char 1个字节*10=10
test1(arr);
test2(ch);
return 0;
}
前置++ 先自增,再使用。
前置-- 先自减,再使用。
后置++ 先使用,再自增。
后置-- 先使用,在自减。
#include<stdio.h>
int main()
{
int a=10;
int m=++a;//a先自增,然后再使用a,也就是表达式的值是a自增后的值。m为11,此时a=11。
int n=--a;//a先自减,然后再使用a,也就是表达式的值是a自减后的值。m为10,此时a=10。
int i=a++;//a先使用,再自增。i为10,此时a=11。
int j=a--//a先使用,再自减。j为11,此时a=10。
return 0;
}
关系操作符
//>
//>=
<
<=
!= -------用于测试不相等
= = -------用于测试相等
warning:
1.不要把 ==写成=
2.字符串不能用关系操作符比较大小,比较字符串大小用strcmp函数。
逻辑操作符
&& 逻辑与------>同真为真,有假为假。从左往右扫描表达式,只要有一个不成立,后续都不执行。
| | 逻辑或------>同假为假,有真为真。从左往右扫描表达式,只要有一个成立,后续都不执行。
区分逻辑与和按位与 区分逻辑或和按位或
1&2------>0
1&&2---->1
1|2------->3
1||2------>1
#include<stdio.h>
int main()
{
int i=0,a=0,b=2,c=3,d=4;
i=a++&&++b&&d++;
printf("a=%d\n b=%d\n c=%d\n d=%d\n",a,b,c,d); //1 2 3 4,a++先使用a,a=0;表达式已经不成立,后续不执行。所以bcd值不变,a自增后变为1。
return 0;
}
条件操作符(三目操作符)
e1? e2: e3
e1为真,执行e2。
e1为假,执行e3。
求两个数的较大值
#include<stdio.h>
int main()
{
int a=10;
int b=20
int max=a>b?a:b;
printf("max=%d\n",max);
return 0;
}
逗号表达式
e1,e2,e3,…en
逗号表达式,就是用逗号隔开的表达式,从左往右依次执行,整个表达式的结果是最后一个表达式的结果。
int a=1;
int b=2;
int c=(a>b,a=b+10,a,b=a+1);//c=13
下标引用.函数调用.结构成员
[ ]下标引用操作符
操作数:一个数组名+一个索引值
int arr[10];//创建数组
arr[9]=10;//使用下标引用操作符
[ ]的操作数有两个,分别是arr和9。
函数调用操作符
接受一个或多个操作数:第一个操作数是函数名,剩余的操作数就是传递给操作符的参数。
#include<stdio.h>
void test()
{
printf("hello,world!\n");
}
int main()
{
test();//使用()作为函数调用操作符。
return 0;
}
访问一个结构体成员
. 结构体.成员名
. 结构体指针->成员名
#include<stdio.h>
struct Stu
{
char name[10];
int age;
char sex;
};//;不能省
void age1(struct Stu stu)
{
stu.age=18;//结构成员访问
}
void age2(struct Stu* pStu)
{
pStu->age=18;//结构成员访问
}
int main()
{
struct Stu stu;
struct Stu* pStu=&stu;
stu.age=20;//结构成员访问
age1(stu);//不能设置成功
pStu->age=20;//结构成员访问
age2(pStu);//可以设置成功,传入的是地址。
return 0;
}
操作符的属性
复杂表达式的求值有三个影响的因素:
1.操作符的优先级
2.操作符的结合性
3.是否控制求值顺序
两个相邻的操作符执行先后顺序:先取决于两者的优先级,然后是他们的结合性。