C语言详解系列——操作符详解

算术操作符

首先我们为大家介绍一下算术操作符

+ - * / %

他们的用法与数学中的加减乘除取余运算一样,值得注意的是,除了%操作符之外,其他几个操作符都可以用于整数和浮点数,%操作符的两个操作数必须位整数。返回的是整除之后的余数。对于/操作符如果两个操作数都为整数,执行整数除法,而只要有浮点数执行的就是浮点数除法

移位操作符

<< 左移操作符
>> 右移操作符
移位操作符的操作数也只能是整数

要了解移位操作符的化,我们需要向大家介绍一下有关原码,反码,和补码的知识。一个整数在内存中存储的是二进制的补码,移位操作符,移动的是存储在内存当中的补码
什么是原码,反码和补码呢?

1.正整数的原码反码补码相同,都是他们自身的二进制表示
2.负整数的原码是他自身的二进制表示,反码是原码的符号位不变,其他位按位取反,补码是反码+1

例如1
原码,补码,反码都是:00000000000000000000000000000001因为整数存放在int类型的变量中,int类型占四个字节,32个比特位。所以二进制有32位,其中最左边的一位是符号位,0表示为正整数,1表示为负整数
例如-1
原码:10000000000000000000000000000001
反码:111111111111111111111111111111111110
补码:111111111111111111111111111111111111

移位操作符移动的就是他们在内存当中存储的补码

例如int num = -1;
num = num <<1;
原码:10000000000000000000000000000001
反码:111111111111111111111111111111111110
补码:111111111111111111111111111111111111
将num向左移动移位,丢弃左边,在右边补0
补码变成:111111111111111111111111111111111110
反码:111111111111111111111111111111111101
原码:10000000000000000000000000000010
num =num >> 1;
原码:10000000000000000000000000000001
反码:111111111111111111111111111111111110
补码:111111111111111111111111111111111111
将num向右移一位,两种规则1.逻辑位移,左边加0,右边丢弃2.算术位移:左边加原符号位,右边丢弃,大部分编译器使用的是算术位移
补码:111111111111111111111111111111111111
反码:111111111111111111111111111111111110
原码:10000000000000000000000000000001

注意:对于移位操作符,不要移动负数位,这个没有定义

位操作符

& 按位与
| 按位或
^ 按位异或
他们的操作数也必须是整数

他们也是对整数在内存当中存储的二进制补码进行操作

3 & -5
00000000000000000000000000000011 3的补码
10000000000000000000000000000101 -5的原码
11111111111111111111111111111010 -5的反码
11111111111111111111111111111011 -5的补码
将他们的补码进行&,同为1取1,有0则0
00000000000000000000000000000011
11111111111111111111111111111011
00000000000000000000000000000011
-3 | -5
00000000000000000000000000000011
11111111111111111111111111111011
11111111111111111111111111111011
将他们的补码进行|,有1则1,同0取0
3 ^ -5
00000000000000000000000000000011
11111111111111111111111111111011
11111111111111111111111111111000
==将他们的补码进行^,相同为0,不同为1

赋值操作符

赋值操作符=,可以连续使用,用于给变量赋值

int main()
{
	int x = 10;
	int y = 20;
	int z = 30;
	x = y = z = 40;
}

但是这样写逻辑不够清晰我们还是建议大家分开写

int main()
{
	int x = 10;
	int y = 20;
	int z = 30;
	y = z;
	x = y;
}

符合赋值符

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

这些运算都有复合的效果,例如:

int main()
{
    int x =10;
    x = x+10;
    x += 10;
    return 0;
}

上面的x = x+10;x += 10;有同的效果。

单目操作符

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

我与大家分析一下sizeof这个操作符

int main()
{
	int x = 10;
	char a = 'a';
	printf("%d\n", sizeof(x));
	printf("%d\n", sizeof(a));
	printf("%d\n", sizeof x);
	printf("%d\n", sizeof a);
}

在这里插入图片描述
如上图我们知道了sizeof的使用可以不用加括号,所以sizeof不是函数而是操作符,他的作用就是求变量所占的空间大小。

void test1(int arr[])
{
	printf("%d \n",sizeof arr);
}

void test2(char arr[])
{
	printf("%d \n", sizeof arr);
}

int main()
{
	int arr[10] = { 0 };
	char arr1[10] = { 0 };

	printf("%d \n", sizeof arr);
	printf("%d \n", sizeof arr1);
	test1(arr);
	test2(arr1);

	return 0;
}

在这里插入图片描述
sizeof与数组,因为sizeof是求变量所占空间大小所以,int类型的数组每个元素占4个字节,有十个元素,所以是40,char类型的数组每个元素占1个字节,所以是10,而数组在传参的过程中,传递的是首元素地址,地址在32位环境下占4个字节,所以都是4。

前置-- 和 ++是先自减和自加,在调用,后置-- ++是先调用在自减和自加

