【C语言】-详解操作符,深入内存中给大家全方面介绍!!

作者:小树苗渴望变成参天大树
作者宣言:认真写好每一篇博客
作者gitee:link
在这里插入图片描述
如 果 你 喜 欢 作 者 的 文 章 ,就 给 作 者 点 点 关 注 吧!


🎄前言

各位友友们,今天我又来更新新的好文了,之前我们讲过数组,函数方面相关的知识,我们今天来讲讲操作符,可以说有了操作符的存在,使我们的编程变得简单方便,并且有些操作符和我们常见的意思一样,所以更容易一点,那我们呢就正式进入操作符的讲解了!!!

🎃一、操作符分类

我们的操作符有许多,所以我们把它总结为一下几类:

1.算术操作符
2.移位操作符
3.位操作符
4.赋值操作符
5.单目操作符
6.关系操作符
7.逻辑操作符
8.条件操作符
9.逗号表达式
10.下标引用、函数调用和结构成员

接下来我将从每个部分的操作符来为大家作详细的介绍

🎊二、算术操作符

+    -   *   /   %//加,减,乘,除,取模

前面三个就是数学中的运算,不重点介绍,主要讲解/ %这两个操作符,我们先来看一段代码:

1./操作符

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

我猜有一些人看到认为这个结果肯定认为是3.3333,但结果是3,这时候有点人会说你以%d的形式打印,怎么可能打印出来是浮点数呢?如果你以%lf的形式打印结果还是不对,那怎么才会出现我们想要结果呢??

在这里插入图片描述

对于 / 操作符如果两个操作数都为整数,执行整数除法。而只要有浮点数执行的就是浮点数除法。特别说明,/两边都是整数的时候,除出来的娴熟,值保留整数部分,不是采取四舍五入的方式。

2.%操作符
这是一个求余数的操作符,我们来看代码:

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

10除以3的余数是1,我相信大家应该都知道。
注意: % 操作符的两个操作数必须为整数。返回的是整除之后的余数,也是整数。
如果出现浮点数,编译器会直接报错,所以除了 % 操作符之外,其他的几个操作符可以作用于整数和浮点数。

🎉三、移位操作符

移位操作符分为两种
1.<< 左移操作符
2.>>右移操作符
注:移位操作符的操作数只能是整数。

3.1左移操作符

上面说过,移位操作符只使用与整数,操作符左边代表操作数,右边代表移几位,整数就是整型,一个整型有char,short,int ,long,long long都属于整型家族的,但我们今天以int类型为主,一个int占四个字节,一个字节是八个比特位,每个比特位是一个二进制数,所以一个int类型是32个比特位。介绍这个的目的是因为我们的操作符是在二进制的基础上进行的。那我们来看看吧。

int a=10;
00000000 00000000 00000000 00001010//这就是10在内存以二进制存储形式

在这里插入图片描述
这里还要补充一点就是我们在内存的存储是补码,补码是原码按位取反加一,刚才写的是可以说是原码,因为正数的原反补三码相同
我们来写一下负数的三码吧

int a=-1;
10000000 00000000 00000000 00000001//原码
11111111 11111111 11111111 11111110//反码
1111 1111 1111 1111 1111 1111 1111 1111//补码
  f   f    f    f     f    f    f    f

按道理在内存应该为ff ff ff ff,我们来看看结果
在这里插入图片描述
果然和我们算的一样,注意:我们在进行负数,取反的时候,最高位为符号位是1,不变其他位按位取反。

位移操作符实际是在补码的基础上进行计算,打印到屏幕的数,还是要转换成原码来打印的,通过补码减一,得到反码,反码按位取反得原码就可以了。

那我们开始介绍位移操作符吧

移位规则:
左边抛弃、右边补0

int a-10;
printf("%d",a<<1);

整数的原反补码相同
在这里插入图片描述
10100转换位十进制应该为20,我们看看结果是不是20?
在这里插入图片描述
结果更我们想的一模一样,并且a的值没有被改变,我们再来一个负数的练习:

