操作符C语言


操作符基本就是分为这几种:

算数操作符

+    -   *   /   %
  1. 除了 % 操作符之外,其他的几个操作符可以作用于整数和浮点数。
  2. 对于 / 操作符如果两个操作数都为整数,执行整数除法。而只要有浮点数执行的就是浮点数除
    法。
  3. % 操作符的两个操作数必须为整数。返回的是整除之后的余数。
    我们来看例子(除法的注意点):
#include <stdio.h>
int main()
{
	int a = 9 / 2;
	double b = 9 / 2;
	double c = 9.0 / 2;
	printf("%d\n", a);
	printf("%lf\n", b);
	printf("%lf\n", c);
	return 0;
}

在这里插入图片描述
在这段代码中,我们看到即使将b指定为double型,结果仍然是4.000000,而不是4.500000。因此我们知道在赋值前就已经完成了计算的过程,所以,对于/(除号)两边都是整数,才执行整数的除法,操作数中有一个是浮点数,才执行浮点数除法。

移位操作符

<< 左移操作符
>> 右移操作符

在讲解移位操作符时我们需要先了解关于整型的二进制表示:
整型的二进制表示有3种:原码、反码、补码;原码是直接按照数字的正负写出的二进制序列,反码是原码的符号位不变(最前面一个0,1数字),其他位按位取反得到的,补码是反码+1得到,而整数在内存中存储都是以二进制的补码形式存储,但是对于正整数,原反补是一样的,对于负整数,则需要计算。

左移操作符

我们先看左移操作符:

#include <stdio.h>
int main()
{
	//>> - 右移操作符
	//<< - 左移操作符
	int a = 5;
	int b = a << 1;
	//移位操作符,移动的是二进制位
	printf("%d\n", b);
	int c = -1;
	int d = c << 1;
	printf("%d\n", d);
	return 0;
}

在这里插入图片描述
那么a用二进制表示是00000000000000000000000000000101,既是原码也是反码,也是补码,左移就是将整个二进制序列左移,右边会空出一位,我们需要补0;因此得到00000000000000000000000000001010,经过计算得出b为10,再看负数,c二进制表示10000000000000000000000000000001计算反码11111111111111111111111111111110,反码再加上1得到补码,补码表示11111111111111111111111111111111,左移一位右边补0,得到了新的补码1111111111111111111111111111110,倒推出原码,将补码-1,再符号位不变其余取反得到10000000000000000000000000000010;计算d得出为-2。

右移操作符

首先右移运算分两种:1. 逻辑移位 左边用0填充,右边丢弃2. 算术移位 左边用原该值的符号位填充,右边丢弃

我们再来右移,右移跟左移基本一致,但是需要注意的是右移分为两种:逻辑右移和算数右移,有什么区别呢?对于逻辑右移来说右移一位,左边会空出一位直接补0;而算数右移则是移位前,整数是正数就补0,整数是负数就补1;一般的编译器也是遵循算数右移,这也符合人们的认知。
ps:还需要补充的一点是在进行移位操作时,不能将操作数写为负数

int a = 1;
int b = a << -1;

这样写是错误的。

位操作符

& ——按位与
| ——按位或
^ ——按位异或
注:他们的操作数必须是整数。

我们那按位与举例:

#include <stdio.h>
int main()
{
	//& - 按二进制位与
	//| - 按位或
	//^ - 按位异或
	int a = 3;
	int b = -2;
	int c = a & b;
	//00000000000000000000000000000011——  3的原反补
	

	//10000000000000000000000000000010——  -2原码
	//11111111111111111111111111111101——  -2反码
	//11111111111111111111111111111110——  -2补码
	//补码进行计算
	//00000000000000000000000000000011
	//11111111111111111111111111111110
	//00000000000000000000000000000010
	printf("%d\n", c);

}

在进行按位与的运算时,我们需要了解位操作符都是按二进制位补码进行运算,所以我们将a和b的补码拿出来,a是正数,所以a的二进制的原反补相同为00000000000000000000000000000011,而b是负数,需要经过简单的计算,b的补码为11111111111111111111111111111110,我们将两者进行按位与的运算,运算规则是对应的二进制位都为1才为1,否则为0;所以我们能得到运算结果c为00000000000000000000000000000010,这也是补码,因此打印结果为2。类似的按位或运算也是如此,只是运算规则不同,按位或运算规则是对应的二进制位都为0才为0,否则为1。按位异或运算规则为相同为0,相异为1。总结一下:

  • 按位与操作符(&):对应的二进制位都为1,才为1,否则为0;
  • 按位或操作符( | ):对应的二进制位都为0,才为0,否则为1;
  • 按位异或操作符(^):对应的二进制位相同为0,相异为1;

