C语言:操作符以及部分表达式介绍

2023-11-06

目录

1 .  操作符

1.1   算数操作符

1.2   移位操作符

1.3   位操作符

1.4 .1   赋值操作符

1.4.2   复合赋值符

1.5   单目操作符

1.6   关系操作符

1.7   逻辑操作符

1.8   条件操作符

1.9   逗号操作符

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

2.   布尔值

3.   左值和右值

4.   表达式求值

4.1   隐式类型转换

4.2   算术转换

4.3   操作符的属性


1 .  操作符

1.1   算数操作符

常用算数操作符:

+        -        *        /        %

其中除了%操作符外,其余几个操作符既适用于浮点类型又适用于整数类型。

当 / 操作符的两个操作数都是整数时,执行整除运算,其他情况执行浮点数除法。

%(取模操作符),它接受两个整型操作数,把左操作数除以右操作数,返回的值是余数而不是商。

1.2   移位操作符

移位操作符移动的是二进制位。(二进制位简称“位”,是二进制计数系统中表示小于2的整数的符号,一般用0或1表示)。左移位操作符为<<,右移位操作符为>>。左操作数的值(操作符左边的数)将移动由右操作数指定的位数。两个操作数都必须是整型类型。

下图以整数2具体表示:

 移位操作只是简单地把一个值地位向左或向右移动。在左移位中,值最左边的几位被丢弃,右边多出来的几个空位则由0补齐。

如下图:

 而右移位操作符在像移动后,对左边补充进的数字有两种方案。一种是逻辑移位,左边移入的位用0填充;另一种是算数移位,左边移入的位由原先该值的符号位决定(PS:正数最左边为0,负数最左边为1),符号位为1则移入的位均为1,符号位为0则移入的位均为0,这样能够保持原数的正负形式不变。

例如,值10010110右移两位,逻辑移位的结果是00100101,但算数移位的结果是11100101。

注意:标准说明无符号值执行的所有移位操作都是逻辑移位,但对于有符号值,采取哪种移位方案则取决于编译器。如果移位的位数比操作数的位数还要多,会发生不可预测的效果(同样由编译器决定)。

1.3   位操作符

位操作符有:& (与)            | (或)            ^(异或)

当两个位进行&操作时,如果两个结果都为1,结果为1,否则都为0。当两个位进行 | 操作时,如果两个位都是0,结果为0,否则结果为1。当两个为进行 ^ 操作时,如果两个位不同,结果为1,如果两个位相同,结果为0。

位操作符要求操作数为整数类型,它们对操作数对应的位进行指定操作,每次对左右操作数的各一位进行操作。

举例如下图:

1.4 .1   赋值操作符

赋值操作符用一个等号表示。赋值是表达式的一种,而不是某种类型的语句。所以,只要是允许出现表达式的地方都允许进行赋值。

如:

x = y + 3;

这一语句包含两个操作符,+ 和 =。首先进行加法运算,所以 = 的操作数是变量 x 和 表达式 y+3 的值。 赋值操作符就是把右操作数的值存储于左操作数指定的位置。但赋值也是个表达式,表达式就具有一个值。赋值表达式的值就是左操作数的新值,它可以作为其他操作符的操作数。

如下面的语句:

a = x = y + 3;

由于赋值操作符的结合性(求值的顺序)是从右到左,所以这个表达式也相当于:

a =  ( x = y + 3 );

它的意思和下面的语句组合完全相同:

x = y + 3;
a = x;

又如下面这个稍微复杂一些的例子:

r = s + ( t = u - v ) / 3;

这条语句把表达式 u - v 的值赋给 t,然后把 t 除以3,再把除法的结果和s相加,其结果再赋值给r。等同于下面的语句:

t = u - v;
r = s + t / 3;

两相比较之下,不难发现后者的写法更好一些,因为它们更便于日常的阅读和调试。人们在编写内嵌赋值操作的表达式时很可能想在一行就表达完自己的意思,但这样做也许会造成表达式难于阅读。因此,在使用这个“特性”之前,要确信这一方法能带来更大的收益。

1.4.2   复合赋值符

目前所介绍的操作符还有一种复合赋值的操作形式:

