C语言教程——操作符详解(2)

目录

前言

1.sizeof和数组(补充)

2.关系操作符

3.逻辑操作符

4.条件操作符

5.逗号表达式

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

6.1 [ ] 下表引用操作符

6.2( ) 函数调用操作符

6.3访问一个结构的成员

7.表达式求值

7.1隐式类型转换

7.2算数转换

7.3操作符的属性

总结


前言

这里接着上一篇的文章内容接着学习和讲解,基本上本篇就把操作符这一部分全部讲解完毕。


这里我们接着再讲一下sizeof和数组之间的关系和使用方法。

1.sizeof和数组(补充)

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

这里创建了两个不同类型的数组,一个是整形,一个是字符型,同时这里写了两个函数,在主函数里面调用这两个函数。

第一条打印数据因为sizeof里面是数组名,这里计算的是数组的大小,因为arr是十个整形元素,一个整形元素是4个字节,所以打印出来就是40,下一条打印也是计算数组的大小,因为ch里面有十个字符型元素,字符型元素占1个字节,所以这里打印的就是10。

而test1和test2是函数,函数传参传入的是数组名,而数组名是首元素的地址,也就是传入的是个指针,sizeof里面计算的是指针的大小,也就是4或者8,因为32位平台下指针位是4个字节,64位平台下指针位是8个字节,test2传入的也是指针,所以输出也是4或者8个字节。

只要是地址(指针)就是4或者8个字节,VS中X86就是32位环境,X64是64位环境。

2.关系操作符

>

>=

<

<=

!=                          用来测试“不相等”

==                         用于测试“相等”

这些关系运算符都比较简单,没有什么可以进行讲解的,但是我们要注意一些运算符使用的时候的陷阱。

注意:在编程的时候==和=不小心写错,导致的错误。

C语言中两个相等是用来判断的,比较的,但是一个等号就是赋值。

3.逻辑操作符

逻辑符号:

&&                逻辑与

| |                  逻辑或

逻辑与又称为并且,逻辑或又称为或者,之前的按位与(&)和按位或(|)是通过二进制进行运算的。但这里的逻辑与和逻辑或是针对于真假来进行的。

对于逻辑与(&&),两边都为真才为真,有假就为假。

对于逻辑或(|   |),有真就为真,两个为假才为假。

这里可以用自己的方法来记住,只要理解为并且和或者就可以,中文语义并且就是两个都的意思,或者就是只要有一个就行。

这里来看下列代码:

int main( )
{int a=10;  //int a=0;int b=5;   //int b=0if(a && b){pritnf("ok\n");}return 0;
}

这里两个都为真,才会打印ok,如果把其中的一个数改成0,那么就不会打印ok。

int main( )
{int a=10;  //int a=0;int b=5;   //int b=0if(a || b){pritnf("ok\n");}return 0;
}

这里就是只要两个有一个非0,也就是有一个为真,那么就会打印ok,只有两个都为假的时候才不会打印ok。

这里我们就可以通过这两个运算符来写一个判断是否为闰年的程序了,就不用套好几层 if 了:

int main( )
{int year=0;scanf("%d\n",&year);if(((year % 4 == 0) && ( year % 100 !=0)) || ( year % 400 ==0)){printf("Yes\n");}return 0;
}

逻辑与(并且)操作符来说,有一个特点,这里用一个程序来进行讲解:

int main( )
{int i = 0, a = 0, b = 3, d = 4;i = a++ && ++b && d++;pritnf("a = %d\nb = %d\nc = %d\nd = %d\n",a,b,c,d);return 0;
} 

这里可以自己先思考一下,之后再看解析。

这里我们都以为输出是1 3 3 4,但是错了,正确答案是1 2 3 4 ,这里因为a++,因为是后置加加,所以这里还是0,0后面跟上逻辑与,由于逻辑与的特点就是里面要是有假的话,那么就为假。而逻辑与的特点就是左边(前面)只要有假了,后面就不需要进行运算了,只有左面(前面)为真的时候,才会继续往后进行判断。

逻辑或(或者)操作符来说:

int main( )
{int i = 0, a = 1, b = 2, c = 3, d = 4;i = a++ || ++b || d++;pritnf("a = %d\nb = %d\nc = %d\nd = %d\n",a,b,c,d);return 0;
} 

