大家好,咱们又见面啦,今天咱们要分享的是操作符的相关内容。
操作符
操作符的分类
1.算术操作符:“+”、“-”、“*”、“/”、“%”
2.移位操作符:
“>>”——右移
"<<"——左移
3.位操作符:
"&"——按位与
“|”——按位或
“^”——按位异或
4.赋值操作符:“=”、“+=”、“-=”、“*=”、“/=”、“&=”、“^=”、“|=”、“>>=”、“<<=”
5.单目操作符:
“!”——逻辑反操作
“-” ——负值
“+”——正值
“&”——取地址
“sizeof” ——操作数的类型长度(以字节为单位)
“~”——对一个数的二进制按位取反
“--”——前置、后置--
“++”——前置、后置++
“*” ——间接访问操作符(解引用操作符)
“(类型)”——强制类型转换
6.关系操作符:
“>”——大于
“>=” ——大于等于
“<”——小于
“<=”——小于等于
“!=” ——用于测试“不相等”
“==”——用于测试“相等”
7.逻辑操作符:
“&&”——逻辑与
“||” ——逻辑或
8.条件操作符:exp1 ? exp2 : exp3
9.逗号表达式:exp1, exp2, exp3, ……, expN
10.下标引用、函数调用和结构成员:“[]”、"()"、"."、"->"
大家看到上面这些分类可能一脸懵,啊??这都什么跟什么啊?完全看不懂。没关系,我现在把我对这些操作符的理解来分享给大家。
1.算术操作符
算术操作符想必大家都不陌生,就是简单的加减乘除,这个我就不多介绍了,但是这个“%”是什么意思?这里的“%”叫做取模,什么是模?这里我们可以简单的理解为就是取余数,比如5/2=2……1,这里的余数1就是模。口说无凭,下面我们来验证一下:
这里我们可以看到a和b的值分别是5/2的整数部分2与余数部分1,我相信大家现在应该就明白了,这里“%”的用法和加减乘除一样,一个数a%另一个数b,就能算出a除以b的余数了。
2.移位操作符
移位操作符,这里对我们来说还是比较陌生的,他这个移位是什么发生了移位呢?这个就是我们现在要探讨的问题。这里我举个例子:
大家还记得int的大小吗?比较陌生的话可以回顾一下我的第三篇博客——计算机萌新的成长历程——初识C语言3,这里面有详细介绍不同数据类型的大小哦。这里我就直接说结论了,int的大小是4个字节大小,也就是32个比特位,那我们用二进制序列表示的话就是:“0000 0000 0000 0000 0000 0000 0000 0000”(这里中间是没有空格的,我是为了方便大家阅读,所以4个为一组,加空格分开了)
下面如果我定义int a = 1;那这个a用二进制序列是怎么表示的呢?如下所示:
“0000 0000 0000 0000 0000 0000 0000 0001”
这里我们可以看到,这32个比特位最右边的0变成了1,它这时转化成十进制就是1*2^0=1,那如果我现在将a左移一位会发生什么呢?接下来我们来测试一下:
这里我们可以看到,a它本身在经过左移后它是不变的,也就是说它的二进制序列也不会发生改变,但是将它左移后的值赋值给b后,我们会发现这个值变成了2,此时的二进制序列是“0000 0000 0000 0000 0000 0000 0000 0010”,这里我们有发现吗?对了,我们发现这个1的位置发生了变化,它往左边移动了,那如果我把b右移一位呢?下面我们继续测试:
大家可以看到这时的c又变成了1,那说明它的二进制序列也变成了“0000 0000 0000 0000 0000 0000 0000 0001”,看到这里我们就可以得出结论:
位移操作符移动的是二进制序列位,左移就是二进制序列往左边移动,右移就是二进制序列往右边移动
最后我们通过测试来验证一下这个结论:
我们将它们的二进制序列分别表示出来:
a:“0000 0000 0000 0000 0000 0000 0000 0001”——1*2^0=1//转化为十进制
b:“0000 0000 0000 0000 0000 0000 0000 0100”——1*2^2+0*2^0+0*2^1=4//转化为十进制
c:“0000 0000 0000 0000 0000 0000 0000 0010”——1*2^1+0*2^0=2//转化为十进制
现在我们不仅进一步验证了我们的猜想,而且能够更加直观的看到在经过左移右移后这个二进制位的具体变化。相信大家应该都能理解了——位移操作符移动的是二进制序列位,移动完后,被移动的对象本身不会发生变化,变化的是它移动后的值。
3.位操作符
位操作符:这里操作的还是二进制位。那这些符号分别是怎么操作的呢?下面我们通过5和3这两个十进制数的二进制位来操作一下,方便大家更好的理解:
这里我们可以看到它们计算后的值分别是1/7/6,下面我们分别把5/3以及1/6/7的二进制序列位写出来
5:“0000 0000 0000 0000 0000 0000 0000 0101”——1*2^2+0*2^1+1*2^0=5//转化为十进制
3:“0000 0000 0000 0000 0000 0000 0000 0011”——0*2^2+1*2^1+1*2^0=3//转化为十进制
1:“0000 0000 0000 0000 0000 0000 0000 0001”——0*2^2+0*2^1+1*2^0=1//转化为十进制
7:“0000 0000 0000 0000 0000 0000 0000 0111”——1*2^2+1*2^1+0*2^0=6//转化为十进制
6:“0000 0000 0000 0000 0000 0000 0000 0110”——1*2^2+1*2^1+1*2^0=7//转化为十进制
这里我们可以观察到哪些变化呀?如下图:
这里我解释一下,为了方便我们理解,这里我们把1作为正确结果,把0作为错误结果。
- 按位与“&”:两个数对应的二进制位都正确则输出结果为1,否则输出结果为0;
- 按位或“|”:两个数对应的二进制位只要有一个正确则输出结果为1,否则输出结果为0;
- 按位异或“^”:两个数对应的二进制位只要不相同则输出结果为1,否则为0。
这里大家注意标红的字与位操作符的对应关系,别弄错咯!!!这里的按位与符号我给他加了下划线,是因为它还有另一个含义——取地址。什么是取地址呢?别着急,后面我们会介绍到,下面我们先来介绍一下单目操作符;
4.单目操作符
我们先了解一下什么是单目。
a + b//这里的"+"是一个操作符,它的左右两边都有一个操作对象,这里我们管“+”叫做双目操作符;
int a = +10//这里的“+”只有一个操作对象,这里我们管它叫单目操作符;
//不管是单目也好、双目也好还是三目也好,这里的目数都是指操作对象的个数。
接下来我们要牢记一个规定:
- 在C语言中我们在表示真假时,“0”表示假,一切的非0表示真。
“!”——逻辑反操作符
我们开始进入正题,今天我们介绍第一种单目操作符——"!"逻辑反操作符。这里测试代码如下:
#define _CRT_SECURE_NO_WARNINGS 1//用来解决在VS编译器中对于scanf、strcpy、strlen、strcat……这些C语言提供的不安全的库函数报错的问题
#include <stdio.h>
#include<string.h>//这里在使用string相关的库函数如:strlen、strcpy、strcat...等函数时需要引用此头文件。
int main()
{
int a = 5;//定义整型a并将5赋值给a;
int b = !a;//定义整型b并将逻辑反操作a的值赋值给b;
printf("%d\n%d\n", a, b);//分别将a、b的值以整数的形式打印出来;
printf("%d\n%d\n", !a, !b);//"!"逻辑反操作符——正确改为错误,错误改为正确;
return 0;
}
运行结果如下:
这里我们可以看到,正确值或者叫做真值5经过逻辑反操作后变成了假值0,假值0在逻辑反操作后变为了真值1而不是真值5,这里我们可以得到以下结论:
1.逻辑反操作符的作用就是改变真假,真值转变为假值,假值转变为真值;
2.在假值转变为真值时,它只会变成真值1。
下面我们来介绍第二个单目操作符——sizeof——计算变量/类型所占空间的大小,单位是字节;
“sizeof”——计算变量/类型所占空间的大小
这里我们用下面的代码进行测试:
#define _CRT_SECURE_NO_WARNINGS 1//用来解决在VS编译器中对于scanf、strcpy、strlen、strcat……这些C语言提供的不安全的库函数报错的问题
#include <stdio.h>
#include<string.h>//这里在使用string相关的库函数如:strlen、strcpy、strcat...等函数时需要引用此头文件。
int main()
{
char ch = 'a';
int b = 10;
printf("%d\n%d\n%d\n", sizeof(ch), sizeof(b), sizeof(char));
return 0;
}
运行结果如下:
这里我们可以看到sizeof不仅可以计算变量“ch”和变量“b”所占空间的大小,还可以计算数据类型“char”所占空间大小,这里变量所占空间的大小与其类型相同。这里就有个问题了,既然都是计算数据类型的大小,为什么还要特地把变量给拎出来呢?这里我们来探讨一下它们的区别,如下图:
这里了我们分别把变量的括号和数据类型的括号给去掉了,结果发现,变量去掉括号是可以正常计算,但是数据类型去掉括号会提示错误,这个就是它们两者的区别,别问我是怎么想到这个点的,问就是看视频上学的。
下面我们来探讨一下sizeof能否计算数组大所占空间大小,这里我们借助字符数组ch[10],代码如下:
#define _CRT_SECURE_NO_WARNINGS 1//用来解决在VS编译器中对于scanf、strcpy、strlen、strcat……这些C语言提供的不安全的库函数报错的问题
#include <stdio.h>
#include<string.h>//这里在使用string相关的库函数如:strlen、strcpy、strcat...等函数时需要引用此头文件。
int main()
{
char ch[10] = { '0' };
printf("%d\n%d\n", sizeof(ch), sizeof(char));
return 0;
}
这里我们分别计算了字符数组ch与字符类型char的所占空间大小,结果如图所示:
由此我们可以得出以下结论:
1.sizeof除了可以计算变量、数据类型的所占空间大小外,还能计算数组的所占空间大小;
2.数组所占空间大小与其数据类型以及数组中的元素个数有关,这里因为是有10个元素的字符数组,所以它的大小为10*1=10个字节大小。
那我们可不可以通过字符所占空间大小来计算它其中的元素个数呢?下面我们来测试一下,代码如下:
#define _CRT_SECURE_NO_WARNINGS 1//用来解决在VS编译器中对于scanf、strcpy、strlen、strcat……这些C语言提供的不安全的库函数报错的问题
#include <stdio.h>
#include<string.h>//这里在使用string相关的库函数如:strlen、strcpy、strcat...等函数时需要引用此头文件。
int main()
{
char ch[10] = { '0' };
printf("%d\n%d\n", sizeof(ch), sizeof(char));
int a = sizeof(ch) / sizeof(ch[0]);
printf("%d\n", a);
return 0;
}
我们运行一下结果看看如何:
这里我们可以看到将数组大小/数组里的元素大小后得到的值赋给整型字符a后打印出来的结果就是10,这里我们就解锁了一种新的计算数组的元素个数的方式,通过sizeof来计算,说不定以后会遇到与之有关的题目,不管有没有用,先掌握了再说。
这里我们最后再提一下当“&”作为单目操作符时的用法
“&”——取地址操作符
这里我们要借助输入函数来进行理解:
#define _CRT_SECURE_NO_WARNINGS 1//用来解决在VS编译器中对于scanf、strcpy、strlen、strcat……这些C语言提供的不安全的库函数报错的问题
#include <stdio.h>
#include<string.h>//这里在使用string相关的库函数如:strlen、strcpy、strcat...等函数时需要引用此头文件。
int main()
{
int a = 0;
int b = 0;
scanf("%d%d", a, b);
printf("a=%d\nb=%d\n", a, b);
return 0;
}
这里我们尝试着运行一下,运行成功后输入1/2看看结果如何:
这里我们可以看到,系统报错了,报错内容如下:
调试断言失败!
程序:E1Visual Studiotest_23.08.22\Debugtest_23.08.22.exe文件:minkernel\crtsucrt\inclcorecrt_internal_stdio_inputh行号:1567
表达式:结果指针!=nullptr
有关程序如何导致断言失败的信息,请参见Visual C++关于断言的文档
(按“重试”调试应用程序)
这里说的乱七八糟的也看不懂,我们可以简单的理解为就是我们在输入值后,计算机有点迷茫了,因为他不知道它应该把这个输入的值如何处理,所以才会报错,我们本想把1赋值给a,把2赋值给b来着,但是计算机它不知道,这里我们就需要告诉他,计算机你要把1送到a的家里,把2送到b的家里,怎么送过去呢?它们的住址我是不是也一并的要告诉计算机啊,这里就需要用到我们的单目操作符“&”,它的作用就是把操作对象的地址告诉给计算机,让计算机能够找到被操作的对象,下面我们加上取地址符号再试一下:
这里我们就可以看到,计算机在有操作对象的地址后,很好的将对应的值送到了操作对象的家里,这个就是取地址的用法。下面我们把“&”的用法总结一下:
- 作为双目操作符时,它的作用是“按位与”;
- 作为单目操作符时,它的作用是取地址。
还有一些操作符在作为双目操作符时和单目操作符时它的作用不一样,比如“+”、“-”、“*”……这些内容我们以后会再提到,今天就不继续展开了。
结语
我们今天的内容到这里就结束了,如果这篇文章能够帮助到各位朋友更好的去理解对应知识点的话,那它的任务就完成了,接下来随着学习的深入,我会继续给大家分享我在学习过程中的感受,感谢各位朋友的翻阅,我们下一篇见。