操作符详解(下)

2024-09-01 21:12
文章标签 详解 操作符

本文主要是介绍操作符详解(下),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

6、单目操作符

!、++、--、&、*、+、-、~、sizeof、(类型)

单目操作符只有一个操作数,除了&、*,剩下的我们之前讲过了,这两个我们再之后的指针我们再讲。

7、逗号表达式

表达式,表达式,表达式,表达式

用逗号隔开的多个表达式就是逗号表达式

逗号表达式依次从左往右执行,最后的表达式就是整个表达式的值;

比如下面代码,看c的逗号表达式,从左往右执行,a>b为假是0,a,这两个表达式没有任何影响,接着a+10,赋予b,b=11,最后b>a,为真,返回1就是整个表达式的结果;

int main()
{int a = 1;int b = 3;int c = (a > b, a, b = a + 10, b > a);return 0;
}

有时候,a<b,是这个判断的条件,那从左往右计算,是会影响到最后的表达式的结果的,所以逗号表达式前面计算也是重要的;

 if(a = b + 10,b = c / 5,a < b)

举个例子,假设有个APP函数的值赋予a,a又是CPP函数的参数,接着while循环,如果a>0,执行代码,执行完后,再继续之前的代码,那这样写就比较冗余,我们可以用逗号表达式写出来;