int a=-1;
//10000000 00000000 00000000 00000001//原码
//11111111 11111111 11111111 11111110//反码
//11111111 11111111 11111111 11111111//补码
a<<1  左边抛弃、右边补0
// 1111111  11111111 11111111  11111111 0//重新得到的补码,我们需要把他转换为原码
//11111111  11111111 11111111  11111101//补码减1得反码
//10000000  00000000 00000000  00000010//符号位不变,其余位按位取反得原码

答案应该是-2

在这里插入图片描述
结果和我们计算的一模一样。那这个时候你是否对左移操作符有了更深的理解呢??
那我们接下来看看右移操作符,比左移复杂一些。

3.2右移操作符

移位规则:
首先右移运算分两种:

  1. 逻辑移位
    左边用0填充,右边丢弃
  2. 算术移位
    左边用原该值的符号位填充,右边丢弃

    我们现在普遍使用算术移位,我们来举个例子说明逻辑移位现在为什么不用
    在这里插入图片描述
    那我们就按照算术移位来做个练习:以负数为例,正数比较简单。
int a=-10;
10000000 00000000 00000000 00001010//原码
11111111 11111111 11111111 11110101//最高位的符号位不变,其余为按位取反得反码
11111111 11111111 11111111 11110110//反码加一得补码
a>>1;
1 11111111 11111111 11111111 1111011//右移之后的补码
11111111 11111111 11111111 11111010//补码减一的反码
10000000 00000000 00000000 00000101//反码符号位不变,其余为按位取反的原码

答案应该是-5

在这里插入图片描述
结果和我们计算的一样,如果大家不理解可以下来自己来取一个数来验证自己的答案对不对,其实对于正数,不管是逻辑移位,还是算术移位都是可以的,感兴趣的小伙伴可以自己下来试试,对于负数只能使用算术位移。相信讲到这里大家对于位移操作数大家应该都理解了吧!

特别说明:对于移位运算符,不要移动负数位,这个是标准未定义的。
int num = 10;
num>>-1;//error
你这是像表示右移-1位?还是左移1位?大家不要写这样匪夷所思的代码!!!

✨四、位操作符

我们有三种位操作符
& //按位与
| //按位或
^ //按位异或
注:他们的操作数必须是整数。
并且也是在内存转换成二进制补码的形式进行计算的

4.1按位与

按位与规则:
有0即为0,两个都为1才为1.

int a=10;
//00000000 00000000 00000000 00001010//原码
//00000000 00000000 00000000 00001010//反码
//00000000 00000000 00000000 00001010//补码
int b=5;
//00000000 00000000 00000000 00000101//原码
//00000000 00000000 00000000 00000101//反码
//00000000 00000000 00000000 00000101//补码
int c=-1
//10000000 00000000 00000000 00000001//原码
//11111111 11111111 11111111 11111110//反码
//11111111 11111111 11111111 11111111//补码
int d=-10//10000000 00000000 00000000 00001010//原码
//11111111 11111111 11111111 11110101//反码
//11111111 11111111 11111111 11110110//补码

1.printf("%d",a&b);
//00000000 00000000 00000000 00001010//a的补码
//00000000 00000000 00000000 00000101//b的补码
a&b
//00000000 00000000 00000000 00000000//按位与的补码,因为最高位是0,所以是正数,三码相同
所以a&b=0

2.printf("%d",a&c);
//00000000 00000000 00000000 00001010//a的补码
//11111111 11111111 11111111 11111111//c的补码
a&c
//00000000 00000000 00000000 00001010//按位与的补码,因为最高位是0,所以是正数,三码相同
所以a&c=10

3.printf("%d",c&d);
//11111111 11111111 11111111 11111111//c的补码
//11111111 11111111 11111111 11110110//d的补码
c&d
//11111111 11111111 11111111 11110110//按位与的补码,因为最高位是1,所以是负数,我们要求他的原码
//11111111 11111111 11111111 11110101//反码
//10000000 00000000 00000000 00001010//反码符号位不变,其余位按位取反
所以c&d=-10

