操作符详解(下)

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

相关文章

一文深入详解Python的secrets模块

《一文深入详解Python的secrets模块》在构建涉及用户身份认证、权限管理、加密通信等系统时,开发者最不能忽视的一个问题就是“安全性”,Python在3.6版本中引入了专门面向安全用途的secr... 目录引言一、背景与动机:为什么需要 secrets 模块?二、secrets 模块的核心功能1. 基

一文详解MySQL如何设置自动备份任务

《一文详解MySQL如何设置自动备份任务》设置自动备份任务可以确保你的数据库定期备份,防止数据丢失,下面我们就来详细介绍一下如何使用Bash脚本和Cron任务在Linux系统上设置MySQL数据库的自... 目录1. 编写备份脚本1.1 创建并编辑备份脚本1.2 给予脚本执行权限2. 设置 Cron 任务2

一文详解如何在idea中快速搭建一个Spring Boot项目

《一文详解如何在idea中快速搭建一个SpringBoot项目》IntelliJIDEA作为Java开发者的‌首选IDE‌,深度集成SpringBoot支持,可一键生成项目骨架、智能配置依赖,这篇文... 目录前言1、创建项目名称2、勾选需要的依赖3、在setting中检查maven4、编写数据源5、开启热

Python常用命令提示符使用方法详解

《Python常用命令提示符使用方法详解》在学习python的过程中,我们需要用到命令提示符(CMD)进行环境的配置,:本文主要介绍Python常用命令提示符使用方法的相关资料,文中通过代码介绍的... 目录一、python环境基础命令【Windows】1、检查Python是否安装2、 查看Python的安

HTML5 搜索框Search Box详解

《HTML5搜索框SearchBox详解》HTML5的搜索框是一个强大的工具,能够有效提升用户体验,通过结合自动补全功能和适当的样式,可以创建出既美观又实用的搜索界面,这篇文章给大家介绍HTML5... html5 搜索框(Search Box)详解搜索框是一个用于输入查询内容的控件,通常用于网站或应用程

Python中使用uv创建环境及原理举例详解

《Python中使用uv创建环境及原理举例详解》uv是Astral团队开发的高性能Python工具,整合包管理、虚拟环境、Python版本控制等功能,:本文主要介绍Python中使用uv创建环境及... 目录一、uv工具简介核心特点:二、安装uv1. 通过pip安装2. 通过脚本安装验证安装:配置镜像源(可

C++ 函数 strftime 和时间格式示例详解

《C++函数strftime和时间格式示例详解》strftime是C/C++标准库中用于格式化日期和时间的函数,定义在ctime头文件中,它将tm结构体中的时间信息转换为指定格式的字符串,是处理... 目录C++ 函数 strftipythonme 详解一、函数原型二、功能描述三、格式字符串说明四、返回值五

LiteFlow轻量级工作流引擎使用示例详解

《LiteFlow轻量级工作流引擎使用示例详解》:本文主要介绍LiteFlow是一个灵活、简洁且轻量的工作流引擎,适合用于中小型项目和微服务架构中的流程编排,本文给大家介绍LiteFlow轻量级工... 目录1. LiteFlow 主要特点2. 工作流定义方式3. LiteFlow 流程示例4. LiteF

CSS3中的字体及相关属性详解

《CSS3中的字体及相关属性详解》:本文主要介绍了CSS3中的字体及相关属性,详细内容请阅读本文,希望能对你有所帮助... 字体网页字体的三个来源:用户机器上安装的字体,放心使用。保存在第三方网站上的字体,例如Typekit和Google,可以link标签链接到你的页面上。保存在你自己Web服务器上的字

MySQL存储过程之循环遍历查询的结果集详解

《MySQL存储过程之循环遍历查询的结果集详解》:本文主要介绍MySQL存储过程之循环遍历查询的结果集,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录前言1. 表结构2. 存储过程3. 关于存储过程的SQL补充总结前言近来碰到这样一个问题:在生产上导入的数据发现