对于逻辑或来说,左边要是为真,右边就无需进行计算,所以最后的结果就是2 2 3 4。因为在a++的时候因为是后置加加,所以只判断a,a这里是真,a判断完成之后再进行加1,因为第一个就为真了,所以后面无需进行计算,最后输出也就是 2 2 3 4。

4.条件操作符

exp1 exp2 exp3

这里exp叫做表达式,上面就是三个表达式,如果表达式1的结果为真,那么表达式2就计算,表达式3不计算;如果表达式1为假,那么表达式2不计算,表达式3计算。

条件操作符也叫做三目操作符,涉及到了三个操作数。

可以看下面的例子:

int main( )
{int a =10;int b =20;int m =0;if(a>b)m =a;elsem =b;m=(a>b?a:b);return 0;
}

这里上面的一对代码就可以用下面的一行来搞定,如果a>b就执行a,如果不符合,就是b,所以这就是三目操作符。

5.逗号表达式

exp1, exp2, exp3, .... expN

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

逗号表达式,从左到右依次执行。整个表达式的结果就是最后一个表达式的结果。

看下面的一个例子:

int a =1;
int b =2;
int c =(a>b, a=b+10, a, b=a+1);

表达式从左到右依次运算,最后的结果就是最后的一个表达式的结果。上述代码从左到右,第一个表达式没有影响,第二个表达式a被赋值为b+10,也就是赋值了12,表达式3就是a+1,也就是13。

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

这里也是从左到右依次运算,最后的结果还是表达式3的最后的结果,所以这里if语句真正判断的就是表达式3(d>0)的条件。

接着再看一个例子:

a = get_val( );
count_val(a);
while(a>0)
{//业务处理a =get_val( );count_val(a);
}

这里是先对a进行赋值,之后再调用这个函数,再判断循环执行循环里面的语句。则这几行语句就可以通过逗号表达式改成下面的语句:

while(a =get_val( ), count_val(a), a>0)
{//业务处理
}

这里就可以用这一行来代替上面的这一堆代码。

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

6.1 [ ] 下表引用操作符

操作数:一个数组名+一个索引值

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

这个[ ]就是下标引用操作符,这里面操作数就是arr和4,这里就是打印下标为4的数,也就是5。

6.2( ) 函数调用操作符

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

int main( )
{int len = strlen("abcdef");return 0;
}

这里就是用了string库里面的strlen函数,()就是函数的调用操作符,操作数就是:strlen,"abcdef",函数调用操作符至少有一个操作数。

6.3访问一个结构的成员

.            结构体.成员名

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

这里简单的讲一下结构体,后续还会进行深入讲解:

结构体(自定义类型)(聚合类型),生活中有些对象要被描述的话,就不能简单的使用内置类型,比如我们要描述一本书,那不可能只描述一个信息,例如:书名,作者,出版社,定价等等。

例如下列代码:

//类型
struct Book
{char name[20];//书名char author[30];//作者int price;
};int main( )
{struct Book b1={"C语言教程","张三",1000};struct Book b2={"这是书","李四",999};print("《%s》 %s %d\n",b1.name,b1.author,b1.price);return 0;
}

通过定义一个类型结构体,在结构体里面加入成员,定义每个成员,之后在主函数里面进行初始化,初始化过后就可以通过 . 来访问,分别访问里面的成员。

如果是结构体指针的话:

void printf(struct Book * p)
{printf("%s %s %d\n",(*p).name,(*p).author,(*p).price);printf("%s %s %d\n",p->name,p->author,p->price);
}

 就用->后面接成员名来进行访问。结构体变量就用 . 进行访问。

7.表达式求值

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

7.1隐式类型转换

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

整形类型是用于类型小于整形的。

我们知道char 1个字节  short 2个字节  int 4个字节

整型提升就是把char或者是short类型变成int类型,之后再参与运算。

就是偷偷的进行着类型转换,它是在一直运行着,只是我们看不见。

int main( )
{char a =3;char b=127;char c= a + b;printf("%d\n",c);return 0;   
}

上述代码就是一个例子。a,b都要提升为整形,之后才会进行运算。

整形提升的意义:

表达式的整形运算要在CPU的相应运算器件内执行,CPU内整形运算器(ALU)的操作数的字节长度一般就是int的字节长度,同时也是CPU的通用寄存器的长度。

因此,即使两个char类型相加,在CPU执行时实际上也要先转换为CPU内整形操作数的标准长度。