+=          -=          *=          /=          %=

<<=        >>=       &=         ^=           | =

以其中的+=操作符为例(其余与它非常相似,只是使用的操作符不同),+=操作符的用法如下:

a += 2;

相当于

a = a + (2);

通常比起后者我更建议使用前者的方式,在熟练之后,将会发现+=的形式能使代码写得更加清楚。另外,编译器可以产生更加紧凑的代码。

如下的例子:

a [ 2 * ( y - 6 * f(x) ) ] = a [ 2 * ( y - 6 * f(x) ) ] + 1;

a [ 2 * ( y - 6 * f(x) ) ] += 1;

在第一种形式中,由于编译器不知道函数 f 是否有副作用(编译器根据对代码进行优化的需要自行规定对实参的求值顺序。有的编译器规定自左至右,有的编译器规定自右至左),所以它必须两次计算下标表达式的值。而第二种只计算一次,效率更高。

1.5   单目操作符

单目操作符是只接受一个操作数的操作符。是

!        ++        -        &        sizeof

~          --         +        *      ( 类型)

接下来逐一介绍:

!  操作符对它的操作数执行逻辑反操作:如果操作数为真,其结果则为假。如果操作数为假,其结果为真。这个操作符实际上产生一个整型结果,0(假)或 1(真)。

~   操作符对整型类型的操作数进行求补操作,操作数中原先为1的位变为0,所有原先为0的位变为1.

-    操作符产生操作数的负值。

+   操作符产生操作数的值(啥也不干,单纯对称)。

&   操作符产生它的操作数的地址。(有机会在指针里面会讲,算了先简略的讲讲)

 

//定义一个变量a,和一个指针b
int a = 3, *b;    //变量b   类型是int *      所以写成int *b  
 ...
b = &a;

上面的语句声明了一个整型变量和一个指向整型变量的指针。接着,&操作符取变量a的地址,并把它赋值给指针变量(提供门牌号)。

*   操作符是间接访问操作符,它与指针一起使用,用于访问指针所指向的的值。在前面例子中的赋值操作完成之后,表达式b的值就是变量a的地址,而进行解引用后(*b这样解引用),*b的值则是变量a的值(拿到门牌号,敲门,拿a里存放的东西)。

sizeof   操作符判断它的操作数的类型长度,以字节位单位表示(一个字节等于8个比特位)操作数既可以是个表达式(常常是单个变量),也可以是两边加上括号的类型名,如下两个例子:

sizeof( int )                    sizeof (x)

第1个表达式返回整型变量的字节数,其结果取决于你所使用的环境(32位环境,和64位环境)。第2个表达式返回变量x所占据的字节数。

 注意!!!判断表达式的长度并不需要对表达式进行求值,所以sizeof(a=b+1)并没有对a赋任何值。

(类型)   这一操作符被称为强制类型转换。它用于显示地把表达式的值转换为另外的类型。例如

int a是个整形变量,在通过(float)a进行强制类型转换后就变成可浮点数值。

++   操作符具有两种使用形式,前缀形式和后缀形式。两种都执行+1操作。前缀形式的++操作出现在操作数的前面。操作数被增加,表达式的值变为操作数增加后的值。后缀形式的++操作出现在操作数的后面。操作数的值仍被增加,但表达式的值是操作数增加前的值。通过下面的例子可以更好的理解:

int a, b, c, d;

a = b = 10;        //a 和 b被赋值为10

c = ++a;           //前缀形式  a先增加到11,然后c才能的到值11.

d = b++;           //后缀形式  d先得到b的值10,然后b才会增加到11.

--   操作符与++操作符类似,同样有两种形式,前缀和后缀。不过执行的是-1操作。

1.6   关系操作符

这类操作符用于测试操作数之间的各种关系。这类操作符是:

>        >=        <        <=        !=        ==

前四个用于比较大于,大于等于,小于和小于等于。

而!=的意思是不等于,用于判断两者是否不同。如:

int a = 3;
int b = 4;
if ( a != b )
    return false;

==操作符则用于比较两者是否相同。

注意!!!切勿搞混 = (赋值) 和 == (比较),两者意思完全不同!!!

1.7   逻辑操作符

