大家好,很高兴又和你们见面啦!!!今天我们来继续探讨操作符的相关内容。
操作符
知识点回顾
在开始今天的内容前,咱们一起回顾一下上一篇中我们一起探讨的操作符的相关内容:
1.算术操作符
1在算术操作符中我们探讨了取模操作符——"%"——取两数相除的余数,如5%2=1;
2.位移操作符
在位移操作符中我们知道了它的位移是移动的二进制序列;
<<——左移就是二进制序列整体往左边移动;
>>——右移就是二进制序列整体往右移动。
这里如何区分左右,我是看的它的尖角朝向,尖角朝左就是左移,尖角朝右就是右移。
这里有一个点需要大家注意一下,
比如int a = 1
,则a的二进制序列为——|0000 0000 0000 0000 0000 0000 0000 0001|;
如果将它左移1位,它的序列则变成了——0|0000 0000 0000 0000 0000 0000 0000 001_|
这里大家可以看到这个||中间的区域是不会移动的,在左移右移时是二进制序列进行移动,这样就会产生一个问题,移动完后会有区域空出来,这个应该怎么办呢?
这里我是这样理解的:
这空出来的区域计算机是直接用0来填补,移动出区域的字符就被舍弃了,这了我们可以看到标红的这个0,它是直接被舍弃的,
1的旁边会被插入一个0,所以它的二进制序列就变成了|0000 0000 0000 0000 0000 0000 0000 0010|,
此时如果我们将移动后的值赋予b,也就是int b = a<<1
,此时b的值就会变成2,a的值不变,还是1;
如果我们是把a往右移动1位呢?它的序列会变成|_0000 0000 0000 0000 0000 0000 0000 000|1
这里我们可以看到1被移出了这个区域,最左边空了一个位置,这时计算机会在最左边自动填补一个0并将1给舍弃掉,所以它的二进制序列就变成了|00000 0000 0000 0000 0000 0000 0000 000|,此时我们将这个序列的值赋值给c,也就是int c = a>>1
,此时c的值就变成了0。下面我们来实际操作一下:
3.位操作符
在位操作符中我们学到了三种位操作符:&——按位与、|——按位或、^——按位异或。它们的计算是通过二进制序列的计算来实现的,在说到计算前,我们需要知道一个前提——在C语言中,0代表的是假值,非0代表真值。(注:上一篇中我是以错误和正确来代表0和1,这里算是我的一个个人习惯吧,切勿深究哦!)
- &——按位与——对应的二进制位只有两个值都为真的情况下,输出才为1,否则输出为0;
- |——按位或——对应的二进制位只要有一个值为真,输出就为1,否则输出为0;
- ^——按位异或——对应的二进制位只有一个值为真,另一个值为假,输出才为1,否则输出为0。
这里有一道题需要各位朋友来做一下:
int a = 1, b = 2, c;
c = a ^ b << 1;
printf("%d\n" ,c);求c的输出值。
4.单目操作符
在单目操作符中,首先我们介绍了“!”——逻辑反操作符——改变操作对象的真假:非零真值变为假值0,假值0变为真值1,如下所示:
int a = 123, b = 0;
!a = 0, !b = 1;
其次我们介绍了“sizeof”——计算变量/类型所占空间大小,我们还展开说明了几个点:
(1)sizeof在计算变量和计算类型所占空间大小时的区别——计算变量时可以不加括号,在计算类型时则需要加括号,如下:
int a ;
sizeof a;
sizeof(int);
(2)sizeof还能计算数组所占空间大小,并且这个大小与数组内的元素个数有关:
数组所占空间大小=元素个数*一个元素所占空间大小
这里我们还反推了计算元素个数——元素个数=数组所占空间大小/一个元素所占空间大小,如下所示:
int a[n];//这里的n是常量,不是变量
sizeof(a) = n * sizeof(a[0]);
n = sizeof(a)/sizeof(a[0]);
最后我们提到了“&”作为单目操作符时的用法——取地址。
接下来我们将继续探讨以下这些操作符:
新知识点介绍
单目操作符
“~”——对一个数的二进制序列按位取反
按位取反是什么意思呢?就比如说一个数的二进制序列为1010,我们将它按位取反后,序列将变成0101,也就是,二进制序列原本是1的变为0,原本是0的变为1,这就是按位取反。下面我们来实际操作一下:
诶!这里这个打印值为什么是-1?b的二进制序列这么多1不应该是1*2^0+1*2^1+……+1*2^31吗?
在解释这个问题之前我们要先提到几个知识点——1.原码、反码、补码;2.负数在内存中存储的时候,存储的是二进制的补码。
我们怎么来理解这个知识点呢?
- 我们要确定b = ~a;//这里的b是一个有符号的整型,当一个数为有符号的整型是,它的二进制序列的最高位就是它的符号位代表着它的正负:1——负号,0——正号。
- b作为一个有符号的整型它的二进制序列为|11111111111111111111111111111111|这里最高位上是1,也就是说b是负数,那它在存储的时候就是补码,如果我们要将其打印出来的时候,打印的是它的原码。那我们如何计算出它的原码呢?
这里我们要介绍一下原码、反码、补码的计算规则:原码符号位不变,其它位按位取反得到反码,反码加1得到补码。
这里我们已知了它的补码我们要求它的原码那我们只需要反过来:补码-1—>反码—>符号位不变,其它位按位取反—>原码,下面我们来操作一下:
11111111111111111111111111111111-1—>
11111111111111111111111111111110—>符号位不变,其它位按位取反—>
10000000000000000000000000000001—>转化为10进制也就是-1
这里我相信有一部分朋友应该理解了,不理解的朋友也没关系,现在咱们才是初识C语言,我们只需要对这些知识点有个大致印象就行,这些内容后面都会学到,我也会在学习到相关知识点后及时的将这些内容通过博客的形式分享出来。
我们接下来继续分享下一个知识点“--”,“++”,
“--”,“++”
这两个符号根据使用形式的不同分为前置--、后置--;前置++,后置++。为什么要把这两个符号放在一起说呢,因为它们的使用规则都是一样的,这里我们以“++”为例来说明它们的用法:
大家可以看到,b此时的值为a进行++之前a的值,d的值为c进行++之后的值,这里我们可能还不能理解为什么会这样,下面我们来监视一下它的一个操作流程:
运行开始,进入计算机世界,这时abcd都还是随机值;
这里abcd都要开始被定义了,但是此时的值是统一的,这个红色的负值,这里我查阅了相关资料得到的解释是它代表着程序中某个变量的地址,没有什么实际意义,这里我们就不深究了;
现在我们成功的将1赋值给了a,程序也走到了b=a++这一行;
这里大家可以看到b=a++是先把a的值也就是1赋值给b,然后再进行a++也就是a+1,此时a=2,b=1;
现在我们将1赋值给了c,程序也走到了++c这一行;
这里我们可以看到d=++c,是先进行++c也就是c+1,然后再将c+1的值赋值给d,所以此时c和d都为2。
这里我们可以得出结论:
- 前置++/前置--:是先++/--,再赋值;//++——+1,--——-1;
- 后置++/后置--:是先赋值,再++/--;
下面给大家分享一道题:
int x = 5, y = 5, i;
for( i = 0; x > 3; y = ++i)
printf("%d %d", x--, y);
求输出结果是多少?
int x = 5, y = 5, i;
for( i = 0; x > 3; y = ++i)
printf("%d %d", --x, y);
求输出结果是多少?
接下来我们继续分享下一个操作符(类型)——强制类型转换。
(类型)——强制类型转换
这里我们可以直接通过字面意思来理解这个操作符,强行将操作对象的类型进行转换,这里我们就不进行测试了,举个例子:
int a = (int)1.23//这里的1.23是浮点型,我通过(int)将它强制转换成了整型
对于这个操作符,大家只要知道它有这个用途就可以了,以后如果能不用就尽量不要使用。
下面我们来简单了解一下关系操作符——“>”、“>=”、“<”、“<=”、“!=”、"=="。
关系操作符
这些操作符是来干什么用的呢?答:它是来比较两个操作对象的大小关系的,如:
int a = 1;
int b = 2;
a < b;//a小于b
b > a;//b大于a
a! = b;//判断a与b是否不相等
int c = 3;
int d = 3;
c == d;//判断c与d是否相等
int a, b;
a <= b;//a小于等于b
a >= b;//a大于等于b
看到这里大家应该就能明白了吧,这里要注意的是!=与==这两个操作符,它们是来判断两个数之间的关系,并不能进行赋值操作,比如:a == 1//这里的意思是判断a是不是等于1,不是把1赋值给a;a != 2//意思是判断a是不是不等于2,并不是把2赋值给a。
接下来继续来分享逻辑操作符:&&——逻辑与、||——逻辑或
逻辑操作符
这里我们简单的理解就是&&——并且,||——或者。不知道大家还记不记得我们在计算机萌新的成长历程——初识C语言10中我们有探讨编写的比较三个数的大小的代码,有兴趣的朋友可以再回顾一遍。当时我们在编写时有用到if(a>b>c)这样的判断格式,测试结果告诉我们这样写格式是错误的,当时我们在一起查阅相关资料后进行了修改,改成了if(a>b && a>c && b>c)之后再运行,程序能正常比较三个数的大小了,我们今天就知道了这里的意思是a>b并且a>c并且b>c,意思就是判断是否同时满足这三个条件,都满足,则运行该条件下的指令,只要有一个不满足,则运行其它指令。那我们马上举一反三,如果我把&&改成||也就是if(a>b || a>c || b>c)那又是什么意思呢?这里改写后的意思是如果满足a>b或者a>c或者b>c三者中的任一一个条件,则执行该条件下的指令。我相信到这里大家应该都能理解了。
下面我们来聊聊这两个符号的运算规则:
前面我们有提到,在计算机的世界里,0为假,非0为真。那现在我们要计算下列的式子:
int a = 2;
int b = 3;
int c = a && b;
int d = a || b;
大家能告诉我c和d的值是多少吗?下面我们来测试一下:
大家可以看到输出结果都是1,这里的1代表的是什么呢?下面我们继续测试:
这里我们可以看到,此时的c变成了0,d不变,为什么会这样呢?我们继续测试:
我去,123和456进行逻辑与之后居然还是1,逻辑或任然没变,那如果是0和0结果又会怎么样?我们继续测试:
大家可以看到此时c和d都变成了0,经过这些测试我们发现,这个值经过逻辑与和逻辑或之后的值只有两个——0或者1。为什么会这样呢?下面我们再回过头来看这句话:在计算机的世界里,0为假,非0为真,那我们现在将这个观点带入进去再来分析一下:
第一种情况中2为真、3也为真,逻辑与后的值为1——真,逻辑或后的值为1——真;
第二种情况中0为假、5为真,逻辑与后的值为0——假,逻辑或后的值为1——真;
第三种情况中123为真、456为真,逻辑与后的值为1——真,逻辑或后的值为1——真;
第四种情况中0为假、0为假,逻辑与后的值为0——假,逻辑或后的值为0——假。
这里我们对逻辑操作符的运算做个总结:
- 逻辑与只有在两个值都为真的情况下输出为真,且这个值为1,否则为假,输出值为0;
- 逻辑或只要两个值中有一个为真,那它的输出值就为真,这个值是1,否则为假,输出值为0;
- 逻辑或和逻辑与是来判断两个操作对象的真假,输出结果只有两种情况:0代表假,1代表真。
这里要区分一下按位与——&和按位或——|,按位与和按位或的操作对象是二进制序列的真假,而逻辑与和逻辑或的操作对象是其值的真假。
下面我们继续探讨条件操作符(三目操作符):exp1?exp2:exp3
条件操作符
这个表达式怎么这么奇怪呢?它究竟是什么意思啊?下面我们来解释一下,上述的表达式表示的是,判断对象1是否成立,成立执行对象2,且对象2的值为整个表达式的值,否则执行对象3,对象3的值为整个表达式的值。
这里有几个点需要说明:
1.这里成立也可以说是结果为真,不成立就是结果为假,这个看你怎么去理解;
2.这里的对象1、对象2、对象3不一定是单一的对象,还有可能是一个表达式。
这里我们举几个列子来说明:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>
//exp1 ? exp2 : exp3
int main()
{
//比较两个值的大小
int a = 10;
int b = 20;
int max = (a > b ? a : b);//如果a>b,则max=a,否则max=b
//这里10>20不成立,所以这里执行的是max=b=20
printf("%d\n", max);
return 0;
}
这里我们看一下结果是不是这个样子:
这里我们可以看到exp2和exp3都是单一的对象a和b,我们接着看下一个例子:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>
//exp1 ? exp2 : exp3
int main()
{
//比较两个值的大小
int a = 10;
int b = 20;
/*int max = (a > b ? a : b);*/
int max = (a > b ? a + b : a * b);//如果a>b,则max=a+b,否则max=a*b;
//这里10>20不成立,所以max=a*b=10*20=200
printf("%d\n", max);
return 0;
}
我们看一下结果:
这里我们可以看到此时exp2为表达式a+b,exp3为表达式a*b,我们继续看下一个例子:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>
//exp1 ? exp2 : exp3
int main()
{
//比较两个值的大小
int a = 10;
int b = 20;
/*int max = (a > b ? a : b);*/
/*int max = (a > b ? a + b : a * b);*/
int c = a > b;
int max = (c ? a + b : a * b);
printf("%d\n", max);
return 0;
}
这里我们把a>b赋值给了c,现在直接运行看一下:
成功运行,这里的exp1为单一对象c,接下来我们来看最后一个例子:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>
//exp1 ? exp2 : exp3
int main()
{
//比较两个值的大小
int a = 10;
int b = 20;
/*int max = (a > b ? a : b);*/
/*int max = (a > b ? a + b : a * b);*/
int c = 5;
int max = (c ? a + b : a * b);
printf("%d\n", max);
return 0;
}
看一下运行结果:
这里可以看到max=a+b=10+20=30。
下面我们总结一下条件操作符:exp1?exp2:exp3的特点:
- exp1、exp2、exp3不仅可以代表单一对象,还可以代表达式;
- exp1?为一个判断语句,判断exp1的值是否为真,值为真,则执行exp2,且exp2的值为整个表达式的值,否则,执行exp3,且exp3的值为整个表达式的值;
- 条件操作符的操作对象是3个,所以也叫三目操作符。
接下来我们看看逗号表达式:exp1,exp2,……,expN
逗号表达式
这里我们只需要知道我们的对象或者表达式可以用逗号隔开,如:
我们看下一个例子:
接下来看第三个例子:
这里我们对逗号操作符做个总结:
- 同一类型的对象可以用“,”隔开,如int a, b, c;
- 同一类型的表达式可以用逗号隔开,如int d = 1, e = 2, f = 3;
- 定义不同类型时不能用逗号隔开,如int a, char b, float c; int a, int b, int c;
下面还有最后一个操作符:下标引用、函数调用和结构成员操作符
下标引用、函数调用和结构成员操作符
[]——下标引用操作符
在数组中我们有提到,比如int arr[4] = {1, 2, 3, 4},我这里要用下标表示数组里的元素比如arr[0]—>1//这里的[]就是下标引用操作符;
()——函数调用操作符
我们在函数中有提到过,比如我定义了一个函数A(int x, int y),我现在要在主函数里调用它,如:
int a, b, c;
c = A(a, b);//这里的()就是在调用我们定义的函数
现在还有“*”、“.”、“->”这3个操作符我们还没有提到,随着接下来学习的深入,我会继续给大家分享关于这些操作符的用法。
结语
操作符的内容咱们就基本上分享完了,不知道大家看完这两篇会不会有什么收获。最后感谢各位朋友的翻阅,随着学习的深入,我会继续给大家分享我在学习过程中的感受,咱们下一篇见。