在这里插入图片描述

答案和我们看到的一样,我这里举了全部的例子,正数和正数,负数和负数,正数个负数,希望大家可以理解。

4.2按位或

按位或规则:
有1即为1,两个都为0才为0.

这里我就讲解大家可能不太理解的负数来举例子吧,正数我相信大家都会,三码相同

int a=-10;
//10000000 00000000 00000000 00001010//原码
//11111111 11111111 11111111 11110101//反码
//11111111 11111111 11111111 11110110//补码
int b=-1;
//10000000 00000000 00000000 00000001//原码
//11111111 11111111 11111111 11111110//反码
//11111111 11111111 11111111 11111111//补码

printf("%d",a|b)
//11111111 11111111 11111111 11110110//a的补码
//11111111 11111111 11111111 11111111//b的补码
a|b
//11111111 11111111 11111111 11111111//按位与的补码,最高位是1,是负数,求原码
//11111111 11111111 11111111 11111110//反码
//10000000 00000000 00000000 00000001//原码
所以a|b=-1

在这里插入图片描述
答案和我们想的一样。

4.3按位异或

按位异或规则:
相同为0,相异为1

这里作者就不举例子了,但在这里给大家介绍几个东西:
1.a^a=0

int a=10;
//00000000 00000000 00000000 00001010//a的补码
//00000000 00000000 00000000 00001010//a的补码
a^a
//00000000 00000000 00000000 00000000
所以a^a=0;

2.0^a=a

int a=10;
//00000000 00000000 00000000 00001010//a的补码
//00000000 00000000 00000000 00000000//0的补码
0^a
//00000000 00000000 00000000 00001010
所以0^a=a;

3.按位异或支持交换律

1.3^5^3
//011//3
//101//5
//110//3^5
//011//3
//101//3^5^3
3^5^3=5

2.3^3^5
3^3=0
0^5=5
3^3^5=5

知道这三个规律后,我们来看一道变态的面试题
1.不能创建临时变量(第三个变量),实现两个数的交换
这个题目我们已经见多不怪了,但是在此之前我们都是创建一个临时变量,有的人会说,我们可以弄一个函数啊,但是在函数的内部也是需要创建一个临时变量啊!

有的牛人就想出一些其他想法:

#include<stdio.h>
int main()
{
	int a = 10;
	int b = 5;
	printf("交换前:a=%d,b=%d\n", a, b);
	a = a + b;//a等于两个数的和
	b = a - b;//a减b等于a赋给b
	a = a - b;//a(这个a是两个数的和)减b等于相当于a(这个a是两个数的和)减a(这个a就是a)等于b赋给a
	printf("交换后:a=%d,b=%d", a, b);
	return 0;
}

也实现了题目给的要求,但是这个代码有问题,当a,b特别大的时候,a,b在没有溢出的情况下,a+b可能溢出,造成程序崩溃,那我们用刚才学的按位异或来解决这个问题吧:

#include <stdio.h>
int main()
{
 int a = 10;
 int b = 20;
 a = a^b;
 b = a^b;//把a代入b=a^b^b=a;
 a = a^b;//把b代入a=a^a^b=b;
 printf("a = %d b = %d\n", a, b);
 return 0;
}

这样也可以完成交换,不明白下去可以自己画一下二进制序列

用按位异或解决这个问题,有三个不好的地方:
1.可读性差
2.效率也不如创建临时变量好
3.按位异或值针对整数
所以说这是一道面试题,目的不是让你用按位异或解决这个问题,而是看你对知识掌握的咋样。

相信大家看到这里对于位操作符有了更深的理解了吧。接下来讲赋值操作符

🎀五、赋值操作符

赋值操作符是一个很基础的操作符了,也是一个很棒的操作符,他可以让你得到一个你之前不满意的值。也就是你可以给自己重新赋值。

int weight = 120;//体重
weight = 89;//不满意就赋值
double salary = 10000.0;
salary = 20000.0;//使用赋值操作符赋值

