目录
一、操作符
1、算术操作符
2、移位操作符
3、位操作符
4、赋值操作符
5、单目操作符
6、关系操作符
7、逻辑操作符
8、条件操作符
9、逗号操作符
10、下标引用、函数调用和结构成员
二、表达式求值
1、隐式类型转换
(1)整型提升
?(2)算术转换
?2、操作符的属性
三、练习:交换a和b的值
一、操作符
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;
}
|