1. 各种操作符的介绍。
2. 表达式求值
1 操作符分类:
算术操作符、移位操作符、位操作符、赋值操作符、单目操作符、关系操作符、逻辑操作符、条件操作符、逗号表达式、下标引用、函数调用和结构成员
2 算术操作符
+ - * / %
2.1除了 % 操作符之外,其他的几个操作符可以作用于整数和浮点数。
2.2对于 / 操作符如果两个操作数都为整数,执行整数除法。而只要有浮点数执行的就是浮点数除法。
2.3 % 操作符的两个操作数必须为整数。返回的是整除之后的余数。
3移位操作符
<< 左移操作符
>> 右移操作符
注:移位操作符的操作数只能是整数。
3.1左移操作符
移位规则:
左边抛弃、右边补0
3.2 右移操作符
移位规则:
首先右移运算分两种:
3. 逻辑移位
左边用0填充,右边丢弃
4. 算术移位
左边用原该值的符号位填充,右边丢弃
警告⚠ :
对于移位运算符,不要移动负数位,这个是标准未定义的。
4位操作符
& //按位与,二进制相同位上全1为1,有0为0
| //按位或,二进制相同位上有1为1
^ //按位异或,二进制位上相同为0,不同为1
//注:他们的操作数必须是整数。
练习理解例1
#include <stdio.h>
int main()
{
int num1 = 1;
int num2 = 2;
num1 & num2;
num1 | num2;
num1 ^ num2;
return 0; }
输出:
原因解释:按位与,二进制相同位上全1为1,有0为0
00000000000000000000000000000001 //1的二进制存储表达式
00000000000000000000000000000010 //2的二进制存储表达式
输出
00000000000000000000000000000000 //0
按位或,二进制相同位上有1为1
00000000000000000000000000000001
00000000000000000000000000000010
输出
00000000000000000000000000000000
^ 按位异或,二进制位上相同为0,不同为1
00000000000000000000000000000001
00000000000000000000000000000010
输出
00000000000000000000000000000011
例2:不能创建临时变量,实现两个变量内容交换
#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;
}
原因解释:按位异或满足交换律
^ 按位异或,二进制位上相同为0,不同为1
所以b=a^b ^b=a;
a=a ^b ^a=b;
(不过此方法不建议平时采用,效率较低,可读性差,针对数据类型单一)
例3 编写代码实现:求一个整数存储在内存中的二进制中1的个数。
参考代码:
//方法1
#include <stdio.h>
int main()
{
int num = 10;
int count= 0;//计数
while(num)
{
if(num%2 == 1)
count++;
num = num/2;
}
printf("二进制中1的个数 = %d\n", count);
return 0;
}
上述方法缺点很明显,只能针对自然数进行运算,不过整体逻辑也是进制转化的逻辑,所以虽然有局限性,但仍然值得借鉴。
//方法二
#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位,并且移动次数(1+32)*16次,计算次数较多,效率较低
//方法二略微优化
#include<stdio.h>
int main()
{
int a = 0;
scanf("%d", &a);
int count = 0;
while (a != 0)
{
if ((a & 1) == 1)
{
count++;
}
a=a >> 1;
}
printf("一共有%d个1\n", count);
return 0;
移动32次,每移动一次判断一次,移动次数减少
//方法3:
#include <stdio.h>
int main()
{
int num = -1;
int i = 0;
int count = 0;//计数
while(num)
{
count++;
num = num&(num-1);
}
printf("二进制中1的个数 = %d\n",count);
return 0;
}
运算逻辑简述
11111111111111111111111111111111 //-1的补码存储
11111111111111111111111111111110 //-1-1的二进制补码
11111111111111111111111111111100//-1-1-1的二进制补码
运算32次
00000000000000000000000000000000
此时循环结束。
num和num的按位与,有多少1运算多少次,效率大大提高
5. 赋值操作符
赋值操作符是一个很棒的操作符,他可以让你得到一个你之前不满意的值。也就是你可以给自己重新赋值。
int weight = 120;//体重
weight = 89;//不满意就赋值
double salary = 10000.0;
salary = 20000.0;//使用赋值操作符赋值。
赋值操作符可以连续使用,比如:
int a = 10;
int x = 0;
int y = 20; a = x = y+1;//连续赋值
这样的代码感觉怎么样?
那同样的语义,你看看:
x = y+1; a = x;
这样的写法是不是更加清晰爽朗而且易于调试。
复合赋值符、
+=、-=、*=、/=、%=、>>=、<<=、&=、|=、^=
这些运算符都可以写成复合的的效果
比如
int x = 10;
x = x+10;
x += 10;//复合赋值
//其他运算符一样的道理
6单目操作符
6.1 单目操作符介绍
! 逻辑反操作
- 负值
+ 正值
& 取地址
sizeof 操作数的类型长度(以字节为单位)
~ 对一个数的二进制按位取反
-- 前置、后置--
++ 前置、后置++
* 间接访问操作符(解引用操作符) (类型) 强制类型转换
代码演示
#include <stdio.h>
int main()
{
int a = -10;
int *p = NULL;
printf("%d\n", !2);
printf("%d\n", !0);
a = -a;
p = &a;
printf("%d\n", sizeof(a));
printf("%d\n", sizeof(int));
return 0; }
非真为0,非假为1,整形变量大小为4个字节,int当然也是4
6.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)
printf("%d\n", sizeof(ch));//(3)
test1(arr);
test2(ch);
return 0;
}
//问:
//(1)、(2)两个地方分别输出多少?
//(3)、(4)两个地方分别输出多少?
无论怎么修改,传过去int类型,大小是4
同理char类型大小为1
//++和--运算符
//前置++和--
#include <stdio.h>
int main()
{
int a = 10;
int x = ++a;
//先对a进行自增,然后对使用a,也就是表达式的值是a自增之后的值。x为11。
int y = --a;
//先对a进行自减,然后对使用a,也就是表达式的值是a自减之后的值。y为10;
return 0; }
//后置++和--
#include <stdio.h>
int main()
{
int a = 10;
int x = a++;
//先对a先使用,再增加,这样x的值是10;之后a变成11;
int y = a--;
//先对a先使用,再自减,这样y的值是11;之后a变成10;
return 0;
}
通过上述代码我们可以清楚感觉到前置++和后置++的区别
总的来说前置运算符先自增自减后使用
后置运算符先使用,后自增自减。