int main()
{a = APP();CPP(a);while (a > 0){//执行代码a = APP();CPP(a);}return 0;
}

用逗号表达式写的效果是一样的,先执行APP函数,CPP函数,再判断a是否大于0,再执行代码,循环完一次后再进行判断,再执行APP,CPP函数; 


int main()
{while (a = APP(),CPP(a), a > 0){//执行代码}return 0;
}

8、下标访问[ ]、函数调用(  )

8.1 [  ]下标引用操作符

比如我们有一个数组,我们想打印数组的第7个元素,那就是arr[6],访问数组的元素下标6,

那[ ]就是下标引用操作符,[  ]下标引用操作符的操作数有两个,一个是数组名,一个是元素下标,这两个操作数,少一个都不行,是找不到对应的元素的;

int main()
{int arr[10] = { 1,2,3,4,5,6,7 };printf("%d\n", arr[6]);return 0;
}

 8.2函数调用操作符

比如我们最简单的printf函数后面的括号( )就是函数调用操作符,add( )这个也是函数调用操作符,那函数调用操作符的操作数有几个?

这是不固定的,比如下面第一行代码操作数是函数名printf和字符串“hahaha”,操作数有两个;第二行代码操作数就是函数名add,参数2,参数3,操作数有3个;第三行代码,操作数只有一个函数名,因为它没有参数,只有函数名,那操作数就是1个;所以是不固定的;

int main()
{printf("hahaha\n");//( )操作数就是printf和“hahaha”int r = add(2, 3);//( )操作数就是add,2,3text();//( )操作数就是textreturn 0;
}

9、结构体成员访问操作符

 9.1结构体

C语言已经提供了内置类型,比如char、short、int、long、float、double,但是只有这些类型是不够的,比如要描述一个学生,学生的名字用char,但是学生的成绩、学生的体重、年龄呢?不能只用一种数据类型来描述学生,学生是一个复杂对象,那我们自定义一个合适的数据类型,结构体就是自定义的数据类型,

结构是一些值得结合,那这些值就是成员变量;

首先先声明结构体,那里面的变量就是成员变量,比如学生的名字,年龄,成绩;

成员变量可以有一个或是多个;

struct Stu
{char name[20];  //名字int  age;       //年龄float score;   //成绩
};

 9.1.1结构体的声明

先是结构体的关键字struct,tag就是结构体的名字,这个可以随便起,括号里面的就是成员变量列表,下面就是变量列表,可写可不写,但是最后的分号;一定要加上;

struct tag{member-list;}variable-list;

 9.1.2结构体变量的定义和初始化

我们定义了结构体类型,那类型就相当于一张图纸,有了图纸,我们就可以按照图纸去盖房子,我们有了学生的数据类型,我们就可以去创建一个“学生”;

那我们创建了数据类型,那就是数据类型+变量名;

int main()
{struct Stu s1;struct Stu s2;return 0;
}

那我们定义变量,那我们就可以初始化; 

那我们说数组给它多个元素,我们用括号,那结构体里面有这么多变量,我们也用括号括起来;

比如s1学生,名字是张三,年龄是18,成绩是95.5;

我们这是按着顺序给它赋值的,那能不能不按顺序给它数值呢?

struct Stu
{char name[20];  //名字int  age;       //年龄float score;   //成绩
};int main()
{struct Stu s1 = { "张三",18,95.5f };struct Stu s2 = { "李四",19,90.5f };return 0;
}

那我们可以通过结果成员访问操作符,来实现这个操作;

  .  是结构成员访问操作符,.name访问结构里的name...........

int main()
{struct Stu s1 = { .age = 18,.score = 95.5f,.name = "张三" };struct Stu s2 = { .score = 90.5f,.name = "李四",.age = 19 };return 0;
}

 那我们再复杂一点,说结构体里面还有一个结构体呢?

结构体Stu里面还有一个结构体BB,结构体BB里面有两个成员变量char a、int b;

那我们再初始化的时候,既然它还是一个结构体,那我们就还是用{ }括号给它赋值,就像s1那样;

也可以向s2那样,.bb.a、.bb.b、bb是个结构体也可以用结构成员访问操作符;

struct AA
{char a;int b;
};struct Stu
{char name[20];  //名字int  age;       //年龄struct AA  BB;float score;  //成绩
};int main()
{struct Stu s1 = { .age = 18,.score = 95.5f,.name = "张三" ,.BB = {"A",1}};struct Stu s2 = { .score = 90.5f,.name = "李四",.age = 19,.BB.a = "2",.BB.b = 1 };return 0;
}

9.2结构成员访问操作符

9.2.1结构体成员的直接访问

那创建好变量,赋值了,我们可以打印出来,比如我们打印s1学生的名字,用结构成员访问操作符,就是结构体变量.成员名;

int main()
{struct Stu s1 = { .age = 18,.score = 95.5f,.name = "张三" ,.BB = {"A",1}};struct Stu s2 = { .score = 90.5f,.name = "李四",.age = 19,.BB.a = "2",.BB.b = 1 };printf("%s\n",s1.name);printf("%d\n",s1.age);return 0;
}

10、操作符的优先级和结合性

 操作符有两个重要的属性就是优先级和结合性,决定了表达式求值的计算顺序。

10.1优先级

 比如在一个表达式里面有多个操作符,哪一个操作符先算呢?那不同的操作符的优先级是不一样的。

*的优先级比+的优先级高,所以先算5*6,乘法,再算3+30,加法;

3+5*6

10.2结合性

 如果两个操作符的优先级是一样的,那就是看结合性了,看它是左结合,从左往右计算,还是右结合,从右往左计算;大部分是操作符是左结合,左往右计算,但是有少部分是右结合,从右往左计算,比如赋值运算符 = 

* 和 / 的优先级是相同的,它们的结合性都是左结合运算符,那就是先算5*6,再 / 2;

5 * 6 / 2

 运算符的优先级顺序很多,我们只要大概记记了解一些常用的运算符优先级就行

下列按照优先级从高到低排列;

1、()圆括号;比如5 * 6 / 2,我就先算6/2,那就那就可以5 * (6 / 2);

2、自增运算符(++)、自减运算符(--)

3、单目运算符+、-

 4、乘法 *、除法  /

 5、加法+、减法-

6、关系运算符(<、>等)

7、赋值运算符 =

我们也有一个表格可以查看

https://zh.cppreference.com/w/c/language/operator_precedence

11、表达式求值

11.1整型提升

C语言中整型算术运算都是以整型类型的精度来计算的;

如果表达式中有char和short,那再使用之前先将它们转换为普通整型,那这种转换就叫做整型提升,char的底层是 ASCII码,它和short也被归为int类型里面;

那a+b就是整型提升;

int main()
{char a = 5;char b = 126;char c = a + b;return 0;
}

 整型提升的意义

表达式的整型运算是在CPU相应的运算器上运行的,CPU的整型运算器的操作数的字节长度一般是int的字节长度,因此,两个char类型相加,在CPU上实际也要先转换为CPU整型运算器操作数的字节长度,CPU是难以实现两个8比特位字节直接相加的,所以,表达式中各种长度小于int长度的整型值,都必须先转换位int后者unsigned int,再给CPU计算。

 如何整型提升?

1、有符号整数的整数提升是按照数据的符号位来提升的

2、无符号整数的整数提升,高位补 0

5的原码放在char里面,5的二进制位是32个比特位,char是8个比特位,放在char里面我们就要截断成8个比特位,126也是同样的道理;

a+b整型运算,对a,b整型提升,就要提升到32个比特位,在当前编译器char是有符号整型, 所以剩下的24个比特位,用这个数据的符号位来提升;

a和b整型提升后的结果是32个比特位,放在char里面也是要截断的;

当我们打印c,用%d打印,%d打印的是有符号的整型,%u打印的是无符号的整型,c还要整型提升,提升完后,这是c在内存的补码,但是打印的是原码,还要取反+1,得到原码;

这就i是char和short在整型运算的过程;

int main()
{char a = 5;//00000000 00000000 00000000 00000101//00000101 --截断char b = 126;//00000000 00000000 00000000 01111110//01111110 --截断char c = a + b;//00000000 00000000 00000000 000000101 --a整型提升//00000000 00000000 00000000 011111110 --b整型提升//00000000 00000000 00000000 100000011//截断//10000011 --c//用%d打印是有符号整型//对c整型提升//11111111 11111111 11111111 100000011 --补码//打印的是原码//10000000 00000000 00000000 011111101 c原码printf("%d\n",c);return 0;
}

11.2算术转换

 如果操作符的两个操作数的类型是不一样的,那就需要算术转化,另一个操作数的类型转换位另一个操作数的类型,是类型相同,可以运算;

算术转换是按照下面表格从下往上转换的;

假设一个操作数类型是float,另一个操作数是int类型,那就i是int操作数向float操作数转换变成float类型,其他的同理;

long double

double

float

unsigned long int

long int

unsigned int

int

 11.3问题表达式解析

11.3.1 表达式1

这个表达式是存在问题的,它的计算顺序不是唯一的;

a*b + c*d + e*f

 它的顺序可能是两种,都是符合表达式运算的优先级,结合性,但这不是唯一的计算路径,是会有两个结果的,比如我们写一个计算银行利息的代码,一个计算是50,另一个计算是50000,这些很可怕的;

11.3.2表达式2

这个表达式是有问题的,在优先级来说,先计算--,再计算+,但是这个表达式右边的--是知道了,那左边的c是--c之后的c,还是--c之前的c,是有争议的,这也不是唯一的计算路径。

 c + --c;

 11.3.3表达式3

这个代码是有问题的,这段代码放在不同的编译器结果是不一样的,里面的i的运算,编译器也会凌乱的,这种代码是不能这么写的,你要么拆分,存在变量里,再计算。


int main(){int i = 10;i = i-- - --i * ( i = -3 ) * i++ + ++i;printf("i = %d\n", i);return 0;}

11.3.4表达式4

这段代码的fun函数里的变量用了static,变量的值是会累计的,fun函数第一次返回的是2,第二次返回的是3,第三次返回的是4,那再main函数里面谁是第一次返回,谁是第二次返回,谁是第三次返回,这是不确定的。

#include <sdtio.h>int fun(){static int count = 1;return ++count;}int main(){int answer;answer = fun() - fun() * fun();}printf( "%d\n", answer);return 0

11.3.5表达式5

这段代码也是有问题的,这段代码放在不同编译器的结果也是不一样的。

#include <stdio.h>int main(){int i = 1;int ret = (++i) + (++i) + (++i);printf("%d\n", ret);printf("%d\n", i);return 0;}

11.4总结

我们学了运算表达式的优先级,结合性,我们写出来的表达式不是万无一失的,通常这种表达式是有问题的,它计算的路径不是唯一的,对于以上的表达式,我们不要写成这样,我们可以根据需要,把这些表达拆分,或者用括号确定它的优先级,拆分用变量存起来,再去计算。

感谢观看,感谢指正!

这篇关于操作符详解(下)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1128052

相关文章

PHP轻松处理千万行数据的方法详解

《PHP轻松处理千万行数据的方法详解》说到处理大数据集,PHP通常不是第一个想到的语言,但如果你曾经需要处理数百万行数据而不让服务器崩溃或内存耗尽,你就会知道PHP用对了工具有多强大,下面小编就... 目录问题的本质php 中的数据流处理:为什么必不可少生成器:内存高效的迭代方式流量控制:避免系统过载一次性

MySQL的JDBC编程详解

《MySQL的JDBC编程详解》:本文主要介绍MySQL的JDBC编程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录前言一、前置知识1. 引入依赖2. 认识 url二、JDBC 操作流程1. JDBC 的写操作2. JDBC 的读操作总结前言本文介绍了mysq

Redis 的 SUBSCRIBE命令详解

《Redis的SUBSCRIBE命令详解》Redis的SUBSCRIBE命令用于订阅一个或多个频道,以便接收发送到这些频道的消息,本文给大家介绍Redis的SUBSCRIBE命令,感兴趣的朋友跟随... 目录基本语法工作原理示例消息格式相关命令python 示例Redis 的 SUBSCRIBE 命令用于订

使用Python批量将.ncm格式的音频文件转换为.mp3格式的实战详解

《使用Python批量将.ncm格式的音频文件转换为.mp3格式的实战详解》本文详细介绍了如何使用Python通过ncmdump工具批量将.ncm音频转换为.mp3的步骤,包括安装、配置ffmpeg环... 目录1. 前言2. 安装 ncmdump3. 实现 .ncm 转 .mp34. 执行过程5. 执行结

Python中 try / except / else / finally 异常处理方法详解

《Python中try/except/else/finally异常处理方法详解》:本文主要介绍Python中try/except/else/finally异常处理方法的相关资料,涵... 目录1. 基本结构2. 各部分的作用tryexceptelsefinally3. 执行流程总结4. 常见用法(1)多个e

SpringBoot日志级别与日志分组详解

《SpringBoot日志级别与日志分组详解》文章介绍了日志级别(ALL至OFF)及其作用,说明SpringBoot默认日志级别为INFO,可通过application.properties调整全局或... 目录日志级别1、级别内容2、调整日志级别调整默认日志级别调整指定类的日志级别项目开发过程中,利用日志

Java中的抽象类与abstract 关键字使用详解

《Java中的抽象类与abstract关键字使用详解》:本文主要介绍Java中的抽象类与abstract关键字使用详解,本文通过实例代码给大家介绍的非常详细,感兴趣的朋友跟随小编一起看看吧... 目录一、抽象类的概念二、使用 abstract2.1 修饰类 => 抽象类2.2 修饰方法 => 抽象方法,没有

MySQL8 密码强度评估与配置详解

《MySQL8密码强度评估与配置详解》MySQL8默认启用密码强度插件,实施MEDIUM策略(长度8、含数字/字母/特殊字符),支持动态调整与配置文件设置,推荐使用STRONG策略并定期更新密码以提... 目录一、mysql 8 密码强度评估机制1.核心插件:validate_password2.密码策略级

从入门到精通详解Python虚拟环境完全指南

《从入门到精通详解Python虚拟环境完全指南》Python虚拟环境是一个独立的Python运行环境,它允许你为不同的项目创建隔离的Python环境,下面小编就来和大家详细介绍一下吧... 目录什么是python虚拟环境一、使用venv创建和管理虚拟环境1.1 创建虚拟环境1.2 激活虚拟环境1.3 验证虚

详解python pycharm与cmd中制表符不一样

《详解pythonpycharm与cmd中制表符不一样》本文主要介绍了pythonpycharm与cmd中制表符不一样,这个问题通常是因为PyCharm和命令行(CMD)使用的制表符(tab)的宽... 这个问题通常是因为PyCharm和命令行(CMD)使用的制表符(tab)的宽度不同导致的。在PyChar