赋值操作符可以连续使用,比如:

int a = 10;
int x = 0;
int y = 20;
a = x = y+1;//连续赋值
这样的代码感觉怎么样?
那同样的语义,你看看:
x = y+1;
a = x;
这样的写法是不是更加清晰爽朗而且易于调试。

赋值操作符可以说是最简单并且是使用最多的操作符,因为每次写代码的时候,创建的变量都要进行初始化,就会使用到赋值操作符。

5.1复合操作符

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

这些都是我们刚才学过的操作符,他们都可以和赋值运算符写成复合的效果
举个例子:

int x = 10;
x = x+10;
x += 10;//复合赋值
//其他运算符一样的道理。这样写更加简洁。

🧧六、单目操作符

6.1单目操作符介绍

我们在上面学习的五种操作符都是双目操作符,双目顾名思义就是在操作数的两边都有数,而单目操作符只要有一个数就行了,下面我将详细来介绍单目操作符。

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

我们有这些单目操作符
下面是逻辑运算符的用法

#include <stdio.h>
int main()
{
	int a = 0;
	if (!a)//非0即为真,!就是把真变为假,假变为真
	{ 
		//!a为真执行下面的语句
		printf("%d\n", 1);
	}
	int b = 10;
	if (!b)
	{
		//!b为假不执行下面的语句
		printf("%d", 2);
	}
}

在这里插入图片描述

  1. -,+,&这三个操作符没什么重点可讲的,接下来在讲讲sizeof
    关于sizeof其实我们之前已经见过了,可以求变量(类型)所占空间的大小
#include <stdio.h>
int main()
{
 int a = -10;
 printf("%d\n", sizeof(a));//1
 printf("%d\n", sizeof(int));//2
 printf("%d\n", sizeof a);//这样写行不行?//3
 printf("%d\n", sizeof int);//这样写行不行?//4
 return 0;
}

sizeof计算大小的时候前三个的写法都是可以的,第四个不允许出现这种情况,编译器会直接报错。

sizeof和数组,我们来看一段代码:

#include <stdio.h>
void test1(int arr[])
{
 printf("%d\n", sizeof(arr));//(2)
}
void test2(char ch[])
{
 printf("%d\n", sizeof(ch));//(4)
}
int main()
{
 int arr[10] = {0};
 char ch[10] = {0};
 printf("%d\n", sizeof(arr));//(1)
 printf("%d\n", sizeof(ch));//(3)
 test1(arr);
 test2(ch);
 return 0;
}
问:
(1)、(2)两个地方分别输出多少?
(3)、(4)两个地方分别输出多少?

在这里插入图片描述
(1)(3)计算的是整个数组的大小,而(2)(4)是在函数体内计算,函数把数组名传过去,实际形参接收到的不是整个数组,而是首元素的地址,是1··用指针变量接收的,在32位机器指针变量的大小都是4个字节,所以打印出来的都是4.

2.来讲讲按位取反~

int a=-10;
//10000000 00000000 00000000 00000001//原码
//11111111 11111111 11111111 11111110//反码
//11111111 11111111 11111111 11111111//补码
printf("%d",~a);
~a//按位取反
//00000000 00000000 00000000 00000000//最高位为0,为整数
所以~a=0

在这里插入图片描述

3.++,–操作符的用法,前置++(–)是先++,在使用,后置++(–)是先使用,后++

//前置++和--
#include <stdio.h>
int main()
{
    int a = 10;
    int x = ++a;
     //先对a进行自增,然后对使用a,也就是表达式的值是a自增之后的值。x为11。
     int y = --a;
    //先对a进行自减,然后对使用a,也就是表达式的值是a自减之后的值。y为10;
    return 0;
}

//后置++和--
#include <stdio.h>
int main()
{
    int a = 10;
    int x = a++;
    //先对a先使用,再增加,这样x的值是10;之后a变成11;
    int y = a--;
    //先对a先使用,再自减,这样y的值是11;之后a变成10;
    return 0;
}

大家看到这里对–,++操作符是不是又理解了一些了。

