目录
一.算数操作符:
众所周知数学里面有加,减,乘,除等算数符号。而在C语言中我们也有进行数学运算的符号,叫做算数操作符,但是和数学中的符号还是有一定的区别。
算数操作符:
+ 进行两个数的加法运算
- 进行两个数的减法运算
* 进行两个数的乘法运算
/ 进行两个数的除法运算
% 进行两个数的取余运算
算数操作符中的加减乘和数学中的加减乘作用十一样的,都可以进行整数和浮点数(小数)的运算,
但是取余操作符 % 只能参与整数运算,即 % 两边不能有浮点数存在。而 / 只保留整数部分,5/2是得不到2.5,所以5/2=2。
int a = 5;
int b = 2;
int c = a / b;//只保留整数部分
int d = a % b;
printf("a/b=%d\n", c);//结果是c=2
printf("a%b=%d\n", d);//结果是d=1
二.移位操作符:
移位操作符,移动的是什么呢?其实移动的是二进制补码的位。在这里我们就要学习一下二进制的原码,反码,补码。
一个数在计算机中的二进制表示形式, 叫做这个数的机器数。机器数是带符号的,在计算机用一个数的最高位存放符号, 正数为0, 负数为1。(即最高位为符号位)
原码:就是将一个数的二进制位原原本本的翻译出来。
反码:最高位不变,将数字的原码进行按位取反,0变成1,1变成0。
补码:反码+1。
正数的原码,反码,补码相同。
移位操作符:
1.<<左移操作符:左边不要,右边补0。
正数:
int main()
{
int a = 1;
//00000000000000000000000000000001-1的补码
int b = a << 1;//向左移动一位二进制位,右边补0
//00000000000000000000000000000010-b的值
printf("%d", b);//由此得出b=2
return 0;
}
负数:
int main()
{
int a = -1;
//10000000000000000000000000000001-原码
//11111111111111111111111111111110-反码
//11111111111111111111111111111111-补码
int b = a << 1;//将a的补码向左移一位,右边补0
//11111111111111111111111111111110-b的补码
//11111111111111111111111111111101-b的反码
//10000000000000000000000000000010-b的原码
printf("%d", b);//由b的原码得知b=-2
return 0;
}
2.>>右移操作符:右移操作符有两种:
第一种:算术右移。右边丢弃,左边用原来的符号位来填充。
第二种:逻辑右移。右边丢弃,左边直接用0补充。
到底我们的编译器到底是那种右移呢?我们不妨来实验一下,这里我们只能用负数来验证,如果是正数,左边都是补的0,看不出差别。
int main()
{
int a = -5;
//10000000000000000000000000000101-原码
//11111111111111111111111111111010-反码
//11111111111111111111111111111011-补码
int b = a >> 1;
//算数右移:
//11111111111111111111111111111101-补码
//11111111111111111111111111111100-反码
//10000000000000000000000000000011-原码
//得出b=-3
//逻辑右移:
//01111111111111111111111111111101-补码
//01111111111111111111111111111100-反码
//00000000000000000000000000000011-原码
//得出b=3
printf("%d", b);
return 0;
}
由结果可知,右移操作符执行的是算术右移,即右边丢弃,左边用原来符号位来填充。
三.位操作符:
& 按位与
| 按位或
^ 按位异或
位操作符都是两个数的补码进行计算。
& 按位与:全1为1,不同为0
| 按位或:有1为1,全0为0
^ 按位异或:相同为0,不同为1
按位与:&
int main()
{
int a = 3;
//00000000000000000000000000000011
int b = -5;
//10000000000000000000000000000101-原码
//11111111111111111111111111111010-反码
//11111111111111111111111111111011-补码
int c = a & b;//补码进行运算
//00000000000000000000000000000011
//11111111111111111111111111111011
//00000000000000000000000000000011-c的补码
//因为最高位是0,即c为整数,原反补码相同
printf("%d", c);//c=3
return 0;
}
按位或:|
int main()
{
int a = 3;
//00000000000000000000000000000011
int b = -5;
//10000000000000000000000000000101-原码
//11111111111111111111111111111010-反码
//11111111111111111111111111111011-补码
int c = a | b;//补码进行运算
//00000000000000000000000000000011
//11111111111111111111111111111011
//11111111111111111111111111111011-c的补码
//11111111111111111111111111111010-c的反码
//10000000000000000000000000000101-c的原码
printf("%d", c);//c=-5
return 0;
}
按位异或:^
int main()
{
int a = 3;
//00000000000000000000000000000011
int b = -5;
//10000000000000000000000000000101-原码
//11111111111111111111111111111010-反码
//11111111111111111111111111111011-补码
int c = a ^ b;//补码进行运算
//00000000000000000000000000000011
//11111111111111111111111111111011
//11111111111111111111111111111000-c的补码
//11111111111111111111111111110111-c的反码
//10000000000000000000000000001000-c的原码
printf("%d", c);//c=-8
return 0;
}
按位异或结论:两个相同的数异或,结果为0。任何数异或0,结果为0。
我们学习位操作符有什么具体的用处吗?
当我交换两个整数时,可以使用异或操作符来实现:
常规方法:
int main()
{
int a = 3;
int b = 5;
printf("交换前:a=%d,b=%d\n", a, b);
int temp = a;
a = b;
b = temp;
printf("交换后:a=%d,b=%d", a, b);
return 0;
}
按位异或:
int main()
{
int a = 3;
int b = 5;
printf("交换前:a=%d,b=%d\n", a, b);
//用上异或的结论
a = a ^ b;
b = a ^ b;//b=a^b^b=a
a = a ^ b;//a=a^b^a=b
printf("交换后:a=%d,b=%d", a, b);
return 0;
}
四.赋值操作符:
赋值就是将常量的值赋值给变量。
int main()
{
int a = 1;
int b = 2;
int c = b = a + 1;//将a+1的值赋值给b,b再赋值给c
//我们不妨写成b=a+1;c=b;这样看起来更加清楚
return 0;
}
复合赋值符:
int main()
{
int a = 3;
a = a + 2;
//a+=1;
a = a - 2;
//a-=1;
a =a*2;
//a*=2;
a = a % 2;
//a%=2;
//它们都是等价的
//还有^=,&=,>>=,<<=等等
return 0;
}
五.单目操作符:
1.单目操作符的介绍:
! 逻辑反操作
- 负值
+ 正值
& 取地址
~ 按位取反
-- 前置,后置--
++ 前置,后置++
* 间接访问操作符(解引用操作符)
sizeof 求字节长
!逻辑反操作:
int main()
{
int a = 1;
if (a)//a为真就执行下面的语句
{
printf("hehe\n");
}
if (!a)//a为假时,!a就为真,执行下面的语句
{
printf("haha\n");
}
return 0;
}
*解引用,&取地址:
int main()
{
int a = 1;
int* pa = &a;
//地址用指针接收
printf("a的地址为%x\n", &a);
printf("a的值为%d\n", *pa);
//*pa解引用找到pa地址指向的值
return 0;
}
~按位取反操作符:
int main()
{
int a = 1;
//00000000000000000000000000000001-a的补码
int b = ~a;//按位取反,包括符号位
//11111111111111111111111111111110-b的补码
//11111111111111111111111111111101-b的反码
//10000000000000000000000000000010-b的原码
printf("%d", b);//所以b=-2
return 0;
}
前置--和后置--,前置++和后置--:
当我们写成--a时,就是前置--,我们应当先把a-1了之后,再使用a的值。
而后置就是完全相反,当我们写成a--时,就是后置--,我们应当先把使用a的值,再把a-1赋值给a。
int main()
{
int a = 2;
int b = a--;
//先使用a的值赋值给b,再将a-1的值赋值给a
//所以a=1,b=2
printf("a=%d b=%d",a, b);
return 0;
}
int main()
{
int a = 2;
int b = --a;
//先使用a-1的值赋值给b,再将a-1的值赋值给a
//所以a=1,b=1
printf("a=%d b=%d",a, b);
return 0;
}
2.sizeof和数组:
int main()
{
int a = 1;;
printf("%d\n", sizeof(int));//int类型,4个字节
printf("%d\n", sizeof(a));
int arr[10] = { 0 };
printf("%d\n", sizeof(arr));
printf("%d\n", sizeof(arr[1]));
int sz = sizeof(arr) / sizeof(arr[0]);
printf("%d\n", sz);
return 0;
}
void test1(int arr[])
{
printf("%d\n",sizeof(arr));//这里也是40吗
}
void test2(char ch[])
{
printf("%d\n", sizeof(ch));//这里也是10吗
}
#include<stdio.h>
int main()
{
int arr[10] = { 0 };
char ch[10] = { 0 };
printf("%d\n", sizeof(arr));
//这里sizeof中的arr整个数字,所以是40
printf("%d\n", sizeof(ch));//这里是10
test1(arr);
test2(ch);
return 0;
}
上面函数部分sizeof求出来的为什么和主函数的不一样呢?因为函数传参的时候,传的是地址,应该用指针接收,但是可以写成这种数组的形式,只是为了方便观察,所以这里求得是指针的大小,在x86平台是4个字节,在x64平台是八个字节。
六.关系操作符:
>
>=
<
<=
==
!=
我们在条件语句中,写的就是关系操作符,如:
int main()
{
int a = -1;
if (a ==1)//判断a是否和1相等
//不能写成if(a=1)
{
printf("hello");
}
return 0;
}
七.逻辑操作符:
&& 逻辑与
| | 逻辑或
这里要区分逻辑操作符和位操作符,这两者是没有关系的,逻辑操作符只判断真假。
int main()
{
int a = 2&&3;
//2和3都为真,逻辑与起来也是真,所以a=1
printf("%d\n", a);
int b = 1 || 0;
//1为真,0为假,逻辑或起来为真,所以b=1
printf("%d\n", b);
int c = 5 && 0;
//5为真,0为假,逻辑与起来为假,所以c=0
printf("%d\n", c);
return 0;
}
我们来看一道笔试题:
int main()
{
int i = 0, a = 0, b = 1, c = 2, d = 4;
i = a++ && ++b && c++;
//这里a++为0
printf("a=%d b=%d c=%d d=%d", a, b, c, d);
return 0;
}
如果我们不是很仔细或者不了解逻辑与&&的含义,这道题很有可能就会做错。
错误理解:
首先我们a++,先使用a,再++,所以打印出来a=1;
接下来++b,先把b+1赋值给b,再使用b,所以打印出来b=2;
然后c++,先使用c,再++,所以打印出来c=3;
最后打印出来就是1 2 3 4
结果却是1 1 2 4,这是为什么呢?这里我们就要理解逻辑与&&的作用了,在a++时,为后置++,先使用再变值,所以表达式中a++为0,逻辑与上后面的表达式为假,所以代码压根就不会执行后面的代码。所以我们一定要清楚每一个符号的含义。
八.条件操作符:
exp1?exp2:exp3
这其实就是三目操作符,当exp1为真时,执行exp2,否则执行exp3
我们可以用三目操作符来比较两个数的最大值:
int main()
{
int a = 5;
int b = 3;
int max = a > b ? a : b;
printf("%d", max);//max=5
return 0;
}
九.逗号表达式:
逗号表达式:就是用逗号隔开的多个表达式,从左到右依次计算,最后的结果是最后一个表达式的结果。
int main()
{
int a = 1;
int b = 3;
int c = (a++, a=b-2, a + 5);
//a++=1,然后a=2,之后a=3-2=1;所以a+5=6
printf("%d\n", c);//c就是6
return 0;
}
一些比较复杂的代码,我们有时可以使用逗号表达式来代替。
int main()
{
int a = 10;
a = test1();
test2(a);
while (a)
{
a = test1();
test2(a);
}
return 0;
}
替换:
int main()
{
int a = 10;
while (a = test1(), test2(), a)
{
;
}
return 0;
}
十.下标引用,函数调用和结构成员:
1.[ ]下标引用操作符:
操作数:一个数组名和一个下标数。
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
printf("%d", arr[5]);
//这里的arr和5就是操作数,使用下标操作符访问到下标为5的元素
return 0;
}
2.( )函数调用操作符:
接受一个或多个操作数:第一个操作数是函数名,剩余的操作数就是传递个函数的参数。
int Add(int x, int y)
{
return x + y;
}
int main()
{
int a = 3;
int b = 4;
int num = Add(a, b);//( )就是函数调用操作符
//Add和参数a,b就是操作数
printf("%d\n", num);
return 0;
}
3.访问结构体成员:
1.结构体变量.结构体成员名
2.结构体指针->结构体成员名
struct s
{
int num;
char b;
double c;
};
void test(struct s* pS)//参数为地址,用指针接收
{
printf("%d %c %.2lf\n", pS->num, pS->b, pS->c);
//用结构体指针来访问结构体成员使用->
}
int main()
{
struct s S = { 10,'B',3.14 };//用大括号
//S是结构体变量
printf("%d %c %.2lf\n", S.num, S.b, S.c);
//用结构体变量来访问结构体成员使用.
test(&S);
return 0;
}
这就是全部的内容,希望能给你带来一点点收获。还望一键三连。感谢!