通用CPU(general-purpose CPU)是难以直接实现两个8比特字节直接相加运算(虽然机器指令中可能有这种字节相加指令)。所以,表达式中各种长度可能小于int 长度的整形值,都必须先转换为int或者unsigned int,然后才能送入CPU去执行运算。

如何进行整型提升?

假如是负数,那么提升整形的时候,高位补充符号位,负数的符号位是1,所以补的是1,如果有一个字符类型的负数,字符类型是一个字节,一个字节八个比特位,所以高位补充1也就是还需要前面补上24个1。

假如是正数的话,那么提升整形的时候,高位补充符号位,正数的符号位是0,所以高位补0。之后的与负数一样。

对于无符号整形的提升,高位补的是0。

提升之后是补码,还要转化成原码,也就是减一取反。(或者先取反再加1)

char 有符号取值范围:-128~127

        无符号的取值范围:0~255

假设这里是有符号的char,因为char是一个字节(八个比特位),八个比特位的所有可能是总共256种,最高位是符号位,符号位为0的是正数,符号位为1的是负数,正数就按补码来读就行,也就到01111111,也就是127,所以正数就是0~127,负数因为是补码,所以先减去一,再按位取反,11111111就是-1,10000001就是-127,10000000直接被解析成-128,所以范围就是-129~127。

假设这里是无符号的char,所以就没有符号了,也就是0~255。

一般不同类型进行整型提升后它也就与之前的不同了,也就是不相等了,但是如果之前就是整形,那么就不会发生整形提升,比较后不变任然相等。

7.2算数转换

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

long double

double

unsigned long int

long int

unsigned int

int

例如如果计算一个int和一个float类型,那么就先把int转换成float类型,之后再进行计算,按照上面的顺序向上转换,这也是一个隐式转换。

如果某个操作数的类型在上面这个列表中排名较低,那么就要先转换成那个排名高的那个类型,之后再进行执行运算。

算数转换要合理,要不然会出现一些潜在问题:

float f =3.15;
int num = f;

这里发生隐式转换,但是发生了精度缺失。

7.3操作符的属性

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

1.操作符的优先级

2.操作符的结合性

3.是否控制求值顺序

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

下面就是操作符优先级的表:

优先级

运算符名称或含义使用形式结合方向说明

       1

   []

数组下标数组名[长度]

  从左往右

()小括号(表达式)或
函数名(形参表)
  .取成员结构体名.成员
  ->指针结构体指针->成员

       2

  -负号运算符-表达式

  从右往左

单目运算符
()强制类型转换(数据类型)表达式
  ++自增运算符++变量或变量++单目运算符
  --自减运算符--变量或变量--单目运算符
  *取内容*指针变量单目运算符
  &取地址&变量名单目运算符
  !逻辑非!表达式单目运算符
  ~按位取反~整型表达式单目运算符
  sizeof求长度sizeof(表达式)单目运算符

       3 

 /表达式 / 表达式

  从左往右

双目运算符
 *表达式 * 表达式双目运算符
  %取余表达式 / 表达式双目运算符

       4

 +表达式+表达式

  从左往右

双目运算符
  -表达式-表达式双目运算符

       5

<<左移变量<<表达式

  从左往右

双目运算符
>>右移变量<<表达式双目运算符

       6

>大于表达式>表达式

  从左往右

双目运算符
>=大于或等于表达式>=表达式双目运算符
<小于表达式<表达式双目运算符
<=小于或等于表达式<=表达式双目运算符

       7

 ==等于表达式==表达式

  从左往右

双目运算符
 !=不等于表达式!=表达式双目运算符
       8  &按位与表达式&表达式  从左往右双目运算符
       9  ^按位异或表达式^表达式  从左往右 双目运算符
      10  |按位或表达式|表达式  从左往右双目运算符
      11&&逻辑与表达式&&表达式  从左往右双目运算符
      12||逻辑或表达式||表达式  从左往右双目运算符

      13

?:条件运算符表达式1? 表达式2: 表达式3

  从右往左

三目运算符

      14

=赋值运算符变量=表达式

  从右往左

双目运算符

/=除后再赋值变量/=表达式
*=乘后再赋值变量*=表达式
%=取余后再赋值变量%=表达式
+=加后再赋值变量+=表达式
-=减后再赋值变量-=表达式
<<=左移再赋值变量<<=表达式
>>=右移再赋值变量>>=表达式
&=按位与再赋值变量&=表达式
^=按位异或再赋值变量^=表达式
|=按位或再赋值变量|=表达式
      15,逗号表达式表达式,表达式,…  从左往右