4.解引用操作符*

int a=10;
int*p=&a;//讲a的地址放到指针变量里面去
*p=0;//通过解引用操作符访问到p地址里面的内容,进行修改

相信带啊看到这里对单目操作符有了更深的理解的吧,那我们接下来讲关系操作符

🎑七、关系操作符

1.>
2.>=
3.<
4.<=
5.!= 用于测试“不相等”
6.== 用于测试“相等”

这些运算符相对而言来说是比较简单的,都是字面意思,但是我们在使用==,这个时候别和赋值操作符搞混了,尤其在一些判断语句中容易错,所以大家还是需要细心一点,给一个小的建议可以把常量放在操作符的左边,例如:5==a,因为在写5=a的时候编译器会报错。

🎐八、逻辑操作符

逻辑操作符有哪些:

&& 逻辑与 //只要出现假,整个语句就是假
|| 逻辑或 //只要出现真,整个语句都为真

区分逻辑与和按位与
区分逻辑或和按位或

1&2----->0
1&&2---->1
1|2----->3
1||2---->1

我们发现和按位与、按位或相似,但我们要区分他们之间的区别,按位与、按位或是在二进制序列的基础上进行计算的,而逻辑与、逻辑或判断真假的,我们来举一个例子:

写一个判断闰年的代码:

int is_leap_year(int year)
{
		if((year%4==0&&year%100!=0)||year%400==0)
		{
				return 1;//是闰年返回1
		}
		return 0;//不是闰年返回0
}

(year%4== 0&&year%100!=0)这个条件就是两个条件都需要满足才为真,比如2020是闰年,可以被4整除,却不能被100整除,2000就不是闰年,他既可以被4整除也可以被100整除,
(year%4== 0&&year%100!=0)||year%400==0这个只要一个为真即可。

友情说明,逻辑与就是我们常说的并且,逻辑或就是我们常说或者。

我们来看一道360的面试题吧

#include <stdio.h>
int main()
{
    int i = 0,a=0,b=2,c =3,d=4;
    i = a++ && ++b && d++;//语句1
    i = a++||++b||d++;//语句2
    printf("a = %d\n b = %d\n c = %d\nd = %d\n", a, b, c, d);
    return 0;
}
//程序输出的结果是什么?

如果使用语句1屏蔽语句2答案是多少?
有没有人一开始计算的1 3 3 5,但结果和我们想的不一样,此题答案是1 2 3 4,为什么会出现这种情况?
如果使用语句2屏蔽语句1答案是多少?
有没有算出的也是1 3 3 5,但结果是1 3 3 4
在这里插入图片描述

我们掌握好这两个特点之后,那么逻辑与、逻辑或就不会什么太大问题。

🎎九、条件操作符

exp1 ? exp2 : exp3
特点: exp1为真就执行exp2,exp1为假就执行exp3

1.
if (a > 5)
        b = 3;
else
        b = -3;
转换成条件表达式,是什么样?
2.使用条件表达式实现找两个数中较大值
a>5?b=3:b=-3;

条件表达式很简单,可以让代码简单化。

🎏十、逗号表达式

exp1, exp2, exp3, …expN
特点:
逗号表达式,就是用逗号隔开的多个表达式。
逗号表达式,从左向右依次执行。整个表达式的结果是最后一个表达式的结果。

我们来举个例子:

//代码1
int a = 1;
int b = 2;
int c = (a>b, a=b+10, a, b=a+1);//逗号表达式
int d=10;
c是多少?

//代码2
if (a =b + 1, c=a / 2, d > 0)

1.代码1执行a>b,在执行a=b+10,此时a为12,在执行最后一句b=a+1,此时b为13,因为b=a+1为整个都好表达式的最后一句,所以返回他计算出来的结果,所以c=13;
2.代码2先指向a=b+1;此时a为3,在执行c=a/2,此时c为1,在执行d>0为真,返回真的结果,所以if判断为真。这个不像逻辑与、逻辑或,遇到假或者真,后面的语句就不执行了,而是一直执行到最后一条语句。

