操作符学习总结
文章目录
一、操作符分类
(1)算术操作符
(2)移位操作符
(3)位操作符
(4)赋值操作符
(5)单目操作符
(6)关系操作符
(7)逻辑操作符
(8)条件操作符
(9)逗号表达式
()10下标引用、函数调用和结构成员
二、算数操作符
+ - * / %
主要介绍‘%’ 。
#include<stdio.h>
int main()
{
int a = 5 % 2;
int b = 5 / 2;
printf("%d\n", a);
printf("%d", b);
return 0;
} //注意‘%’两边应该是整数
注意:
\1. 除了 % 操作符之外,其他的几个操作符可以作用于整数和浮点数。
\2. 对于 / 操作符如果两个操作数都为整数,执行整数除法。而只要有浮点数执行的就是浮点数除法。
\3. % 操作符的两个操作数必须为整数。返回的是整除之后的余数。
三、移位操作符
<< //左移操作符
>> //右移操作符
//注:移位操作符的操作数只能是整数。
// 移动的是二进制位。
(一)左移操作符
<<
移位规则:左边抛弃、右边补0
(注意:整数在内存中存的是补码)
#include <stdio.h>
int main()
{
int a = 5;
int b = a << 1;
printf("%d", b);
return 0;
}
其中移位和运行情况如下
如果是负数如“-5”
#include <stdio.h>
int main()
{
int a = -5;
int b = a << 1;
printf("%d", b);
return 0;
}
(二)右移操作符
>>
移位规则:
首先右移运算分两种:
\1. 逻辑移位
左边用0填充,右边丢弃
\2. 算术移位
左边用原该值的符号位填充,右边丢弃
不同编译器下使用的规则不同
#include <stdio.h>
int main()
{
int a = 5;
int b = a>>1;
return 0;
}
算术移位:
逻辑移位:
正数是两者右移形式上相同。
如果是负数的情况下:
#include <stdio.h>
int main()
{
int a = -5;
int b = a>>1;
printf("%d",b);
return 0;
}
可以看出,当前编译器使用算数移位
算数移位背后规则:
警告⚠:
对于移位运算符,不要移动负数位,这个是标准未定义的。
四、位操作符
位操作符有:
这里提到的所有位,都是二进制位。
& //按位与
| //按位或
^ //按位异或
注:他们的操作数必须是整数。
【1】&, 按位与操作符
两位都为1时得到‘1’,只要有一个为0就得到0。
#include <stdio.h>
int main()
{
int a = -5;
int b = 3;
int c = a & b;
printf("%d", c);
return 0;
}
注意:这里结果为正数,其补码反码原码三者相同
【2】|,按位或操作符
两者只要有一方为1,结果就为1,两者都为0时,结果为0。
#include<stdio.h>
int main()
{
int a = -5;
int b = 3;
int c = a&b;
printf("%d",c);
return 0;
}
【3】^, 按位异或
两者相同得到0,两者相异得到1
#include<stdio.h>
int main()
{
int a = -5;
int b = 3;
int c = a ^ b;
printf("%d", c);
return 0;
}
注:
a ^ a = 0;
0 ^ a = a;
a ^ a ^ b = a ^ b ^ a = b;
//所以按位异或支持交换律。
五、赋值操作符
(一)=
=
赋值操作符可以让你得到一个你之前不满意的值。也就是你可以给自己重新赋值。
int a = 0 //此时叫初始话,并不是赋值操作符
a = 10; //这里是赋值操作符
a = x = y+1; //连续赋值,但并不推荐
//这个和
x = y+1;
a = x;
//是一样的
(二)复合赋值符
+= //a=a+5 和a+=5一样
-=
*=
/=
%=
>>=
<<=
&=
|=
^=
六、单目操作符
单目操作符是只有一个操作数的
! 逻辑反操作(可以将真变为假,假变为真)
- 负值
+ 正值
& 取地址
sizeof 操作数的类型长度(以字节为单位)
~ 对一个数的二进制按位取反
-- 前置、后置--
++ 前置、后置++
* 间接访问操作符(解引用操作符) (类型) 强制类型转换
//C语言中0表示假,非0表示真
注意:
int c = 5;
printf("%d",c--);
printf("%d",c);
//这里的c要先赋值再减,因此输出结果是5,4
七、关系操作符
>
>=
<
<=
!= //用于测试“不相等”
== //用于测试“相等”
//在编程的过程中== 和=不小心写错,导致的错误。
八、逻辑操作符
&& //逻辑与
|| //逻辑或
// && 两边都为真时才为真
// || 两边都为假时才会为假
#include <stdio.h>
int main()
{
int i = 0,a=0,b=2,c =3,d=4;
i = a++ && ++b && d++; //先运算a时,先操作再加,所以a=0,&&左边为假,整个式子为假,则只 //有a参与运算,则为1,2,3,4
//i = a++||++b||d++; //这里a为0,然后运算,b先加后算为非零为真,则无需继续算,则结果为 //1,3,3,4
printf("a = %d\n b = %d\n c = %d\nd = %d\n", a, b, c, d);
return 0; }
//程序输出的结果是什么?
九、条件操作符
exp1 ? exp2 : exp3
表达式1如果是真,则运算表达式2;否则运算3。
if (a > 5)
b = 3;
else
b = -3;
//转换成条件表达式,是什么样?
b=(a > 5 ? 3 : -3);
//或者
a > 5? b = 3 : b = -3;
十、逗号表达式
exp1, exp2, exp3, …expN
从左向右依次计算,并且以最后一个表达式结果为最终结果。
int a = 1;
int b = 2;
int c = (a>b, a=b+10, a, b=a+1);
十一、下标引用、函数调用和结构成员
【1】 ‘ [ ] ’ 下标引用操作符
操作数:一个数组名 + 一个索引值
如:
#include<stdio.h>
int main()
{
int arr[10]={0};
arr[7]=8;
8[arr]=6;
printf("%d",arr[7]);
printf("%d",8[arr]);
return 0;
}
【2】函数调用操作符
接受一个或者多个操作数:第一个操作数是函数名,剩余的操作数就是传递给函数的参数。
#include<stdio.h>
int Add(int x, int y)
{
return x+y;
}
int main()
{
int a = 10;
int b = 5;
int c = Add(a,b); //()就是函数调用操作符,操作数为 Add,a,b
//函数调用时至少要有一个操作数,即函数名。
return 0;
}
【3】 访问一个结构的成员
struct Stu
{
char name[20];
int age;
double score;
};
void set_stu(struct Stu ss)
{
strcpy(ss.name , "zhangsan"); //这里不能直接赋值,因为name数组名是一个地址,不能直接放字符串
ss.age = 20;
ss.score = 100.0;
}
void print_stu(struct Stu ss)
{
printf("%s %d %lf\n", ss.name, ss.age, ss.score);
}
int main()
{
struct Stu a = { 0 };
set_stu(a);
print_stu(a);
return 0;
}
这样运行出现了问题是因为实参和形参的关系,应该改成指针
#include <stdio.h>
#include<string.h>
struct Stu
{
char name[20];
int age;
double score;
};
void set_stu(struct Stu *ps)
{
strcpy((*ps).name , "zhangsan"); //这里不能直接赋值,因为name数组名是一个地址,不能直接放字符串
(*ps).age = 20;
(*ps).score = 100.0;
//strcpy(ps->name,"zhangsan");
// ps->age = 20;
// ps->score = 100.0;
//
//结构体指针->成员
//结构体对象. 成员 两者等价
}
void print_stu(struct Stu ss)
{
printf("%s %d %lf\n", ss.name, ss.age, ss.score);
}
int main()
{
struct Stu a = { 0 };
set_stu(&a);
print_stu(a);
return 0;
}
十二、表达式求值
表达式求值的顺序一部分是由操作符的优先级和结合性决定。
同样,有些表达式的操作数在求值的过程中可能需要转换为其他类型。
(一) 隐式类型转换
C的整型算术运算总是至少以缺省(默认)整型类型的精度来进行的。
为了获得这个精度,表达式中的字符和短整型操作数在使用之前被转换为普通整型,这种转换称为整型提升。
如何进行整体提升呢?
整形提升是按照变量的数据类型的符号位来提升的
//比如‘-1’
char a = -1;
//对于 int a = -1,这里面共有32个比特位
//原码 10000000 00000000 00000000 00000001
//反码 11111111 11111111 11111111 11111110
//补码 11111111 11111111 11111111 11111111
而char a = -1;//其中只有8个比特位空间
//补码11111111
//因为最高位是1,所以整形提升时候全部补上最高位的数字
//11111111 11111111 11111111 11111111
举例1:
int main()
{
char a = 25;
//截断后
//00011001
char b = 125;
//截断后
//01111101
char c = a + b;
//00011001 - a
//01111101 - b
//提升
//00000000 00000000 00000000 00011001
//00000000 00000000 00000000 01111101
//相加并截断
//10010110
//提升(最高位是1)
//11111111 11111111 11111111 10010110(补码)
//11111111 11111111 11111111 10010101(反码)
//10000000 00000000 00000000 01101010(原码)
// 转为十进制为-106
//
printf("%d\n", c);
return 0;
}
举例2:
int main()
{
char a = 0xb6;
short b = 0xb600;
int c = 0xb6000000;
if(a==0xb6)
printf("a");
if(b==0xb600)
printf("b");
if(c==0xb6000000)
printf("c");
return 0;
}
这里只会打印出‘c’,是因为a,b两者都要进行整型提升,导致不相同;
如果在char和short前都加上unsigned,那会使a和b都成为无符号,无符号整形提升,高位补0,那十进制结果不会有改变,这时会打印a , b , c三者。
举例3:
int main()
{
char c = 1;
printf("%u\n", sizeof(c));
printf("%u\n", sizeof(+c));
printf("%u\n", sizeof(-c));
return 0;
}
出现这种情况是因为后两者c都放入表达式,一旦进入表达式,就会整型提升。
(二)算数转换
如果某个操作符的各个操作数属于不同的类型,那么除非其中一个操作数的转换为另一个操作数的类型,否则操作就无法进行。下面的层次体系称为寻常算术转换。
long double
double
float
unsigned long int
long int
unsigned int
int
这个表格是向上转换,比如int 和 float 相遇,会转化为float
(三)操作符的属性
复杂表达式的求值有三个影响的因素。
\1. 操作符的优先级
\2. 操作符的结合性
\3. 是否控制求值顺序。
两个相邻的操作符先执行哪个?取决于他们的优先级。如果两者的优先级相同,取决于他们的结合性。
一些有问题的表达式:
【1】
a*b + c*d + e*f
代码1在计算的时候,由于比+的优先级高,只能保证,的计算是比+早,但是优先级并不能决定第三个*比第一个+早执行。
这里最好给每一个项加上括号。
【2】
c + --c;
同上,操作符的优先级只能决定自减–的运算在+的运算的前面,但是并没有办法得知, +操作符的左操作数的获取在右操作数之前还是之后求值,所以结果是不可预测的,是有歧义的。
int c = 2;
//如果c先获取,那运算为:2+1
//如果c后获取,那运算为:1+1
【3】
int fun()
{
static int count = 1;
return ++count;
}
int main()
{
int answer;
answer = fun() - fun() * fun();
printf( "%d\n", answer);//输出多少?
return 0;
}
上述代码 answer = fun() - fun() * fun();
中我们只能通过操作符的优先级得知:先算乘法,再算减法。
但是函数的调用先后顺序无法通过操作符的优先级确定。
【4】
#include <stdio.h>
int main()
{
int i = 1;
int ret = (++i) + (++i) + (++i);
printf("%d\n", ret);
printf("%d\n", i);
return 0; }
这个代码在不同环境编译器下结果不同,因此也具有问题
最近一直忙,没有时间更新博客,今天花了一个下午终于写出来了,请各位大佬指导!