如果优先级不知道,就可以加括号。

只有相邻符号才讨论优先级。优先级高的先算,优先级低的后算。优先级相同的时候取决于结合性。有一些操作符会影响求值顺序,逻辑与、逻辑或、条件逻辑符、逗号表达式,都可以控制求值顺序。

注意在写代码时候的时候,不要写问题代码,那样的话编译器会不知道该如何运行,最后的结果就会出现错误,有些问题代码而且在不同的编译器下结果都不同。

我们写出的表达式如果不能通过操作符的属性确定唯一的计算路径,那么这个表达式就是存在问题的。


总结

这就是所有操作符的讲解和分析。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/464656.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

el-date-picker日期选择器动态设置日期

需求&#xff1a;选择开始时间&#xff0c;或者在开始时间已存在的情况下&#xff1b;结束时间下拉日期选择框展示从开始日期展示&#xff1b;而不是当前日期&#xff0c;并且结束时间下拉框日期要禁用开始时间之前的日期。 <el-form-item label"开始时间" prop&q…

web实操2——idea创建普通web项目

创建项目 就是普通的java项目&#xff0c;项目右键add framework support&#xff08;添加框架支持&#xff09;,然后点击Web Application&#xff08;web应用程序&#xff09;&#xff0c;然后点击OK。即可。 文件下就会多一个web文件夹&#xff0c;里面是WEB-INF文件夹&…

ES跟Kafka集成

配合流程 1. Kafka作为分布式流处理平台&#xff0c;能够实时收集和处理不同数据源的数据流&#xff1b; 2. 通过Kafka Connect或者Logstash等中间件&#xff0c;可以将Kafka中的数据流实时推送到Elasticsearch中&#xff1b; 3. Elasticsearch接收到数据后&#xff0c;会根据…

RT-Thread操作系统(2)

RT-Thread操作系统&#xff08;2&#xff09; 目录 RT-Thread操作系统&#xff08;2&#xff09; 设备驱动 IO设备模型框架 PIN设备&#xff08;控制LED灯&#xff09; 软件包开发 DHT11的使用 自动初始化机制 串口 LCD LVGL 连接阿里云和服务器 设备驱动 IO设备模…

多线程--简单模拟实现线程池并使用--Java

一、序言 阅读这篇博客之前建议先读多线程--线程池概念以及使用--Java-CSDN博客&#xff0c;里面有对线程池的详细介绍&#xff0c;这边就不过多赘述。 二、模拟实现固定线程数目的线程池 通过对线程池的理解&#xff0c;我们了解到线程池将我们需要执行的任务Runnable放在阻…

bert-base-chinese模型使用教程

