1.操作符分类
算术操作符
移位操作符
位操作符
赋值操作符
单目操作符
关系操作符
逻辑操作符
条件操作符
逗号表达式
下标引用、函数调用和结构成员
2.算术操作符
+、-、*、/、%
对于/操作符。如果两个操作数都为整数,执行整数除法,返回两个数的商,如果任意一个或者两个都是浮点数则执行浮点数乘法,返回两个数相除的值
对于%操作符,两个操作数只能为整数,返回两个操作数相除的余数
除了%操作符,其他操作符都可以用于整数浮点数
3.移位操作符
<< 左移位操作符
>>右移位操作符
注意:移位操作符的操作数只能是整数,不要移动负数位,移位操作符的对象是操作数的补码,
左移操作符:操作数的补码左边抛弃,右边补零,其效果相当于让操作数*2
如5和-5
5的补码是00000000000000000000000000000101
5<<1,则变为00000000000000000000000000001010,
而正整数的原码与补码相同,所以5<<1的值就为10
-5的补码是111111111111111111111111111111111011
-5<<1,则变为111111111111111111111111111111110110
可得到原码为10000000000000000000000000001010
右移操作符:1.算术右移:操作数的补码右边丢弃,左边用符号位填充 2.逻辑右移:操作数的补码右边丢弃左边补零
绝大数编译器采用算术右移,操作如上
4.位操作数
按位与:&,按位或| 按位异或^
注意:他们的操作数都为整数
按位与:两个操作数的补码的二进制位一一比较,若都为1则为1,反之为0
按位或:两个操作数的补码的二进制位一一比较,若其中一个为1则为1,反之为0
按位异或,两个操作数的补码的二进制位一一比较,若相同则为0,反之为1
利用按位异或可以交换两个变量大小,如下
#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;
}
如果我们想要知道一个整数储存在内存中的二进制1的个数,可以利用&解决代码如下
#include <stdio.h>
int main()
{
int num = -1;
int i = 0;
int count = 0;//计数
for(i=0; i<32; i++)
{
if( num & (1 << i) )
count++;
}
printf("二进制中1的个数 = %d\n",count);
return 0;
}
因为整数占四个字节,32个比特位,所以进行32次循环,让1进行32次左右操作,让1分别依次出现在32个位置,因为除了当前位置外的其他位置都为零,其他位置进行&操作一定为0,所以可以判断目标数的当前位置是0还是1,若为0,num & (1 << i)为0,为假,若为1,num & (1 << i)不为0,为真,count增加
举例:5的二进制为000000000000000000000000000000101
只有1<<0 得到 000000000000000000000000000000001
和 1<<2得到 000000000000000000000000000000100
的情况下
num & (1 << i)才为真,因此count为2
5.赋值操作符
=,可以连续使用比如,比如a=x=y+1,从右往左看一一赋值,等同于x=y+1,a=x
复合赋值符:+=、-=、*=、/=、%=、>>=、<<=、^=、&=、|=
6.单目操作符
! 逻辑反操作
- 负值
+ 正值
& 取地址
sizeof 操作数的类型长度
~ 对一个数的补码二进制按位取反
-- 前置、后置--
++ 前置、后置++
* 间接访问操作符
(类型)强制类型转换
注意: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()
{
icnt arr[10] = {0};
char ch[10] = {0};
printf("%d\n", sizeof(arr));//(1)
printf("%d\n", sizeof(ch));//(3)
test1(arr);
test2(ch);
return 0;
}
此时(1)为10*4(2)为10*1,而(2)(4)都是计算指针大小,为4或8
7.关系操作
>、>=、<=、!=、==
注意:相等是两个等号
8.逻辑操作符
&& 逻辑与
||逻辑或
&&:若左右两个表达式都为真,则为真,反之为假
||:若左右两个表达式都为假,则为假,反之为真
若&&左边为假,则后面不进行判断,因为此时整个表达式一定为假
若||左边为真,则后面不进行判断,因为此时整个表达式一定为真
int i = 0,a=0,b=2,c =3,d=4;
i = a++ && ++b && d++;
printf("a = %d\n b = %d\n c = %d\nd = %d\n", a, b, c, d);
return 0;
此时a++先用a的值为0,整个表达式已经确定为假,所以++b与d++不进行计算
此时a=1,b=2,c=3,d=4
9.条件操作符
exp1?exp2:exp3
若exp为真表达式的值为exp2的值,若exp1为假表达式的值为exp3的值
10.逗号表达式
逗号表达式是用逗号隔开的多个表达式,从左到右依次执行,整个表达式的结果是最后一个表达式的结果
逗号表达式在循环中有一个神奇的运用
a = get_val();
count_val(a);
while (a > 0)
{
//业务处理
a = get_val();
count_val(a);
}
若用逗号表达式可以改写成
while (a = get_val(), count_val(a), a>0)
{
//业务处理
}
11.下标引用、函数调用和结构成员
[]下标引用操作符
操作数:一个数组名+一个索引值,如下
int arr[10]=0;
arr[1]=8;
这里面arr和1都是操作数
()函数调用操作符
操作数:函数名+参数(可以没有),如下
#include <stdio.h>
void test1()
{
printf("hehe\n");
}
void test2(const char *str)
{
printf("%s\n", str);
}
int main()
{
test1();
test2("hello bit.");
return 0;
}
这里面test1是不需要参数的函数,test2是需要参数的函数
3.访问一个结构的成员
结构体.成员名
结构体指针->成员名
如下
#include <stdio.h>
struct Stu
{
char name[10];
int age;
char sex[5];
double score;
};
void set_age1(struct Stu stu)
{
stu.age = 18;
}
void set_age2(struct Stu* pStu)
{
pStu->age = 18;//结构成员访问
}
int main()
{
struct Stu stu;
struct Stu* pStu = &stu;//结构成员访问
stu.age = 20;//结构成员访问
set_age1(stu);
pStu->age = 20;//结构成员访问
set_age2(pStu);
return 0;
}