逻辑操作符有&&(且) 和 ||(或)。虽然看上去像位操作符,但具体作用却完全不同。

expression1 && expression2,如果expression1和expression2的值都为真,那么整个表达式的值也是真的,如果两个表达式任何一个表达式的值位假,那么整个表达式的值则为假。

首先通过下面这段代码来看&&操作符:

int a = 3;
int b = 6;
if ( a < 5 && b > 5)
{
    printf("nice");
}

由于&&操作符的优先级比>和<操作符的优先级都要低,所以表达式相当于(a>5)&& (b>5)。满足条件则打印nice。

尽管&&操作符的优先级较低,但它任然会对两个关系表达式施加控制。&&操作符的左边的操作数总是首先进行求值,如果它的值为真,才会对右边的操作数进行求值。如果左操作数为假,那么右操作数便不再进行求值。如:

int a = 3;
int b = 5;
int c = 0;
int d = 0;
if ( ( (c = a - 5) > 0) && ( (d = b + 1) > 0))
{
    printf("nice");
}

在这一代码中,c的值由于进行a - 5的赋值变为了-2,而-2 > 0为假,那么&&的有边便不在进行判断,也就是说 d = b + 1 这一操作将不在进行,不管它的值是真是假,都不进行,d的值依然是0.

而|| 操作符(或)也具有相同的特点,它首先对左操作数进行求值,如果它的值是真,那么右操作数变不再求值。而如果左操作数进行求值后为假,才会对右操作数进行求值,若两者都为假,则返回假,两者有一个为真,则为真。如:

int a = 3;
int b = 6;
if ( a > 5 || b > 5)
{
    printf("ncie");
}

左为假,右为真,则依然为真。上述的这种行为往往被称为“短路求值”(很形象吧)。

注意!!!不要把&&和&,||和|搞混,一个是逻辑操作符,一个是位操作符,不能乱来。

1.8   条件操作符

条件操作符接受三个操作数(也被称为三目运算)。下面使它的用法:

expression1        ?        expression2        :        expression3

条件操作符的优先级非常低,所以它的各个操作数(?和:)即使不加括号,一般也不会有问题。但是为了更加清楚,还是加上比较稳妥。

首先计算的是expression1,如果它的值为真(非零值),那么整个表达式的值就是expression2的值,如果expression1的值为假,那么整个条件语句的值就是expression3的值。举例如下:

int b = 0;
int a = 5;

(a > 4) ? (b=1) : (b=2);

由于a>4为真,那么b便被赋值为1.如果a>4为假,那么b的值将被赋为2.

1.9   逗号操作符

逗号操作符将两个或多个表达式分搁开来,这些表达式自左向右逐个进行求值,整个逗号表达式的值就是最后那个表达式的值。例如:

int a = 1;
int b = 4;
int c = 5;

if (a+2, b / 2, c > 0)
{
    printf("nice");
}

首先a+2变为3,其次b / 2变为2,然后再将最后一个式子作为判断条件,c>0为真。打印nice.

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

这些操作符如果有机会将在以后联系其他内容进行详细介绍,这边先提一嘴。

下标引用操作符是一对方括号[ ].它接受两个操作数:一个数组名和一个索引值。事实上,下标引用并不仅限于数组名(还是有机会^ _ ^)。下标引用的下标值总是从零开始的。并且不会对下标值进行有效性检查。除了优先级不同之外,下标引用操作和间接访问表达式是等价的。这里是它们两者的关系:

array [ 下标 ]

*( array + ( 下标 ) )

通常编译器在编译过程中会自动的将第一种转换为第二种。

函数调用操作符接受一个或多个操作数。它的第一个操作数是你希望调用的函数名,剩余的操作数就是传递给函数的参数。把函数调用以操作符的方式实现意味着“表达式”可以替代“常量”作为函数名(以后会详细讲解)。

. 和 ->操作符用于访问一个结构的成员。如果s是个结构变量,那么访问s.a就访问名叫a的成员。当你拥有一个指向结构的指针而不是结构本身,且与访问它的成员时,就需要->操作符而不是.操作符(我将会在结构体一文中进行详细讲解,如果你没找到我这篇文章,就说明我太懒了,记得评论区踹我一脚- _ -)。

