目录
一、操作符
1、算术操作符
+ - * / %
●除了%操作符只能作用于整数外,其他的几个操作符都可以作用于整数和浮点数。
●对于/操作符,如果两个操作数都为整数,执行整数除法。而只要有浮点数执行的就是浮点数除法。
●%操作符的两个操作数必须为整数,返回的是整除之后的余数。
例:
int a=3/5; //0
float a=6/5; //1.00000
float a=6/5.0 //1.2
int a=7%3.0 //报错
2、移位操作符
>> 右移操作符
<< 左移操作符
●移动位数为正数
●左移:把二进制位向左移动,左边丢弃,右边补0
●右移:把二进制位向右移动,右边丢弃,左边补0
int a=2;
int b=a<<1; //4
int c=10;
int d=c>>1; //5
3、位操作符
& 按位与
| 按位或
^ 按位异或
●操作数必须是整数
4、赋值操作符
直接赋值:=
复合赋值:+= -= *= /= %= >>= <<= &= |= ^=
●=赋值 ==判断
5、单目操作符
! 逻辑反操作
- 负值
+ 正值
& 取地址
sizeof() 操作数的类型长度(单位为字节),是一个操作符不是函数
~ 对一个数的二进制位按位取反
-- 前置、后置--
++ 前置、后置++
* 间接访问操作符(解引用操作符)
(类型) 强制类型转换
例:
//sizeof():
char arr[10]={0};
int size=sizeof(arr);
int len=sizeof(arr)/sizeof(arr[0]);
6、关系操作符
> 大于
>= 大于等于
< 小于
<= 小于等于
!= 用来测试“不相等”
== 用来测试“相等”
7、逻辑操作符
&& 逻辑与
|| 逻辑或
例:
int i=0,a=0,b=2,c=3,d=4;
i=a++ && ++b && d++; //a=1,b=2,c=3,d=4,因为第一部分为0,后面的不计算
8、条件操作符
表达式?表达式1:表达式2
9、逗号操作符
表达式1,表达式2,表达式3,...,表达式N
●从左向右依次执行,整个表达式的结果是最后一个表达式的结果
10、下标引用、函数调用和结构成员
●[] 下标引用
[]有两个操作数,一个是数组名,一个是索引值
●. 函数调用,结构成员访问
.可以接受一个或多个操作数,第一个操作数是函数名,剩余的操作数就是传递给函数的参数
●-> 结构成员访问
二、表达式求值
1、隐式类型转换
(1)整型提升
C的整形算术运算总是至少以缺省整型类型的精度来进行的。为了获得这个精度,表达式中的字符和短整型操作数在使用之前被转换为普通整型,这种转换称为整型提升。
例:
char a=3;
char b=127;
char c=a+b; //此时c=-126,因为a和b都是char类型,没有达到一个int的大小。这里就会发生整型提升。
整型提升是按照变量的数据类型的符号位来提升的。
●负数的整型提升:高位补1
char c1=-1;
变量c1的二进制位(补码)中只有8个比特位:11111111。因为char为有符号的char,所以整型提升的时候,高位补充符号位,即为1。
提升后的结果是:11111111111111111111111111111111
●正数的整型提升:高位补0
char c2=+1;
变量c2的二进制位(补码)中只有8个比特位:00000001。因为char为有符号的char,所以整型提升的时候,高位补充符号位,即为0。
提升后的结果是:00000000000000000000000000000001
●无符号整型提升:高位补0
例:
//例1
int main()
{
char a=0xb6;
char b=0xb600;
char c=0xb6000000;
if(a==0xb6)
printf("a"); //要进行整型提升,变成负数,不会输出
if(b==0xb600)
printf("b"); //要进行整型提升,变成负数,不会输出
if(c==0xb6000000)
printf("c"); //不需要整型提升,输出
return 0;
}
//例2
int main()
{
char c=1;
printf("%u\n",sizeof(c)); //1个字节
printf("%u\n",sizeof(+c)); //发生整型提升,4个字节
printf("%u\n",sizeof(-c)); //发生整型提升,4个字节
printf("%u\n",sizeof(!c)); //发生整型提升,4个字节
return 0;
}
(2)算术转换
如果某个操作符的各个操作符属于不同的类型,那么除非其中一个操作数的转换为另一个操作数的类型,否则操作就无法进行。
寻常算术转换:
long double>double>float>unsigned long int>long int >unsigned int>int
算术转换是根据操作数的类型在上述排名决定的。如果排名较低,就要转换为另外一个操作数的类型后执行运算。
2、操作符的属性
复杂表达式的求值有三个影响的因素:操作符的优先级、操作符的结合性、是否控制求值顺序。
两个相邻的操作符先执行哪个取决于它们的优先级。如果两者的优先级相同,取决于它们的结合性。
操作符 | 描述 | 用法示例 | 结果类型 | 结合性 | 是否控制求值顺序 |
( ) | 聚组 | (表达式) | 与表达式相同 | N/A | 否 |
( ) | 函数调用 | rexp(rexp,...,rexp) | rexp | L-R | 否 |
[] | 下标引用 | rexp[rexp] | lexp | L-R | 否 |
. | 访问结构成员 | lexp.member_name | lexp | L-R | 否 |
-> | 访问结构指针成员 | rexp->menber_name | lexp | L-R | 否 |
++ | 后缀自增 | lexp++ | rexp | L-R | 否 |
-- | 后缀自减 | lexp-- | rexp | L-R | 否 |
! | 逻辑反 | !lexp | rexp | R-L | 否 |
~ | 按位取反 | ~lexp | rexp | R-L | 否 |
+ | 单目,表示正值 | +rexp | rexp | R-L | 否 |
- | 单目,表示负值 | -rexp | rexp | R-L | 否 |
++ | 前缀自增 | ++rexp | rexp | R-L | 否 |
-- | 前缀自减 | --rexp | rexp | R-L | 否 |
* | 间接访问 | *rexp | lexp | R-L | 否 |
& | 取地址 | &rexp | rexp | R-L | 否 |
sizeof | 取其长度,以字节表示 | sizeof rexp sizeof(类型) | rexp | R-L | 否 |
(类型) | 类型转换 | (类型)rexp | rexp | R-L | 否 |
* | 乘法 | rexp*rexp | rexp | L-R | 否 |
/ | 除法 | rexp/rexp | rexp | L-R | 否 |
% | 整数取余 | rexp%rexp | rexp | L-R | 否 |
+ | 加法 | rexp+rexp | rexp | L-R | 否 |
- | 减法 | rexp-rexp | rexp | L-R | 否 |
<< | 左移位 | rexp<<rexp | rexp | L-R | 否 |
>> | 右移位 | rexp>>rexp | rexp | L-R | 否 |
> | 大于 | rexp>rexp | rexp | L-R | 否 |
>= | 大于等于 | rexp>=rexp | rexp | L-R | 否 |
< | 小于 | rexp<rexp | rexp | L-R | 否 |
<= | 小于等于 | rexp<=rexp | rexp | L-R | 否 |
= | 等于 | rexp=rexp | rexp | L-R | 否 |
!= | 不等于 | rexp!=rexp | rexp | L-R | 否 |
& | 位与 | rexp&rexp | rexp | L-R | 否 |
^ | 位异或 | rexp^rexp | rexp | L-R | 否 |
| | 位或 | rexp|rexp | rexp | L-R | 否 |
&& | 逻辑与 | rexp&&rexp | rexp | L-R | 是 |
|| | 逻辑或 | rexp||rexp | rexp | L-R | 是 |
?: | 条件操作符 | rexp?rexp:rexp | rexp | N/A | 是 |
= | 赋值 | lexp=rexp | rexp | R-L | 否 |
+= | 以...加 | lexp+=rexp | rexp | R-L | 否 |
-= | 以...减 | lexp-=rexp | rexp | R-L | 否 |
*= | 以...乘 | lexp*=rexp | rexp | R-L | 否 |
/= | 以...除 | lexp/=rexp | rexp | R-L | 否 |
%= | 以...取模 | lexp%=rexp | rexp | R-L | 否 |
<<= | 以...左移 | lexp<<=rexp | rexp | R-L | 否 |
>>= | 以...右移 | lexp>>=rexp | rexp | R-L | 否 |
&= | 以...与 | lexp&=rexp | rexp | R-L | 否 |
^= | 以...异或 | lexp^=rexp | rexp | R-L | 否 |
|= | 以...或 | lexp|=rexp | rexp | R-L | 否 |
, | 逗号 | rexp,rexp | rexp | L-R | 是 |
●一些问题表达式
写出的表达式如果不能通过操作符的属性确定唯一的计算路径,那这个表达式就是存在问题的。
例1:
a*b+c*d+e*f
表达式的计算顺序可能是:
a*b c*d a*b+c*d e*f a*b+c*d+e*f a*b c*d e*f a*b+c*d a*b+c*d+e*f
例2:
c + --c
表达式的计算顺序可能为:
--c c+--c c+--c --c
例3:来源于《C和指针》
int main() { int i=10; i=i-- - --i*(i=-3)*i++ + ++i; printf("i=%d\n",i); return 0; }
例4:
int fun() { static int count=1; return ++count; } int main() { int answer; answer=fun()-fun()*fun(); printf("%d\n",answer); return 0; }
三、练习:交换a和b的值
//1:借助第三个变量
int main()
{
int a=3;
int b=5;
int c=0;
c=a;
a=b;
b=c;
return 0;
}
//2:数值太大可能会溢出
int main()
{
int a=3;
int b=5;
a=a+b;
b=a-b;
a=a-b;
return 0;
}
//3:异或
int main()
{
int a=3;
int b=5;
a=a^b;
b=a^b;
a=a^b;
return 0;
}