目录
❤博主CSDN:啊苏要学习
▶专栏分类:C语言◀
C语言的学习,是为我们今后学习其它语言打好基础,C生万物!
开始我们的C语言之旅吧!✈
前言:
紧接上一篇,仍旧是来讲解操作符的使用。
链接:操作符讲解1---C语言
1.赋值操作符
赋值操作符是用来为变量进行赋值操作的(内存空间里放值),分为简单赋值操作符和复合赋值操作符。
- 简单赋值操作符:=
int main()
{
int a = 10;
a = 20;//这里的等号就是赋值操作符
return 0;
}
赋值可以让我们改变变量里我们原先不想要的值,把我们理想的值放到变量里面去。
- 复合赋值操作符:+=、-=、*=、/=、%=、>>=、<<=、&=、|=、^=。
正是因为有上面这些赋值操作符,才将=与上面这些赋值符划分为简单和复合类型。那么我们来认识和使用复合赋值操作符。
我们看a那一行代码,a本身加上10再赋值给a本身。屏幕上a和b的打印出来的值都是20,这说明+=这个复合赋值符是将一个数与b相加,再赋值给b。是的,所有的复合赋值操作符都是一个能进行运算的操作符配合简单赋值操作符,结合起来的先运算再赋值的形式。
2.单目操作符
单目操作符有:!、+、-、sizeof、~、&、*、--、++、(强制类型转换)
单目操作符的概念是,这个操作符的操作数只有一个(只对一个数据进行操作)。
!的操作数只有一个,它也是逻辑操作符,在上一篇我们讲过了,加上无非就是从真变假,从假变真的效果。
+(正号),这里不是加号的意思,而是正号。加号是双目操作符,一个正数前面写上+是没问题的,不过简直是多一举罢了,我们一般都不会用到这个操作符来表示正数。
-(符号),这个操作符能将正数变成负数,负数变成正数。比如int a = 1; a = -a; a的值被赋成了-1。
2.1取地址操作符
&(取地址操作符),由于我们在创建变量的时候,会向操作系统申请空间,也就是借用内存单元给我们使用,这些内存单元都有编号,为了取得这些编号,C语言用&取地址操作符取出变量所在的内存单元编号。
&取地变量址的时候,我们以上面的例子为例吧。在这个代码样例中,一个整型所占空间的大小是4个字节,由于每个内存单元的大小是1个字节,所以一个整型占了4个内存单元,而且每一个内存单元都有它自己的地址,那我们取这个整型的地址的时候,取得是哪个内存单元的地址呢?
答案是:这个变量起始位置的内存单元地址。这是因为,当我们标明了这个变量的数据类型时,我们只需要记住这个变量从哪里开始,编译器会自动根据数据的类型,往后访问相应大小的内存单元空间。
2.2sizeof操作符
大家可能在没完全了解sizeof的时候,就在用sizeof了。和大家对sizeof的认识一样,它是用来求变量或数据类型所占用内存单元的多少的,单位是字节,比如int占用4个字节,那用sizeof来求int创建的变量的时候,结果是4。
也许你会觉得,sizeof()这样子很像是一个函数调用,实际上不是的,我们看下面的代码:
这里我改了一下,把变量名括起来的括号给去掉了,然后结果还是正确的,如果sizeof是函数的话,调用函数的时候()(函数调用操作符),是不能省略的。 可能细心的小伙伴会发现,博主博主,你为什么括起类型的括号不去掉呢?哈哈,其实是不行的,去掉的话,编译器会报错。
因为int这些数据类型本身就是C语言中的关键字,如果不用()将其束缚起来,让它明显地作为sizeof操作符的操作数,编译器的理解将会是:这里凭空出现了一个int关键字,又没有声明变量等等原因,我只处理唯一性的指令。总之就是这个有歧义,编译器无法帮我们处理就对了。
总结:所以sizeof本质上是一个操作符,用来求数据类型或变量所占空间的大小,单位是字节,求类型时,括号不能省略。
~波浪号的作用是,二进制位取反。它不像整型从原码变到反码时,符号位不变其它位按位取反;也不是!感叹号对结果取反;而是对二进制位全部位取反。比如:
补充:表达式求值的时候,其实不会对变量本身造成影响。可以这样理解,我们在用到变量名,实际上是把变量名控制的这块内存空间里的数值拷贝一份供别人去使用,不管别人怎么使用,不会影响到我变量本身。我们称这种情况为:没有副作用。但我们下面讲的操作符就有副作用了,它们会改变变量内存空间里的值。
2.3++和--
++和--操作符,这两个操作符讲一个,另一个就明白了。
++在变量的后面,是后置++,看到控制台上的结果是,b的值为5,a的值为6。这说明,a++赋值给b的时候,用的是5赋值给b,随后a自己加了个1,打印a的时候打印出来6。
后置++或--的特点是,使用变量原来的值进行表达式运算,然后变量自己再加1或减1,叫做先使用后调整。先使用值,后面再调整自身。
前置++或--:
前置的++是先调整自身变成6,再使用赋值给b为6,叫做先调整后使用。
前置--和后置--也是一样的道理,这里就不再赘述了。
练习:
*(间接访问操作符)解引用操作符,这个符号用于对指针进行解应用操作,这里简单提及一下*的用法。
这里我们首先创建一个a变量,输入一个520,然后将a变量的地址取出来放到整型指针变量pa里,*pa进行的操作是:找到pa里存放的值它相对应的内存单元的编号空间。也就是pa里的值是a变量的地址,*pa通过地址找到a变量,*pa等价于a。具有意义的5.20近在咫尺,博主没有女朋友,可以多更新几篇,用博文为身为情侣的你们送上祝福~ 哈哈。
(数据类型)是一个强制转换的操作符,它可以把原先属于整型的数据转换成浮点型,是C语言比较霸道的解决类型不兼容(不同数据之间进行运算会有不兼容的警告)的方法,因为是霸道的操作符,所以还是有一点破坏的,比如数据可能会丢失之类的,所以数据类型能写对就写对,到一些需要用的场所就用。
整型没有小数,所以强制转换成整数打印,会把小数部分去掉。
3.关系操作符
关系操作符有:>,<,>=,<=,==,!=
关系操作符主要用在测试条件(选择,循环的判断部分),如果表达式是对的,那结果为真,反之为否。注意的一点是,计算机中大于等于和小于等于的写法不像数学那样。
==用来判断相等,千万不要在写判断条件的时候把两个等号写成了一个,那样是赋值而不是判断相等。!=用来判断不相等。
#include <stdio.h>
int main()
{
int a = 0;
if(a = 0)
{
printf("haha\n");
}
return 0;
}
这道编程会不会打印haha?答案是不会的,因为这里不是判断,a是否等于0,而是将0赋值给了a,这个赋值表达式的结果是0,在测试条件部分,0为假,所以不会打印。
#include <stdio.h>
int main()
{
int a = 0;
if(0 = a)
{
printf("haha\n");
}
return 0;
}
这种写法呢?如果我们运行起来,编译器会报错,因为我们把a赋值给了0,0是常量。第一种写法往往会让我们得到意向不到的结果,而且我们不知道错在哪里,需要去调试。第二种写法可以防止我们因为少写了一个等号而需要去通过调试找到这个bug,编译器会报错错误在第几行的。
补充:赋值操作符是从右像左进行的,表达式的值是左边操作符数里的值。比如 a = 10;把10赋给a,结果为a,是10。
4.条件表达式和逗号表达式
4.1条件表达式
条件表达式是唯一的一个三目操作符,它使用的符号是?:。它与if else双分支语句的功能一样,但它写起来比较间接且可以当成表达式赋值。
a大于b吗?如果大于,这一整个表达式的结果就是a;如果不大于,这一整个表达式的结果就是b;
exp1?exp2:exp3,exp1表达式为真,exp2就是条件表达式的结果,反之,exp3是条件表达式的结果。
我们可以把这个条件表达式直接赋值给max,因为它是有值的,我们看下面用if else语句等效用法。
所以我们得知条件表达式与if else语句等效,第二点就是,我们不能把if else语句放在max初始化的赋值操作符的右边,像这样int max = if else语句;是的,我先埋个伏笔,后面讲表达式和语句的关系的时候读者们自然就恍然大悟了。
4.2逗号表达式
逗号表达式是用来表示连续一串需要执行的表达式,它和代码块有点像,且看下面代码:
本来给c赋值的时候,一般只有一个表达式,现在我们使用逗号表达式,把四个表达式a +=10等串起来,计算机会从左到右依次执行a+=10,b>0,b++,c+=1这四个表达式。所以a和b分别变成了10和1,那为什么c的结果是1?
逗号表达式的结果是最后一个表达式的值。c+=1把c变成了1,再把1赋值给c,这里c经历了两次赋值,因为c+=1的本质是 c = c + 1;
前面提到说代码块和逗号表达式有相像的地方,相像在哪里呢?我们知道在我们需要多条语句的时候并且语法限制语句只能是一条,我们就用代码块把多条语句括起来,使这一整个代码块被视作一条语句(里面包含多条语句)。在赋值的时候,我们只能用一个表达式给变量赋值,但我们想要执行其它多个表达式的时候,使用逗号表达式,可以在原先只能用一个表达式的语句中,使用多个表达式,是逗号表达式让多个表达式被视为一个表达式成为可能。
总结:代码块使多条语句成为一条语句,逗号表达式使多个表达式成为一个表达式。逗号表达式的运算特点是从左到右依次执行,整个表达式的结果是最后一个表达式的值。
补充:逗号表达式的优先级最低,如果我们没有加括号,可能会运算出我们意料之外的结果,所以加上括号表示计算顺序是有必要的。
甚至,细心的话,你可以看到感受到计算的顺序。(c = ++a),(b+=2),(c+=3);在第一个表达式计算完后,c变成了1,到第三个表达式 c = c(1) + 3,这里右边的c已经是1了,不再是0了。所以c最后的值打印出来才是4。
其实博主在举例的时候也没想到,在运行之前自己脑中过了一遍认为c最后是3,运行后看到结果想了想,嗯嗯~ 你懂得。
补充:在C++里有顺序点的概念,后来因为在多线程中用顺序点不好表示,就去除去了顺序点的概念,不过依旧对我们有辅助理解的作用。语句的结尾(分号),一个完整的表达式(for循环中的控制表达式,while中的判断表达式等诸如此类的),逗号表达式里的每个表达式都是一个顺序点,计算机保证在进入到下一个顺序点前,将此次发生的改变全部更新到内存中。
用这里的例子讲就是:执行完(c = ++a),在进入到(b += 2)这个表达式之前,c在内存中的值被改成1的动作是完成了的。
5.下标引用&函数调用&结构访问
这三种操作符也经常用:[]、()、.(点操作符)、->
数组元素的访问可以数组的方式,后面讲到指针的时候,会介绍使用指针的方式,数组和指针很像,因为数组的底层实现是用指针滴~
使用下标可以轻松访问到数组中的每个元素,[]的操作数是数组名arr和元素对应的下标,数组的下标是从0开始的,相比较于元素个数,下标的最大值是元素个数减1。
函数调用操作符(),函数在调用的时候,不能省略。
#include <stdio.h>
//这里的()不是操作符,而是函数的形式和数组一样
int Add(int x, int y)
{
return x + y;
}
int main()
{
int a = 0;
int b = 0;
scanf("%d%d", &a, &b);
//()函数调用操作符
int ret = Add(a, b);//,不是逗号表达式
return 0;
}
访问结构体结构成员的操作符有两个,一个是用于结构体变量的,一个是用于结构体指针的。
- 点操作符.
#include <stdio.h>
struct Stu
{
int age;
char name[12];
char sex[5];
};
int main()
{
struct Stu s1 = {10, "小明", "男"};
printf("%d %s %s", s1.age, s1.name, s1.sex);
return 0;
}
struct Stu创建一个结构体类型,还没学过结构体的读者可以想成数据类型一样,用数据类型创建一个s1变量,它里面有三个成员变量,访问的时候要.。
- 箭头操作符->
#include <stdio.h>
struct Stu
{
int age;
char name[12];
char sex[5];
};
int main()
{
struct Stu s1 = {10, "小明", "男"};
struct Stu* ps = &s1;
printf("%d %s %s\n", ps->age, ps->name, ps->sex);
printf("%d %s %s\n", (*ps).age, (*ps).name, (*ps).sex);
//对指针解应用,*ps == s1
return 0;
}
结构体指针访问结构体成员的时候,用箭头指向结构体成员就可以了。
6.表达式与语句的关系
任何一个表达式都有是有值的,可能表面认不出来它有值,但可以算出来。比如:int a = 5;a = 10;这个表达式a = 10的值是10, a > 11这个表达式为0,因为a不大于11,为假,计算机对这些假的表达式赋值为0,真的表达式赋值为1。
而任何一个表达式加上分号后就能成为一个语句,反之则不成立,一个语句去掉分号不一定是表达式。
int main()
{
3//这是一个表达式,值为3
3;//表达式加上分号,变成一条语句,只不过这条语句没啥作用
//聪明一点的编译器甚至会跳过这种无意义的语句
//声明语句
int a = 10;//声明a
int b;//声明b
//去掉分号
int a = 10//这是啥??我不认识,也没见过,bug
int b//这是?表达式?有人说,这不就是一个b是一个随机值嘛,那么?
int c = int b;//这句代码见过吗?没错你见过了,就是now,反正它编不过去。
//所以表达式加上分号是能变成语句的
int d = 10;
d = 20//表达式
d = 20;//语句
//声明语句呀,if语句,for语句是不能当表达式的,因为它们没有值。
}
没想到两篇操作符讲解字数加起来还能差不都到一万字,真没想到,哈哈(苦笑不得)。好啦,这就是C语言中有关操作符的所有知识啦,下一篇见~
结语:希望读者读完有所收获!在学C的路上,祝福我们能越来越C!✔
读者对本文不理解的地方,或是发现文章在内容上有误等,请在下方评论区留言告诉博主哟~,也可以对博主提出一些文章改进的建议,感激不尽!最后的最后!
❤求点赞,求关注,你的点赞是我更新的动力,一起努力进步吧。