看到这里我相信大家都都好表达式有了一定的理解,但不知道逗号表达式有什么好处,那接下来我在举个例子:

//代码3
a = get_val();
count_val(a);
while (a > 0)
{
         //业务处理
        a = get_val();
        count_val(a);
}
如果使用逗号表达式,改写
//代码4
while (a = get_val(), count_val(a), a>0)
{
         //业务处理
}

我们看到代码3有重复的语句,我们利用逗号表达式的特性可以写成代码4,使代码简化。

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

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

int arr[10];//创建数组
arr[9] = 10;//实用下标引用操作符。
[ ]的两个操作数是arr和9。

学习数组的时候经常会用到.

2. ( ) 函数调用操作符
接受一个或者多个操作数:第一个操作数是函数名,剩余的操作数就是传递给函数的参数。

#include <stdio.h>
 void test1()
 {
 printf("hehe\n");
 }
 void test2(const char *str)
 {
 printf("%s\n", str);
 }
 int main()
 {
 test1();            //实用()作为函数调用操作符。
 test2("hello bit.");//实用()作为函数调用操作符。
 return 0;
 }

一个函数调用操作符至少要有一个操作数,test1(),必须要有函数名,可以没有参数。

3. 访问一个结构的成员
在这里我就不详细介绍结构体了,可以看我这篇博客,初步认识结构体初识C语言

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

#include <stdio.h>
struct Stu
{
 char name[10];
 int age;
 char sex[5];
 double score;
 }void set_age1(struct Stu stu)
{
 stu.age = 18;
}
void set_age2(struct Stu* pStu)
{
*(pStu).age=18//这样写也是对的
 pStu->age = 18;//结构成员访问
}
int main()
{
 struct Stu stu;
 struct Stu* pStu = &stu;//结构成员访问
 
 stu.age = 20;//结构成员访问
 set_age1(stu);
 
 pStu->age = 20;//结构成员访问
 set_age2(pStu);
 return 0;
}

对于 set_age1我们知道里面的age是不可以被改变的,因为这是传值调用,而 set_age2是传址调用,里面的值可以被改变的.

💤十二、 表达式求值

我们上面学的操作符有的参与到表达式求值中来,我们有的操作可能需要转换为其他类型进行计算。

12.1 隐式类型转换

C的整型算术运算总是至少以缺省整型类型的精度来进行的。//意思就是精度低于int 类型的需要转换为int类型来计算。
为了获得这个精度,表达式中的字符和短整型操作数在使用之前被转换为普通整型,这种转换称为整型提升。

//实例1
char a,b,c;
...
a = b + c;
b和c的值被提升为普通整型,然后再执行加法运算。
加法运算完成之后,结果将被截断,然后再存储于a中

如何进行整体提升呢?
整形提升是按照变量的数据类型的符号位来提升的

//负数的整形提升
char c1 = -1;
变量c1的二进制位(补码)中只有8个比特位:
1111111
因为 char 为有符号的 char
所以整形提升的时候,高位补充符号位,即为1
提升之后的结果是:
11111111111111111111111111111111
//正数的整形提升
char c2 = 1;
变量c2的二进制位(补码)中只有8个比特位:
00000001
因为 char 为有符号的 char
所以整形提升的时候,高位补充符号位,即为0
提升之后的结果是:
00000000000000000000000000000001
//无符号整形提升,高位补0

注意:是在补码的基础上进行整型提升的

我们来看一段练习题看代码:

 char a=3;
 char b=127;
 char c=a+b;
 printf("%d",c);

在这里插入图片描述

在这里插入图片描述
结果和我们算的一样,如果不整形提升可能会出现错误。接下来给大家讲一个小的知识,有符号的char的取值范围是-128-127,无符号的取值范围是0-255,那我们看看怎么计算的:
在这里插入图片描述
我们也可以按照这个办法算算short类型的取值范围

那我们为什么要整型提升呢?

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

再来看两个例子:

//实例1
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;
}