2.   布尔值

C并不具备显示的布尔类型,所以使用整数来代替。其规则是:

零是假,任何非零值都为真。(标准并没有说1这个值比其他任何非零值“更真”,只是我们为了日常方便才这么说的。)

3.   左值和右值

!!!为了理解有些操作符存在的限制,必须理解左值和右值之间的区别!!!

左值就是哪些能够出现在赋值符号左边的东西。右值就是那些可以出现在赋值符号右边的东西,你也许认为我在说废话文学,但这很重要。左值意味着一个位置(房子),右值意味着一个值。      例如:

a = b + 25;

a是个左值,因为它标识了一个可以存储结果值的地方(房子),b + 25是个右值,因为它指定了一个值。但是请注意!!!

它们并不可以互换。

b + 25 = a;

原先作左值的a此时可以作为右值,这没问题,因为每个位置包含一个值。然而,b + 25 不能作为左值,因为它并为标识一个特定的位置(就像我在上面提到的一排房子一样,b房子门牌号+了25你就不知道是谁家了,不能随便访问!!!)。因此这条赋值语句是非法的。同样你也不能将一个字面值常量作为左值,你不能将5 = 6(相当于强行撬开存放5这一数据的房子,再把5拉出来,然后把6塞进去,这犯法啊!!!)。所以在使用右值的地方也可以使用左值,但是在需要左值的地方不能使用右值。

4.   表达式求值

4.1   隐式类型转换

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

char    a, b, c;
 ...
a = b + c;

b和c的值将被提升为普通整型,然后再执行加法运算。加法运算的结果将被截断,然后在存储到a中(比如int型占4个字节,char类型占1个字节,你要把4个字节硬塞到1个字节里,这肯定不行啊,这尺寸不对啊,于是便会发生截断,至于截断,和整型提升我将会单独写一篇文章来进行说明。如果没有,就踢我一脚/dog)。

4.2   算术转换

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

long double

double

float

unsigned long int

long int

unsigned int

int

如果某个操作数的类型在上面这个列表中排名较低(小的变大),那么它首先将转换为另外一个操作数的类型再执行操作。

4.3   操作符的属性

复杂表达式的求值顺序是由三个因素决定的:操作符的优先级,操作符的结合性以及操作符是否控制执行的顺序。两个相邻的操作符哪个先执行取决于它们的优先级,如果两者优先级相同,那么它们的执行顺序由它们的结合性决定。简单地说,结合性就是一串操作符是从左向右依次执行还是从右向左逐个执行。(关于操作符优先级将在下一篇文章中详细介绍。)

_____________________________________________________________________________仓促成文,如有内容偏颇之处,望指正。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C语言:操作符以及部分表达式介绍 的相关文章

