文章目录
1. 算数操作符(+、 -、 *、 /、 %)
在算数操作符中 +、 -、 * 和在我们平时在数学上所学到的运算法则都是相同的
#include<stdio.h>
int main()
{
int a = 3;
int b = 8;
int sum = 0;
//a与b相加
sum = a + b;
//a与b相减
sum = a - b;
//a与b相乘
sum = a * b;
return 0;
}
不同的是 /、 % ,所以接下来我们一起来看一看这两个操作符的具体用法吧
1.1 /(除法) 操作符的使用
对于 / 来说不是我们所熟悉的,但是换一下表达方式就明白了,就是 ÷(除法),哎 / 这个操作符就是我们的除法操作符,它的用法有点区别,分为两种
第一种,就是 / 左边的数大于右边,则得出的结果是取商的那部分,相反的,若 / 左边的数小于右边,则得到的结果不是小数而是为0
#include <stdio.h>
int main()
{
int a = 7;
int b = 2;
int sum = a / b;
printf("%d ", sum);//此时打印出来的结果为3
//7除以2 商3余1
a = 2;
b = 7;//将a b重新赋值
sum = a / b;
printf("%d ", sum);
//此时打印出来的结果就是0
return 0;
}
第二种,就是 / 两边有一边是浮点数
浮点数又是什么呢,也就是我们常说的小数嘛,对吧,很简单的
在除法运算中,只要有一个操作数是浮点数,就是浮点数的除法运算,所谓的浮点数除法运算就是,最后的结果是一个浮点数
#include <stdio.h>
int main()
{
double sum = 7 / 2.0;
printf("sum = %lf \n", sum);
//此时打印出来的结果为3.5
//1f 就是保留一位小数
sum = 2 / 7.0;
printf("sum = %lf \n", sum);
//此时打印出来的结果是0.2
return 0;
}
1.2 %(取余) 操作符的使用
取模操作符,取的是一个数除另外一个直至除不尽的余数,若取模对象刚好是自己的公约数,则结果为0
例如 5%1=0 5%2=1 5%3=2 5%4=1 5%5=0
需要注意的是取模的对象不能为0,且不能为浮点型,必须为整数
#include <stdio.h>
int main()
{
int a, b, sum;
scanf("%d %d", &a, &b);
sum = a % b;
printf("%d", sum);
//若a=5 b=1 则sum=0
//若a=5 b=2 则sum=1
//若a=5 b=3 则sum=2
//若a=5 b=4 则sum=1
//若a=5 b=5 则sum=0
return 0;
}
2. 移位操作符( << >> )
在正式介绍移位操作符之前我们需要了解一下原码,反码和补码
2.1 原码,反码和补码
整数的2进制表示方法有三种,即原码、反码和补码
有符号整数的三种表示方法均有符号位和数值位两部分,2进制序列中,最高位的1位是被当做符号位,剩余的都是数值位
符号位都是用0表示“正”,用1表示“负”
正整数的原、反、补码都相同
负整数的三种表示方法各不相同
原码:直接将数值按照正负数的形式翻译成二进制得到的就是原码
反码:将原码的符号位不变,其他位依次按位取反就可以得到反码
补码:反码+1就得到补码
例如:
10 - 原码:00000000000000000000000000001010
反码:00000000000000000000000000001010
补码:00000000000000000000000000001010
-10 - 原码:10000000000000000000000000001010
反码:11111111111111111111111111110101
补码:11111111111111111111111111110110
这里我们还要补充一个知识点
我们认为对于整形来说:数据存放内存中其实存放的是补码
这是为什么呢
在计算机系统中,数值一律用补码来表示和存储。原因在于,使用补码,可以将符号位和数值域统一处理;同时,加法和减法也可以统一处理(CPU只有加法器)此外,补码与原码相互转换,其运算过程是相同的,不需要额外的硬件电路
简单来说,就是计算机只会进行加法运算,那怎么才能减法运算呢,哎,我们说了,7 - 2 ,可以看做 7 + (-2) , -2就可以用补码表示,所以数据存放内存中其实存放的是补码
2.2 << 左移操作符
这里又需要注意的是移位操作符的操作数只能是整数
移位规则:左边抛弃、右边补0
#include <stdio.h>
int main()
{
int num = 10;
int n = num<<1;
printf("n= %d\n",n);
printf("num= %d\n", num);
return 0;
}
2.3 >> 右移操作符
移位规则:首先右移运算分两种:
1.逻辑右移:左边用0填充,右边丢弃
2.算术右移:左边用原该值的符号位填充,右边丢弃
#include <stdio.h>
int main()
{
int num = -1;
int n = num<<1;
printf("n= %d\n",n);
printf("num= %d\n", num);
return 0;
}
警告:对于移位运算符,不要移动负数位,这个是标准未定义的
例如:
int num = 10;
num>>-1;//error
3. 位操作符(&、|、^、~)
位操作符有:
& – 按位与 – 有0为0,全为1为1
| – 按位或 – 有1为1 ,同0为0
-
^ – 按位异或 –
相同为0,相异为1
- – 按位取反 – 0变成1 ,1变成0
这里大家也需要注意一下,他们的操作数必须是整数
是吧,很简单,咱们直接上代码理解一下
#include <stdio.h>
int main()
{
int num1 = -3;
int num2 = 5;
printf("%d\n", num1 & num2);
//10000000000000000000000000000011 - 3的原码
//11111111111111111111111111111101 - 3的补码
//00000000000000000000000000000101 - 5的原码
//00000000000000000000000000000101 - 5的补码
//00000000000000000000000000000101 - 按位与之后的补码
//00000000000000000000000000000101 - 按位与之后的原码,5
printf("%d\n", num1 | num2);
//11111111111111111111111111111101 - 3的补码
//00000000000000000000000000000101 - 5的补码
//11111111111111111111111111111101 - 按位或之后的补码
//10000000000000000000000000000011 - 按位或之后的原码,-3
printf("%d\n", num1 ^ num2);
//11111111111111111111111111111101 - 3的补码
//00000000000000000000000000000101 - 5的补码
//11111111111111111111111111111000 - 按位异或之后的补码
//10000000000000000000000000001000 - 按位异或之后的补码,-8
printf("%d\n", ~0);
//00000000000000000000000000000000 - 0的原码
//00000000000000000000000000000000 - 0的补码
//111111111111111111411111111111111 - 0按位取反之后的补码
//10000000000000000000000000000001 - 0按位取反之后的原码,-1
return 0;
}
有小伙伴就要问了有什么用,别急,我们先看一道变态的题目
⼀道变态的题目:
不能创建临时变量(第三个变量),实现两个数的交换
我相信小伙伴们,对于实现两个数的交换很熟悉了,就是
int tmp = a;
a = b;
b = tmp;
很简单,对不对,但是不能创建临时变量,我们应该怎么做呢,这就需要用到我们的 ^ 按位异或
在给出答案之前,我们需要知道两个基础的公式
a ^ 0 = a;
a ^ a = 0;
#include <stdio.h>
int main()
{
int a = 10;
int b = 20;
a = a^b;
b = a^b;//(a ^ b) ^ b = a ^ b ^ b = a
a = a^b;//(a ^ b) ^ a = a ^ b ^ a = b
printf("a = %d b = %d\n", a, b);
return 0;
}
这样是不是就很轻松的不创建临时变量进行两个数的交换,小伙伴们学到了吗
如果学到的话,咱们来继续看几个练习吧,没学到的话,那就通过一些练习慢慢学吧
练习1:编写代码实现:求⼀个整数存储在内存中的⼆进制中1的个数
我们来想一想,假如是一个10进制的数字,怎么算
假如我给一个十进制的数字 1010 ,然后让我们求1的个数
是不是
1010%10 = 0
1010/10 = 101
先算个位上的数字,然后再算更高位的,对吧,这是算十进制数字的基本思路
然后我们接下来想想二进制的数字怎么办
还是 1010 ,不过这次是二进制的数字
1010%2 = 0
1010/2 = 101
是吧,很顺理成章吧,还是很容易想到的
你们加上一点while循环的小细节,代码就写出来了
#include <stdio.h>
int main()
{
int num = 1010;
int count = 0;//计数
while(num)//num不为0,就进入循环
{
if(num%2 == 1)
count++;
num = num/2;
}
printf("⼆进制中1的个数 = %d\n", count);
return 0;
}
有没有问题,其实再仔细想想看的话,还是有点小问题的
假如我要求 -1 二进制中的1的个数怎么办
前面我们已经提到了计算机存储数据的时候,存的是数据的补码
而 -1 的补码是 11111111111111111111111111111111
也就是32个1,这时怎么办
我们就要用到一个谷歌的算法
num = num&(num-1);
什么意思
假如 num = 1010
num - 1 = 1001
1010 & 1001 = 1000
这样从右往左的第一个1就被提出来了,有些小伙伴就不行,那我们再来看看
书接上回 num = 1000
num - 1 = 0111
1000 & 0111 = 0000;
怎么样,这下行了吧,然后我们再在原有的代码上这么一改就行了,好好体会一下这个算法吧
#include <stdio.h>
int main()
{
int num = -1;
int i = 0;
int count = 0;//计数
while(num)
{
count++;
num = num&(num-1);
}
printf("⼆进制中1的个数 = %d\n",count);
return 0;
}
4. 单目操作符(!、++、–、&、*、+、-、 ~、sizeof、(类型))
单⽬操作符的特点是只有⼀个操作数
-
!:逻辑反操作符 、
-
-:负值
-
+:正值
-
& :取地址
-
sizeof 操作符
-
++:自增
-
– :自减
-
*:解引用操作符
-
(类型):强制类型转换
这么多的操作符,有一部分已经讲过了,&和*,这2个操作符,我们会放在学习指针的时候讲到
那我们说说 ++、–、 (类型) 这三个单目操作符吧
4.1 ++(自增)
前置++:先++后使用(先让变量自增1,再使用)
后置++:先使用后++(先使用,再让变量自增1)
这个很好理解吧
来几个代码看看吧
#include <stdio.h>
int main()
{
//前置++
int a = 10;
int b = ++a;
//分为两步
// a=a+1;
// b=a;
// 先让a自增1 再使用a 把a的值赋给b
printf("%d\n", b);//b的值为11
//后置++
int c = 20;
int d = c++;
//分为两步
// d=c;
// c=c+1;
//先使用c 把c的值赋给d 再让c自增1
printf("%d\n", d);//d的值为20
return 0;
}
4.2 --(自减)
前置–:先–,后使用 (先让变量自减1,再使用)
后置–:先使用,后-- (先使用,再让变量自减1)
这个也很好理解吧
来几个代码看看吧
#include <stdio.h>
int main()
{
//前置--
int a = 10;
int b = --a;
//此代码执行了两部操作
// 第一步:a=a-1;
// 第二步:b=a;
printf("%d\n", b);//b的值为9
//后置--
int c = 20;
int d = c--;
//此代码也执行了两步操作
// 第一步:d=c;
// 第二步:c=c-1;
printf("%d\n", d);//d的值为20
return 0;
}
4.3 (类型)(强制类型转换)
强制类型转换就是将一种类型的数据,强制转换成另一种类型的数据
注意:当大类型往小类型转换时会发生截断
为什么是截断呢
当 float 或者 dauble 类型强转为 int 类型时: 发生截断 取到的是整数部分
当 char 类型 强转为 int 类型时:取到的是其对于的 ACSII码值
不好理解?来几个代码看看
#include <malloc.h>
#include <stdio.h>
int main()
{
// float 转 int 截断取整数部分
float a = 3.14f;
int b = (int)a;
printf("%d\n", b);// b为3
// double 转 int 截断取整数部分
double d = 4.225;
int e = (int)d;
printf("%d\n", e);// e为4
// char 转 int 取到ASCLL值
char c = 'A';
int f = (int)c;
printf("%d\n", f);// f 为 65
return 0;
}
5. 逗号表达式
表达式1,表达式2,……,表达式n
逗号表达式,就是⽤逗号隔开的多个表达式
逗号表达式,从左向右依次执行,整个表达式的结果是最后⼀个表达式的结果
来几个代码吧
#include <stdio.h>
int main()
{
int a = 1;
int b = 2;
int c = (a>b, a=b+10, a, b=a+1);
// 1, 12, 12, 13
printf("%d\n",c);//13
return 0;
}
很简单吧,小伙伴们想对了吗
我再给几个代码,小伙伴们下去在看看吧
//代码1
if (a =b + 1, c=a / 2, d > 0)
//代码2
a = get_val();
count_val(a);
while (a > 0)
{
//业务处理
a = get_val();
count_val(a);
}
//如果使⽤逗号表达式,改写:
while (a = get_val(), count_val(a), a>0)
{
//业务处理
}
6. 下标访问[]、函数调用()
6.1 [ ] 下标引用操作符
操作数:⼀个数组名 + ⼀个索引值
int arr[10];//创建数组
arr[9] = 10;//实⽤下标引⽤操作符。
[ ]的两个操作数是arr和9。
#include <stdio.h>
int main()
{
int arr[5] = { 1,2,3,4,5 };
//下标引用
arr[0] = 8;
//打印数组
int i = 0;
for (i = 0; i < 5; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
6.2 ()函数调用操作符
接受⼀个或者多个操作数:第⼀个操作数是函数名,剩余的操作数就是传递给函数的参数
#include <stdio.h>
void test1()
{
printf("hehe\n");
}
void test2(const char *str)
{
printf("%s\n", str);
}
int main()
{
test1(); //这⾥的()就是作为函数调⽤操作符。
test2("hello qiken");//这⾥的()就是函数调⽤操作符。
return 0;
}
7. 操作符的属性:优先级、结合性
C语⾔的操作符有2个重要的属性:优先级、结合性,这两个属性决定了表达式求值的计算顺序
7.1 优先级
优先级指的是,如果⼀个表达式包含多个运算符,哪个运算符应该优先执⾏。各种运算符的优先级是不⼀样的
3 + 4 * 5;
上⾯⽰例中,表达式 3 + 4 * 5 ⾥⾯既有加法运算符 +,⼜有乘法运算符 * ,由于乘法的优先级⾼于加法,所以会先计算 4 * 5 ,⽽不是先计算 3 + 4
和我们在数学中的运算法则有点像,但还是有区别的
7.2 结合性
如果两个运算符优先级相同,优先级没办法确定先计算哪个了,这时候就看结合性了,则根据运算符是左结合,还是右结合,决定执⾏顺序。⼤部分运算符是左结合(从左到右执行),少数运算符是右结合(从右到左执行),⽐如赋值运算符=
5 * 6 / 2;
上面示例中, * 和 / 的优先级相同,它们都是左结合运算符,所以从左到右执行,先计算 5 * 6 ,再计算 6 / 2
运算符的优先级顺序很多,下面是部分运算符的优先级顺序(按照优先级从高到低排列),建议⼤概记住这些操作符的优先级就行,其他操作符在使用的时候查看下面表格就可以了
好了,我们到这里也结束了,其实还有几个重要的知识点还没讲到,比如,整形提升,算术转换,对于没有讲到的知识点,我现在还是蒙的,讲的话也解释不清楚,索性就不讲了,感兴趣可以自己下去了解,这里就抱歉了
最后,感谢你能看到这里,希望这篇文章对你有用,溜了溜了,我们下个星期再见吧