赋值操作符

关于赋值操作符,我们敲代码几乎每次都涉及到,因此我们不再过多的讲述,需要进一步了解的就是复合赋值符:

+=
-=
*=
/=
%=
>>=
<<=
&=
|=
^=

这种属于复合操作符,跟普通的操作符没什么区别。类似:

int a = 0;
a = a + 1;
a += 1;

这两种写法,效果是一样的。

单目操作符

单目操作符:只有一个操作数的操作符。那么有哪些单目操作符呢?

  • ! 逻辑反操作
  • - 负值
  • + 正值
  • & 取地址
  • sizeof 操作数的类型长度(以字节为单位)
  • ~ 对一个数的二进制位按位取反
  • - - 前置、后置- -
  • ++ 前置、后置++
  • * 间接访问操作符(解引用操作符)
  • (类型)强制类型转换

!——逻辑反操作

举例子:

#include <stdio.h>
int main()
{
	int a = 5;
	int b = !a;
	printf("%d", b);
	return 0;
}

这段代码中,我们可以看到a=5为真(非0为真),而“!”是逻辑反操作,此时!a就是为假(0为假),所以b的值为0。相反如果a为0,那么b的默认结果为1。
关于正值和负值就不过多介绍了,跟数学中的正号负号没有什么区别。

&——取地址操作符和*——解引用操作符

看个例子:

#include <stdio.h>
int main()
{
	int a = 10;
	int* p = &a;
	*p = 20;
	printf("a=%d\n", a);
	return 0;
}

在这段代码中,&a是将a的地址取出来,int* p中的“*”并不是解引用操作符,这里的*是定义变量用的,告诉编译器这里的p是指针变量,用来存放地址,而int则是p地址指向内容是什么类型,而下面的*p=20中的*则是解引用操作符,代表着p指针指向的内容,也就是a,所以这个语句的作用可以理解为将20赋给a。

sizeof——求类型长度

注意sizeof并不是函数,他是操作符,用于计算变量或者类型所创建的变量的内存大小,和内存中存放什么数据没有关系。
我们来看几个例子:

#include <stdio.h>
int main()
{
	char arr[10] = "abc";
	int a = 0;
	printf("%d\n", sizeof(a));
	printf("%d\n", sizeof(int));
	printf("%d\n", sizeof(arr));
	return 0;
}

在代码中,我们看到arr中不管输入是什么,他只与元素的个数和类型有关,并不与内容有关,而对于a来说,sizeof(a)和sizeof(int)是一样的效果所以打印出来的结果是相同的。

~——按位取反

看个例子:

#include <stdio.h>
int main()
{
	int a = 0;
	int b = ~a;
	printf("%d\n", b);
}

这段代码中,我们将a定义并赋值为0,因此得到了“0”的二进制序列为00000000000000000000000000000000,因此按位取反得到了11111111111111111111111111111111,得到的是补码,因此需要经过计算得到原码10000000000000000000000000000001,所以b的打印结果为-1。

++——自增和-- ——自减

关于自增和自减,我们并不需要了解的太过于深入,但是一些基本的操作需要了解,我们看一个例子:

#include <stdio.h>
int main()
{
	int a = 10;
	int b = a++;
	printf("%d\n", b);
	printf("%d\n", a);
	int c = 10;
	int d = ++c;
	printf("%d\n", d);
	printf("%d\n", c);
	return 0;
}

在这里插入图片描述
关于自增自减我们需要了解,a++是先运算(使用),再+1;++a是先+1,再运算(使用),在上述代码中我们看到,a是先赋值给b,再进行+1;而c是先+1,再赋值给d的,同理自减也是如此。

(类型)——强制类型转换

关于强制类型转换同样并不难以理解,我们看个例子:

int main()
{
	int a = (int)3.14;
	printf("%d\n", a);
	return 0;
}

我们可以看到3.14是浮点数,但是我们存入到int类型中,如果不进行强制编译器就会报警告,当我们加上强制类型转换,那么编译器就会将浮点数后面的小数部分舍去。

关系操作符

>
>=
<
<=
!=
==

关于关系运算符比较简单,但是我们需要著一些细节上的处理,对于新手而言是非常容易出错的,比如再变成是判断相等时会经常将“==”写成“=”

逻辑操作符

  • &&——逻辑与
  • | |—— 逻辑或

逻辑操作符是判断真假用的,要和位操作符区分开来。我们看个例子:

