入门学习计算机第十二天—操作符详解
编译器:Microsoft Visual Studio 2010
前言
记录第十一天学习C语言的博客。
1、 算术操作符
、+ 、 - 、*、/、%
1,除了%操作符之外,其他的几个操作符可以作用于整数和浮点数。
2,对于/操作符,如果两个操作数都为整数,执行整数除法。而只要有浮点数(小数)执行的就是浮点数除法。
3,%操作符的两个操作数必须为整数,返回的是整除之后的余数
2、移位操作符
<< 左移操作符
` >>右移操作符
右操作符:
1、算术右移:(基本都是算术右移)
右边丢弃,左边补原符号位
2、逻辑右移:
右边丢弃,左边补0
int main()
{
int a = 16;
int b = a>>1;
//>> -- 右移操作符,移动的是二进制位
//00000000000000000000000000010000
// 00000000000000000000000000010000
//最高位补原符号位0
return 0;
}
结果输出的是8,右移一位有除以2的效果。
当负数进行右移时:
int main()
{
int a = -1;
int b = a>>1;
//>> -- 右移操作符,移动的是二进制位
//
//
//最高位补原符号位0
return 0;
}
输出的结果是-1
整数的二进制表示有:原码,反码,补码
存储在内存中的是补码
-1的原码是10000000000000000000000000000001,最高位为符号位
(原码转化为反码,符号位不变,其他位按位取反)
-1的反码是11111111111111111111111111111110
(反码转化位补码,反码加一)
-1的补码是11111111111111111111111111111111
右移一位之后还是11111111111111111111111111111111(此时还是补码,需要转化为原码输出),所以输出的结果还是-1;
左移操作符:
左边丢弃,右边补0
int main()
{
int a = 10;
int b = a << 1;
//<<-- 左移操作符,移动的是二进制位
return 0;
}
输出的结果是20。左移一位有乘以2的效果。
警告!!!!!:对于移位操作符,不要移动负数位,这个是标准未定义;
3、位操作符
位操作符:
&:按位与
|:按位或
^:按位异或
他们的操作数都是整数
&按位与: 有0则0
int main()
{
int a = 3;//011
int b = 5;//101
int c = a&b//001
return 0;
}
输出的结果是1
|按位或: 有1则1
int main()
{
int a = 3;//011
int b = 5;//101
int c = a|b;//111
return 0;
}
输出的结果是7
^按位异或: 对应的二进制位相同为0,相异为1
int main()
{
int a = 3;//011
int b = 5;//101
int c = a^b;//110
return 0;
}
输出的结果是6
一道变态的面试题:
不能创建临时变量 (即第三个变量),实现两个数的交换
第一种方法:加减法
int main()
{
int a = 5;
int b = 3;
a= a+b;//a=8,b=3
b= a-b;//a=8,b=5
a = a-b;//a=3,b=5
return 0;
}
这种方法有缺陷,当a和b的值非常大的时候,两个数相加有可能会溢出。
第二种方法:使用异或操作符
int main ()
{
int a = 5;
int b = 3;
a = a^b;//a为110,b为011。a = a^b为101
b = a^b;//a为101,b为011。b = a^b为110
a = a^b;//a为101,b为110。a = a^b为011
}
题目二:
统计一个整数的二进制有多少个1
第一种解法:
int main()
{
int num = 0;
scanf("%d",&num);
while(num)
{
if(num % 2 ==1)
count++;
num = num /2;
}
printf("%d\n", num);
return 0;
}
但此方法有个缺陷是,当值为负数时,会错误。
第二种解法:
int main()
{
int num = 0;
int count = 0;
scanf("%d", &num);
int i = 0;
for(i=0; i<32; i++)
{
if(1 ==((num>>i)&1))
count++;
}
return 0;
}
这种方法可以运用于负数
第三种:
int main()
{
int num = 0;
int count = 0;
scanf("%d", &num);
while(num)
{
count++;
num = (num&(num-1));
}
pirntf("%d\n",count);
}
4、赋值操作符
赋值操作符可以连续使用,比如
int a = 10;
int x = 0;
int y = 20;
a = x= y+1;//不推荐,建议分开写
x =y+1;
a = x;
复合赋值操作符
+=;-=;*=;/=;>>=;<<=;
5、单目操作符
!:逻辑反操作
真变成假,假变成真
int main()
{
int a = 0;
if(!a)
{
printf("hehe\n")
}
return 0;
}
± : 负值\正值
&:取地址操作符
int main()
{
int a = 10;
int* p = &a;
//*p解引用操作符
}
sizeof 操作数的类型长度(以字节为单位)
int main()
{
int a =10;
char c = 'r';
char* p = &c;
int arr[10]= {0};
printf("%d\n",sizeof(a));//4
printf("%d\n",sizeof(c));//1
printf("%d\n",sizeof(p));//4
printf("%d\n",sizeof(arr));//40
return 0;
}
int main()
{
short s = 0;
int a = 10;
printf("%d\n", sizeof(s= a+5));//2
printf("%d\n",s);// 0 s的值并没有改变
return 0;
}
~:按(二进制)位取反
int main()
{
int a = 0;
//00000000000000000000000000000000
//11111111111111111111111111111111 -按位取反/补码
//11111111111111111111111111111110 -反码
//10000000000000000000000000000001 - 原码
printf("%d\n", ~a);
return 0;
}
输出的值就是-1
案例:
int main()
{
int a = 15;
//00000000000000000000000000001111 让1111变成1011
//11111111111111111111111111111011 按位与 但是这个数如何获得
//00000000000000000000000000000001 1<<2再取反即可
a = a & (~(1<<2));
printf("%d\n",a);
return 0;
}
– ++ :前置 /后置
int main()
{
int a = 10;
printf("%d\n",++a);//先++,后使用 打印结果是11
printf("%d\n",a++);//先使用,后++ 打印结果是10
return 0;
}
*:间接访问操作符(解引用操作符)
(类型):强制转化类型操作符:
int mian()
{
int a = (int)3.14;
return 0;
}
6、关系操作符
<= >= != ==
赋值 = 和判断相等== 容易弄混
7、逻辑操作符
&&逻辑与 :
| |逻辑或 :关注数本身
区分逻辑与和按位与:
1&&2 ----->1
1&2-------->0
区分逻辑或与按位或:
1||2---------->1
1|2----------->3
360面试题:
int main()
{
int i =0,a=0,b = 2 ,c = 3 ,d =4;
i = a++ && ++b && d++;
printf("a = %d\nb = %d\nc = %d\nd = %d\n",a,b,c,d);
return 0;
}
输出的结果是
a++&&++b,当左边是a++,先使用a,a=0,&&的结果就是0,为假,&&右边的值不管是什么表达式都不会再计算了。
int main()
{
int i =0,a=0,b = 2 ,c = 3 ,d =4;
i = a++ || ++b || d++;
printf("a = %d\nb = %d\nc = %d\nd = %d\n",a,b,c,d);
return 0;
}
输出的结果为
a++||++b,当左边是a++,先使用a,a=0,||的结果就是0,为假,继续计算++b, 先++,后使用,b等于3,为真,||右边的值不管是什么表达式都不会再计算了。
8、条件操作符
exp1 ? exp2 : exp3
表达式1如果为真,执行表达式2,表达式2的结果为整个表达式的结果。如果为假,执行表达式3,表达式3的结果为整个表达式的结果
int main()
{
int a = 0;
int b = 0;
if(a > 5)
b = 3;
else
b = -3;
//可以写成 b = (a > 5 ? 3 : -3);
}
9、逗号表达式
exp1,exp2,exp3, … ,expN
从左向右依次执行,整个表达式的结果是最后一个表达式的结果。
int main()
{
int a = 1;
int b = 2;
int c = (a>b, a=b+10, a, b=a+1);//a>b不执行
print("%d\n",c)
return 0;
}
输出的结果是13
10、下标引用、函数调用和结构成员
下标引用操作符: 操作数:一个数组名+一个索引值
int arr[10];//创建数组
arr[9] = 10;//实用下标引用操作符
[ ]的两个操作数是arr和9
()函数调用操作符 接受一个或者多个操作数:第一个操作数是函数名,剩余的操作数就是传递给函数的参数。
int get_max(int x , int y)//定义函数的语法规则
{
return x > y ? x : y;
}
int main()
{
int a =10;
int b =20;
int max = get_max(a,b);//函数调用操作符
printf("max = %d\n",max);
}
访问一个结构的成员
结构体.成员名
结构体指针->成员名
struct Stu
{ //成员变量
char name[20];
int age;
char id[20];//创建一个结构体类型-struct Stu
};
int main()
{ //使用struct Stu 这个类型创建了一个学生对象s1,并初始化
struct Stu s1 ={"张三",20,"2018024"};
// struct Stu* ps= &s1;
printf("%s\n", s1.name);
//printf("%s\n", ps->name);
printf("%d\n", s1.age);
printf("%s\n", s1.id);
return 0;
}
表达式求值
表达式求值的顺序一部分是由操作符的优先级和结合性决定。
同样,有些表达式的操作数在求值的过程中可能需要转换为其他类型。
隐式类型转换:
C的整型算术运算总是至少以缺省整型类型的精度进行的。
为了获得这个精度,表达式中的字符和短整型操作符在使用之前转换为普通整型,这种转换类型称为整型提升
如何进行整型提升?
整型提升是按照变量的数据类型的符号位来提升的,无符号类型,补0来提升
int main()
{
char a =3;
//00000000000000000000000000000011 因为a是char类型,char类型是1个字节
//00000011 - a
char b = 127;
//00000000000000000000000001111111 同理
//01111111 -b
//a和b如何相加,整型提升,char是有符号
//a-00000000000000000000000000000011
//b-00000000000000000000000001111111
//c-00000000000000000000000010000010
//进行截断c-10000010
//进行整型提升
//11111111111111111111111110000010--补码
//11111111111111111111111110000001--反码
//10000000000000000000000001111110--原码
char c = a+b;
printf("%d\n",c);
return 0;
}
输出的结果是-126
int main()
{
char a = 0xb6;//1011 0110
short b = 0x600;// 0000 0000
int c = 0x6000000;
if(a == 0xb6)
printf("a");
if(b == 0x600)
printf("b");
if(c == 0x6000000)
printf("c");
return 0;
}
只输出了c。
因为 char short 在进行判断(也算表达式运算)时,需要整型提升,一旦提升,值就不会再相等了。
int main()
{
char c = 1;
printf("%u\n",sizeof(c));
printf("%u\n",sizeof(+c));
printf("%u\n",sizeof(!c));
return 0;
}
输出的结果为
1
4
1
只要参与表达式运算,就会发生整型提升,表达式+c,就会发生提升,所以sizeof(+c)是4个字节
算术转换
如果某个操作符的各个操作数属于不同类型,那么除非其中一个操作数的转换为另一个操作数的类型,否则操作就无法进行。
long double
double
float
unsigned long int
long int
unsigned int
int
如果某个操作数的类型在上面这个列表中排名较低,那么首先要转换为另外一个操作数的类型后执行运算。
操作符的属性
复杂表达式的求值有三个影响的因素:
1、操作符的优先级
2、操作符的结合性
3、是否控制求值顺序
两个相邻的操作符先执行哪个?取决于他们的优先级。如果两者的优先级相同,取决于他们的结合性。
计算机知识/代码知识(零碎)
在Window系统下,win+r输入calc可以打开计算器。