向量编码和向量相似度展示 import torch from transformers import BertTokenizer, BertModel import numpy as npmodel_name "C:/Users/Administrator.DESKTOP-TPJL4TC/.cache/modelscope/hub/tiansz/bert-base-chinese"sentences [春眠不觉晓, 大梦谁先觉, 浓睡…

mutable用法

mutable 关键字用于允许类的某个成员变量在 const 成员函数中被修改。通常&#xff0c;const 成员函数不能改变对象的任何成员变量&#xff0c;但将成员变量声明为 mutable 可以例外 class Hero { public:Hero():m_Hp(0), m_getHpCounter(0){}int getHp() const {m_getHpCounte…

map和set和pair

目录 一.序列式容器和关联式容器 一.set set类的介绍&#xff1a; Construct &#xff1a;set的初始化 insert&#xff1a;插入 ​编辑find&#xff1a;查找 erase&#xff1a;删除 set查找范围的函数&#xff1a;​编辑 二.map 2.1map介绍 2.2pair类型介绍 在map的i…

BEV数据集标注成本高?BEVPose:减少对标注数据依赖!

引言 本文提出了一个名为BEVPose的框架&#xff0c;通过利用自监督和传感器位姿信息&#xff0c;实现相机和激光雷达数据的多模态BEV表示对齐&#xff0c;显著减少了对标注数据的依赖。BEVPose在BEV地图分割任务中表现出色&#xff0c;能够超越全监督的方法&#xff0c;同时提升…

AI - 使用LangChain构建简单LLM应用程序

AI - 使用LangChain构建简单LLM应用程序 什么是LLM LLM&#xff08;Large Language Model&#xff0c;大型语言模型&#xff09;是一种由大量文本数据训练而成的深度学习模型&#xff0c;能够理解和生成自然语言。例如&#xff0c;GPT-3就是一种流行的LLM&#xff0c;可以用于…

linux shell脚本学习(1):shell脚本基本概念与操作

1.什么是shell脚本 linux系统中&#xff0c;shell脚本或称之为bash shell程序&#xff0c;通常是由vim编辑&#xff0c;由linux命令、bash shell指令、逻辑控制语句、注释信息组成的可执行文件 *linux中常以.sh后缀作为shell脚本的后缀。linux系统中文件乃至脚本的后缀并没有…

Linux云计算 |【第五阶段】CLOUD-DAY6

主要内容&#xff1a; 了解Kubernetes的架构、搭建Kubernetes集群 一、Kubernetes 概述 Kubernetes 这个名字来自希腊语&#xff0c;意思是“舵手”或“领航员”&#xff1b;K8S 是 Kubernetes 的缩写&#xff0c;其中“8”代表字母“ubernete”中的8个字母。Kubernetes 是由…

无人机之中继通信技术篇

一、定义与原理 无人机中继通信技术是指通过无人机搭载中继设备&#xff0c;将信号从一个地点传输到另一个地点&#xff0c;从而延长通信距离并保持较好的通信质量。其原理类似于传统的中继通信&#xff0c;即在两个终端站之间设置若干中继站&#xff0c;中继站将前站送来的信号…

【深度学习基础】深入理解 卷积与卷积核

&#x1f308; 个人主页&#xff1a;十二月的猫-CSDN博客 &#x1f525; 系列专栏&#xff1a; &#x1f3c0;深度学习_十二月的猫的博客-CSDN博客 &#x1f4aa;&#x1f3fb; 十二月的寒冬阻挡不了春天的脚步&#xff0c;十二点的黑夜遮蔽不住黎明的曙光 目录 1. 卷积 1.1 …

数字身份发展趋势前瞻:身份韧性与安全

身份韧性与安全是身份与访问管理IAM发展的重要趋势&#xff0c;身份既是防御者的盾牌&#xff0c;也是攻击者的目标。面对日益复杂的网络威胁和不断增长的身份盗窃风险&#xff0c;身份韧性与安全不仅仅涉及产品的防御能力&#xff0c;还包括应对突发事件、快速恢复的弹性和灵活…

QChart中柱形图的简单使用并实现【Qt】

预备工作 如果qt没下载去下载一个&#xff0c;下载太慢了可以试试它[点击跳转]  (https://blog.csdn.net/qq_19319481/article/details/131655379)。   如果已经下载了qt发现自己的组件中没有QCharts&#xff0c;可以去试试它点击跳转。 都搞定了以后在pro文件里面添加QT …

智能语音机器人智能在哪里?AI人工智能电话机器人部署

随着科技的不断进步&#xff0c;人工智能已经成为了我们生活中不可或缺的一部分。AI人工智能机器人电话正是其中的一种形式&#xff0c;可以帮助企业或组织更好地实现电话营销的目标&#xff0c;那么智能语音机器人智能在哪里?我们来看看&#xff1a; 智能语音机器人&#xf…

UI自动化测试 —— CSS元素定位实践!

前言 自动化测试元素定位是指在自动化测试过程中&#xff0c;通过特定的方法或策略来准确识别和定位页面上的元素&#xff0c;以便对这些元素进行进一步的操作或断言。这些元素可以是文本框、按钮、链接、图片等HTML页面上的任何可见或不可见的组件。 在自动化测试中&#xf…

Unity 使用Netcode实现用户登录和登出

Unity之NetCode for GameObjets 基本使用 说明思路相关API代码实现Tips 说明 最近项目需要联机&#xff0c;项目方案选用Unity提供的NetCode for GameObjets&#xff08;以下简称NGO&#xff09;&#xff0c;踩了不少坑&#xff0c;本文不介绍基础使用&#xff0c;围绕双端&…

专业网页设计服务重要是什么

当在搜索引擎中键入“网页设计”&#xff0c;您将获得超过2000万个相关结果。在如此众多的网站制作公司中&#xff0c;应该如何做出明智的选择呢&#xff1f;首先&#xff0c;让我们深入探讨一下网页设计的重要性。网站设计需要综合各种不同领域的专业知识&#xff0c;它是一个…