随机推荐

  • flask框架实现文件下载功能

    传入文件名即可下载文件 from flask import Flask send file Response send from directory app Flask name app route download def downloa
  • Python编程题

    把数组 0 1 1 0 1 1 0 1 1 1 0 0 中所有的1排到左侧 0排到右侧 方法1 思路 1 首先进行可以保证0在左侧 1在右侧 2 新建一个空列表 3 把原列表中的值从最后1个复制给新建列表 直到第一个元素被复制完 list1
  • Qt 画图,void A::paintEvent(QPaintEvent *event){..}这函数怎么调用它?

    不用调用 需要用这个函数的时候调用A gt update 就可以得到调用这个函数的目的
  • shell中单引号、双引号、反引号的用法及区别

    单引号 这个比较暴力 不管单引号里面有什么都原样输出 无视一切变量 所见即所得 如果要用来做字符比较和输出 注意不能输出变量 也不认识通配符 命令等 even ubuntu echo a PATH aa a PATH aa 双引号 双引号感
  • Leetcode刷题总结-3.二叉树篇

    Leetcode刷题总结 二叉树刷题心得 总结 文章目录 Leetcode刷题总结 前言 一 二叉树刷题思路 二 美团面试题 2 1 第十套卷面试题 2 2 第九套卷面试题 三 华为研发工程师编程题 四 华为2016研发工程师编程题 前言
  • 【华为OD机试真题2023B卷 JAVA&JS】太阳能板最大面积

    华为OD2023 B卷 机试题库全覆盖 刷题指南点这里 太阳能板最大面积 知识点分治 时间限制 1s 空间限制 32MB 限定语言 不限 题目描述 给航天器一侧加装长方形或正方形的太阳能板 图中的红色斜线区域 需要先安装两个支柱 图中的黑色
  • 【项目总结】基于SpringBoot+Ansj分词+正倒排索引的Java文档搜索引擎项目总结

    文章目录 项目介绍 开发背景 主要用到的技术点 前端 后端 Ansj分词 实现索引模块 实现Parser类 实现Index类 完善Parser类 优化制作索引速度 实现搜索模块 实现DocSearcher类 处理暂停词 项目编写过程中遇到的
  • Hadoop系统入门之Join在MapReduce中的实现

    MapReduce Interview 描述如何使用MapReduce来实现join的功能 考察点 1 MapReduce执行流程 2 JOIN的底层执行过程 3 JOIN的多种实现方式 ReduceJoin shuffle MapJoin
  • 怎样将计算机32位换为62位,电脑32位怎么换62位

    大家好 我是时间财富网智能客服时间君 上述问题将由我为大家进行解答 电脑32位换62位的方法如下 1 备份原来的操作系统中的文件 特别注意收藏夹 我的文档 以及桌面等位置的文件 2 准备好64位操作系统的安装光盘或者ISO映像 制作操作系统
  • 关于Boolean==Boolean和Boolean=Boolean的启示

    昨晚做java面试题 一道原题是这样的 来自 JAVA最最最 基本的面试题 慕课手记 http www imooc com article 13754 6 给出以下代码 请问该程序的运行结果是什么 class Example public
  • C++中将csv文件中的数据存储到数组中

    ifstream fin filename c str 以输入方式打开文件存到缓冲空间fin中 string line int i 0 int comma 0 while getline fin line 读取fin中的整行字符存在line
  • 责任链中的嵌套使用

    1 按照之前的业务 用户建档后 挂号 生成挂号费用 根据挂号费 根据用户的身份生成不同的金额 儿童或者特殊人群挂号免费 2 具体实现 我们需要增加几个符合过滤器 public class CompositeFreeFilter extend
  • Winform ListView控件用法

    用法一 1直接从工具箱里把ListView拖到控件上 2可在窗体的Load 事件里 写如下代码 设置一些常用属性 listView1 View View Details listView1 LabelEdit true listView1
  • javascript全量匹配屏蔽词

  • iptables官方手册整理

    iptables官方手册整理 目录 1 简介 2 首先 什么是包过滤 3 快速入门指南 4 数据包过滤流程 5 具体如何使用Iptables命令实现过滤功能 6 地址转换 NAT 7 排除建议 1 简介 读者们 大家好 在这里我们假设你已经
  • 安装golang项目的 GVM

    GITHUB地址 https github com moovweb gvm bash lt lt curl s S L https raw githubusercontent com moovweb gvm master binscript
  • keras fine-tune方法

    https blog csdn net jdzwanghao article details 80697104
  • lzma算法分析

    lzma算法分析 这几天在公司主要在做压缩相关 记录一下所得 目前业界主流的压缩算法感觉并不多 好用的就Huffman lz系列 其他的像差分编码 vlq编码 感觉只能做个数据预处理 或者一种小范围的压缩 lz系列有很多 主要有lz77 l
  • 禁用系统【快应用】,停止【快应用】自动弹出

    快应用 九大厂商同时宣布建立即时应用生态发展联盟 通过统一标准让开发者低成本接入 快应用 在研发接口 场景接入 服务能力和接入方式上建设标准平台 以平台化的生态模式对个人开发者和企业开发者全品类开放 此次九大厂商共建 快应用 标准和平台 最
  • C语言:操作符以及部分表达式介绍

    目录 1 操作符 1 1 算数操作符 1 2 移位操作符 1 3 位操作符 1 4 1 赋值操作符 1 4 2 复合赋值符 1 5 单目操作符 1 6 关系操作符 1 7 逻辑操作符 1 8 条件操作符 1 9 逗号操作符 1 10 下标引