#include <stdio.h>
int main()
{
	int a = 3;
	int b = 5;
	int c = a && b;
	printf("c=%d\n", c);
	int x = 0;
	int y = 2;
	int z = x || y;
	printf("z=%d\n", z);
	return 0;
}

这段代码中对于逻辑与来说,a和b都是非0,即为真,所以c为1(默认真打印1),而对于逻辑或来说,x和y中有一个为非0,即为真,所以z为1(默认真打印1)。所以逻辑与是只有两个表达式都为真才为真,对于逻辑或来说,只要一个表达式为真就为真。

条件操作符

条件操作符:

exp1 ? exp2 : exp3

这就是条件操作符,也叫做三目操作符,如果exp1结果为真,则exp2结果作为整个表达式的结果,如果exp1结果为假,则exp3结果作为整个表达式的结果。举个例子:

#include <stdio.h>
int main()
{
	int a = 3;
	int b = 5;
	int m = (a > b ? a : b);
	printf("%d", m);
	return 0;
}

这段代码实现的是a和b的比较,同样也可以用if语句实现,其实条件操作符就是if语句的简化表达。

逗号表达式

exp1, exp2, exp3, …expN

逗号表达式,就是用逗号隔开的多个表达式。 逗号表达式,从左向右依次执行。整个表达式的结果是最后一个表达式的结果。举个例子:

#include <stdio.h>
int main()
{
	int a = 1;
	int b = 2;
	int c = (a > b, a = b + 10, a, b = a + 1);
	printf("%d", c);
	return 0;
}

这段代码中,第一个表达式a>b,为假所以为0,第二个表达式b+10赋值给a得到12,第三个表达式为12,第四个表达式为13,所以最后的结果为13。

下标引用、函数调用和结构成员

[ ]——下标引用操作符

下标引用操作符我们在数组中经常遇见,关于下标引用操作符有几点需要注意,举个例子:

#include <stdio.h>
int main()
{
	int arr[] = { 1,2,3,4,5 };
	int i = 0;
	for (i = 0; i < 5; i++)
	{
		printf("%p-----%p\n", &arr[i], arr + i);
	}
	return 0;
}

经过打印我们发现,打印对应的地址是一摸一样的。
在这里插入图片描述
这也进一步说明了,数组名arr表示的就是首元素的地址。那么我们就可以说arr[3](第四个元素的内容)就相当于是*(arr+3),两者的意义是一模一样的,只是第一个更便于读者理解。

( )——函数调用操作符

函数调用操作符,顾名思义就是在函数调用时引用的操作符,这个操作符是不能省略的,我们看个例子:

#include <stdio.h>
#include <string.h>
int main()
{
	printf("%u", strlen("abc"));
	return 0;
}

在这段代码中,我们可以看到printf()函数和strlen()函数,这两个函数中“()”是不能够省略的,但是前面我们还讲过sizeof,记住sizeof是操作符,不是函数,因此sizeof有时可以加“()”,当然也可以省略。同理不管是库函数还是自定义函数,不管传参还是不传参都需要加上“()”

.和->——结构体成员

自然生活,日常生产中,各式各样的复杂结构,我们无法用一种类型表示,比如:人。人有年龄,体重,身高,性别,家庭住址等等,因此我们引入结构体的概念,将这些包含进去。举个例子:

#include <stdio.h>
//自定义类型
struct Book
{
	char name[20];
	float price;
	char id[10];
};

void print1(struct Book b)
{
	printf("书名:%s\n", b.name);
	printf("价格:%f\n", b.price);  
	printf("书号:%s\n", b.id);  
}
void print2(struct Book* pb)
{
	printf("书名:%s\n", (*pb).name);
	printf("价格:%f\n", (*pb).price);
	printf("书号:%s\n", (*pb).id);
}
void print3(struct Book* pb)
{
	printf("书名:%s\n", pb->name);
	printf("价格:%f\n", pb->price);
	printf("书号:%s\n", pb->id);
}
int main()
{
	struct Book b = { "C语言程序设计",55.5f,"C20190201" };
	print1(b);
	printf("\n");
	print2(&b);
	printf("\n");
	print3(&b);
	return 0;
}

这段代码中,我们自定义一个Book类型,这个类型中有name,price,id三种类型,主函数中我们创建b这个类型的变量,并赋值。print1()函数使用的"."操作符,打印时b相当于一个大房间,每个其中的类型相当于小物品,用b.成员名就能够访问,同理print2和print3是使用指针,pb是b的地址,就相当于大房间的门口一样,“->”就可以指向所在空间的内容。
好了,关于操作符所需要了解的就是这么多,如果有什么错误,欢迎大家指正,谢谢各位!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Solitudefire

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值