关系操作符

>=
<=
<
>
!=
==

他们的用法跟他们在数学当中的用法一样!=用于测试不等于,==用于测试等于

逻辑操作符

&& 逻辑与
|| 逻辑或

逻辑与和或是判断真假的,他们特点:

int main()
{
    int i = 0,a=0,b=2,c =3,d=4;
    i = a++ && ++b && d++;
    //i = a++||++b||d++;
    printf("a = %d\n b = %d\n c = %d\nd = %d\n", a, b, c, d);
    return 0; }

输出的结果为 1 2 3 4,因为&&判断真假,遇假则假,当a++进行判断时,先调用a++,调用时a = 0;所以后面不需要在进行判断,直接返回为假。这是&& ||的短路特点,至于下一个是多少大家可以自己思考一下。

条件操作符

expq ? expe2 : exp3;

条件操作符又称三目操作符,作用与if基本相同

int main()
{
	int a = 10;
	int b = 20;
	
	printf("%d",b > a ? b : a);

	return 0;
}
int main()
{
	int a = 10;
	int b = 20;
	if (b > a)
	{
		printf("%d", b);
	}
	else
	{
		printf("%d", a);
	}
	return 0;
}

如上图这两个代码所表达的逻辑一样。

逗号表达式

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

int main()
{
	int a = 0;
	int b = 10;
	int c = (a=a + b,b = a * b, b);
	printf("%d %d %d", a, b, c);
}

在这里插入图片描述

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

下标引用

[]下标引用操作符,操作数:一个数组名,一个索引值

int main()
{
	int arr[10] = { 0 };
	arr[9] = 10;
}

这里的[]两个操作时arr和9。

函数调用操作符

接收一个或者多个操作数:第一个操作数是函数名,剩余的操作数就是传递个给函数的参数。

int add(int a, int b)
{
	return (a + b);
}

int main()
{
	int a = 10;
	int b = 10;
	int c = add(a, b);
	
}

访问结构成员

. 结构体成员名
-> 结构体指针名

struct student
{
	char name[20];
	int age;
};

print(struct student stu)
{
	stu.age = 20;
}

print2(struct student* stu)
{
	stu->age = 20;
}
int main()
{
	struct student stu = { "lll",18 };
	print(stu);
	print(stu);
}

表达式求值

表达式求值的顺序一部分是由操作符的优先级和结合性决定的,同样,有些表达式的操作在求值的过程中可能需要转换为其他类型。

隐式类型转换

在C语言中,整形算数运算总是至少以缺省整形类型的精度进行的。为了获得这个精度,表达式中的字符和短整型在使用前被转换为普通整型,这种转换称为整型提升
整形提升的意义:

表达式的整型运算要在CPU的相应运算器件内执行,CPU内整型运算器(ALU)的操作数的字节长度
一般就是int的字节长度,同时也是CPU的通用寄存器的长度。
因此,即使两个char类型的相加,在CPU执行时实际上也要先转换为CPU内整型操作数的标准长
度。
通用CPU(general-purpose CPU)是难以直接实现两个8比特字节直接相加运算(虽然机器指令
中可能有这种字节相加指令)。所以,表达式中各种长度可能小于int长度的整型值,都必须先转
换为int或unsigned int,然后才能送入CPU去执行运算。

int main()
{
    char a = 0xb6;
    short b = 0xb600;
    int c = 0xb6000000;
    if(a==0xb6)
    printf("a");
    if(b==0xb600)
    printf("b");
    if(c==0xb6000000)
    printf("c");
    return 0;
  }

因为变量a与b在进行判断时,进行整形提升, 变为的负数,所以表达式 a == 0xb6,b == 0bb600的结果为假。
那么整形提升时如何进行的呢?

正数的整型提升:高位补充符号位,即为1。
负数的整形提升:高位补充符号位,即为0。
无符号的整形提升,高位补0。

算术转换

如果某个操作符的各个操作数属于不同的类型,那么除非其中一个操作数的转换为另一个操作数的类
型,否则操作就无法进行。下面的层次体系称为寻常算术转换。

long double
double
float
unsigned long int
long int
unsigned int
int

如果某个操作数的类型在上面这个列表中排名较低,那么首先要转换为另一个操作数的类型后执行运算。
注意:
但是算数运算符要合理,要不然会有潜在的问题

int main()
{
   float f = 3.14;
   int num = f;//隐式转换,会有精度的丢失。
   return 0;
}

操作符的属性

复杂表达式的求值有三个影响的因素。

  1. 操作符的优先级
  2. 操作符的结合性
  3. 是否控制求值顺序。

两个相邻的操作符先执行哪个?取决于他们的优先级。如果两者的优先级相同,取决于他们的结合性。
操作符优先级

  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

悲伤猪小猪

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

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

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

打赏作者

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

抵扣说明:

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

余额充值