//实例2
int main()
{
 char c = 1;
 printf("%u\n", sizeof(c));//1
 printf("%u\n", sizeof(+c));//4
 printf("%u\n", sizeof(-c));//4
 return 0;
}

实例1结果是打印c,为什么是这样,因为我们在进行if判断的时候里面进行的表达式求值,因为计算是在补码的基础上操作的,所以会进行提升,变成int 类型,是四个字节,而前两个if判断里面一个地址是一个字节,另一个里面是2个字节,所以肯定不一样,而第三个不需要进行整型提升,他本身就是Int类型的。

实例2的c只要参与表达式运算,就会发生整形提升,表达式 +c ,就会发生提升,所以 sizeof(+c) 是4个字节.表达式 -c 也会发生整形提升,所以 sizeof(-c) 是4个字节,但是 sizeof© ,就是1个字节.

int a=10;
float b=3.14
int c=a+b;
结果计算出来的是13
因为float在进行整型运算的时候会转换为整型

这种转换只会从精度高向精度低转换

12.2 算术转换

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

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

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

float f = 3.14;
int num = f;//隐式转换,会有精度丢失

12.3 操作符的属性

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

  1. 操作符的优先级
  2. 操作符的结合性
  3. 是否控制求值顺序。
    两个相邻的操作符先执行哪个?取决于他们的优先级。如果两者的优先级相同,取决于他们的结合性。

一些问题表达式

//表达式的求值部分由操作符的优先级决定。
//表达式1
a*b + c*d + e*f

说明:代码1在计算的时候,由于*比+的优先级高,只能保证, * 的计算是比+早,但是优先级并不能决定第三个 * 比第一个+早执行。
所以表达式的计算机顺序就可能是:

a*b
c*d
a*b + c*d
e*f
a*b + c*d + e*f

或者:
a*b
c*d
e*f
a*b + c*d
a*b + c*d + e*f

在比如:

//表达式2
c + --c;

同上,操作符的优先级只能决定自减–的运算在+的运算的前面,但是我们并没有办法得知,+操作符的左操作数的获取在右操作数之前还是之后求值,所以结果是不可预测的,是有歧义的。

有分歧的代码

//代码3-非法表达式
int main()
{
 int i = 10;
 i = i-- - --i * ( i = -3 ) * i++ + ++i;
 printf("i = %d\n", i);
 return 0;
}

表达式3在不同编译器中测试结果:非法表达式程序的结果,这把计算器都搞懵逼了
值 编译器
—128 ----Tandy 6000 Xenix 3.2
—95 ----Think C 5.02(Macintosh)
—86 ----IBM PowerPC AIX 3.2.5
—85 ----Sun Sparc cc(K&C编译器)
—63 ----gcc,HP_UX 9.0,Power C 2.0.0
4---- Sun Sparc acc(K&C编译器)
21 ----Turbo C/C++ 4.5
22 ----FreeBSD 2.1 R
30---- Dec Alpha OSF1 2.0
36 ----Dec VAX/VMS
42 ----Microsoft C 5.1

所以我们尽量不要写出这样的代码,害了自己,也害了别人,更害了计算机。我们要先算那个,我们可以用括号,或者分开写都是可以的。

💦十三、总结

花了两天,耗时9个小时,终于讲完操作符了,我们对熟悉的操作符更加熟练了,对不熟悉的操作符也有了一定的了解,操作符可以说对我们编程有了更深的帮助,并且有了操作符的存在,我们写代码的时候会更加简单,得心应手。我们也可以想想规定这些的人他们是有多么的厉害,既可以写出各种功能的操作符,还写的这么全。通过此篇博客我希望读者不在害怕操作符,并且能熟练的使用操作符,这就是我这篇博客的意义所在。

那我们对于操作符的讲解就谢下帷幕了,如果觉得作者写的,你收获到了知识,那就给作者点点赞吧,如果有不对的地方,希望大佬指正出来。作者会及时修改的。

在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

橘柚!

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

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

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

打赏作者

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

抵扣说明:

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

余额充值