《C和指针》笔记21:表达式求值

这篇具有很好参考价值的文章主要介绍了《C和指针》笔记21:表达式求值。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1. 隐式类型转换

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

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

b和c的值被提升为普通整型,然后再执行加法运算。加法运算的结果将被截短,然后再存储于a中。这个例子的结果和使用8位算术的结果是一样的。但在下面这个例子中,它的结果就不再相同。这个例子用于计算一系列字符的简单检验和。

a = ( ~a ^ b << 1 ) >> 1;

由于存在求补和左移操作,所以8位的精度是不够的。

2. 算术转换

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

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

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

int a = 5000;
int b = 25;
long c = a * b;

表达式a*b是以整型进行计算,在32位整数的机器上,这段代码运行起来毫无问题,但在16位整数的机器上,这个乘法运算会产生溢出,这样c就会被初始化为错误的值。解决方案是在执行乘法运算之前把其中一个(或两个)操作数转换为长整型

  • 当整型值转换为float型值时,也有可能损失精度。float型值仅要求6位数字的精度。如果将一个超过6位数字的整型值赋值给一个float型变量时,其结果可能只是该整型值的近似值。

  • 当float型值转换为整型值时,小数部分被舍弃(并不进行四舍五入)。如果浮点数的值过于庞大,无法容纳于整型值中,那么其结果将是未定义的。

3. 操作符的属性

下表列出了操作符的所有的属性,其中包括

  • 描述
  • 用法示例
  • 结果类型:lexp表示左值表达式,rexp表示右值表达式。

左值(可以实现在赋值符号左边的东西)意味着一个位置,右值(可以实现在赋值符号右边的东西)意味着一个值,所以使用右值的地方可以使用左值,但是需要左值的地方不能使用右值。

  • 结合性:从左往右写为L-R,从右往左写为R-L。没有结合性写作N/A。
  • 是否控制求值顺序:是/否。
操作符 描述 用法示例 结果类型 结合性 是否控制求值顺序
() 聚组 (表达式) 与表达式同 N/A
() 函数调用 rexp(rexp, ..., rexp) rexp L-R
[] 下标引用 rexp[rexp] lexp L-R
. 访问结构成员 lexp.member_name lexp L-R
-> 访问结构指针成员 rexp->member_name lexp L-R
++ 后缀自增 lexp++ rexp L-R
-- 后缀自增 lexp-- rexp L-R
! 逻辑反 !rexp rexp R-L
~ 按位取反 ~rexp rexp R-L
+ 单目,表示正值 +rexp rexp R-L
- 单目,表示负值 -rexp rexp R-L
++ 前缀自增 ++lexp rexp R-L
-- 前缀自减 --lexp rexp R-L
* 间接访问 *rexp lexp R-L
& 取地址 &lexp rexp R-L
sizeof 取其长度 sizeof rexp/sizeof(类型) rexp R-L
(类型) 类型转换 (类型)rexp rexp R-L
* 乘法 rexp * rexp rexp L-R
/ 除法 rexp / rexp rexp L-R
% 整数取余 rexp % rexp rexp L-R
+ 加法 rexp + rexp rexp L-R
- 减法 rexp - rexp rexp L-R
<< 左移位 rexp << rexp rexp L-R
>> 右移位 rexp >> rexp rexp L-R
> 大于 rexp > rexp rexp L-R
>= 大于等于 rexp >= rexp rexp L-R
< 小于 rexp < rexp rexp L-R
<= 小于等于 rexp <= rexp rexp L-R
== 等于 rexp == rexp rexp L-R
!= 不等于 rexp != rexp rexp L-R
& 位与 rexp & rexp rexp L-R
^ 位异或 rexp ^ rexp rexp L-R
| 位或 rexp | rexp rexp L-R
&& 逻辑与 rexp && rexp rexp L-R
|| 逻辑或 rexp || rexp rexp L-R
?: 条件操作符 rexp ? rexp : rexp rexp N/A
= 赋值 lexp = rexp rexp R-L
+= 以…加 lexp += rexp rexp R-L
-= 以…减 lexp -= rexp rexp R-L
*= 以…乘 lexp *= rexp rexp R-L
/= 以…除 lexp /= rexp rexp R-L
%= 以…取模 lexp %= rexp rexp R-L
<<= 以…左移 lexp <<= rexp rexp R-L
>>= 以…右移 lexp >>= rexp rexp R-L
&= 以…与 lexp &= rexp rexp R-L
^= 以…异或 lexp ^= rexp rexp R-L
|= 以…或 lexp |= rexp rexp R-L
, 逗号 rexp, rexp rexp L-R

4. 优先级和求值顺序

两个相邻操作符的执行顺序由它们的优先级决定。如果它们的优先级相同,它们的执行顺序由它们的结合性决定。除此之外,编译器可以自由决定使用任何顺序对表达式进行求值,只要它不违背逗号、&&||?:操作符所施加的限制。

对于表达式

a * b + c * d + e * f

其求值顺序可能是

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

也可能是

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

加法运算的结合性要求两个加法运算按照先左后右的顺序执行,但它对表达式剩余部分的执行顺序并未加以限制。尤其是,这里并没有任何规则要求所有的乘法运算首先进行,也没有规则规定这几个乘法运算之间谁先执行。优先级规则在这里起不到作用,优先级只对相邻操作符的执行顺序起作用。

由于表达式的求值顺序并非完全由操作符的优先级决定,所以像下面这样的语句是很危险的。

c + --c

--cc之前或之后执行,表达式的结果在两种情况下将会不同。像这样的表达式是不可移植的,应该予以避免。

编译器只要不违背优先级和结合性规则,它可以自由决定复杂表达式的求值顺序。表达式的结果如果依赖于求值的顺序,那么它在本质上就是不可移植的,应该避免使用。

不同的编译器可以以任何顺序对类似下面这样的表达式进行求值。

a + b + c
x * y * z

之所以允许编译器这样做是因为b+c(或y*z)的值可能可以从前面的一些表达式中获得,所以直接复用这个值比重新求值效率更高。加法运算和乘法运算都具有结合性,这样做的缺点在什么地方呢?

考虑下面这个表达式,它使用了有符号整型变量:

if( x + y + 1 > 0 )

如果表达式x+y的结果大于整型所能容纳的值,它就会产生溢出。在有些机器上,下面这个测试的结果将取决于先计算x+y还是y+1,因为在两种情况下溢出的地点不同。问题在于我们无法肯定地预测编译器将按哪种顺序对这个表达式求值。

下面的表达式也说明了这样一个问题:

f() + g() + h()

尽管左边那个加法运算必须在右边那个加法运算之前执行,但对于各个函数调用的顺序,并没有规则加以限制。如果它们的执行具有副作用,比如执行一些I/O任务或修改全局变量,那么函数调用顺序的不同可能会产生不同的结果。因此,如果顺序会导致结果产生区别,你最好使用临时变量,让每个函数调用都在单独的语句中进行。

temp = f();
temp += g();
temp += h();

其他注意的小点:

  1. 有符号值的右移位操作是不可移植的。
  2. 移位操作的位数是个负值。
  3. 使用复合赋值符可以使程序更易于维护。
  4. 使用条件操作符替代if语句以简化表达式。
  5. 使用逗号操作符来消除多余的代码。
  6. 不要混用整型和布尔型值。

相关参考
1.《C和指针》文章来源地址https://www.toymoban.com/news/detail-701007.html

到了这里,关于《C和指针》笔记21:表达式求值的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Go基础12-理解Go语言表达式的求值顺序

    Go语言在变量声明、初始化以及赋值语句上相比其先祖C语言做了一些改进,诸如: ● 支持在同一行声明和初始化多个变量(不同类型也可以) ● 支持在同一行对多个变量进行赋值 这种语法糖在给我们带来便利的同时,也可能带来一些令人困惑的问题。 Go语言之父Rob Pike在

    2024年02月09日
    浏览(58)
  • C语言——表达式求值中类型转换和优先级等问题

    目录 1.隐式类型转换 2.算数转换 ​3.操作符的属性 C的整型算术运算总是至少以缺省整型类型的精度来进行的。 为了获得这个精度,表达式中的字符和短整型操作数在使用之前被转换为普通整型,这种转换称为 整型提升 。 整型提升的意义: 表达式的整型运算要在CPU的相应运

    2024年02月05日
    浏览(86)
  • 【数据结构】利用顺序栈/链栈完成表达式求值(C语言实现)

    利用顺序栈完成表达式求值(将字符型转换为整型) 程序代码: #include stdio.h #include malloc.h #include stdlib.h #include math.h #define MAXSIZE 100 #define ElemType char #define LEN sizeof ( ElemType ) typedef struct {     ElemType * data;     int top; } SqStack ; void InitStack( SqStack * S ) {     S -data = ( ElemType *)

    2024年02月05日
    浏览(55)
  • 初始C语言(6)——详细讲解表达式求值以及其易错点

     第一章 “C“浒传——初识C语言(1)(更适合初学者体质哦!)  第二章 初始C语言(2)——详细认识分支语句和循环语句以及他们的易错点   第三章 初阶C语言(3)——特别详细地介绍函数  第四章 初始C语言(4)——详细地讲解数组的内容以及易错点  第五章 初

    2024年02月12日
    浏览(37)
  • c++表达式求值

    给定一个表达式,其中运算符仅包含 +,-, ,/(加 减 乘 整除),可能包含括号,请你求出表达式的最终值。 注意: 数据保证给定的表达式合法。题目保证符号 - 只作为减号出现,不会作为负号出现,例如,-1+2,(2+2) (-(1+1)+2) 之类表达式均不会出现。题目保表达式中所有数字均

    2024年01月21日
    浏览(38)
  • 表达式求值和转换

    2024年02月06日
    浏览(37)
  • 栈|逆波兰表达式求值

    逆波兰表达式求值 逆波兰表达式就是后缀表达式,我们平时写的带括号的是中缀表达式。区分中缀表达式和后缀表达式 就是 操作数 和 操作符 的先后关系。 操作符在后 就是后缀表达式 后缀表达式 的用途就是 让计算机直到计算的先后顺序! 比如 我们中缀表达式 a * (b -

    2024年04月11日
    浏览(48)
  • C++ 数据结构 栈 中缀表达式转后缀表达式并求值

    写在前面,这里用的是我自己写的Stack类,并非STL,实现方法为静态数组,但使用过程中的函数方法一样,无伤大雅。(完整code和Stack_static类赋在最后) 1.从左到右遍历 2.数,即参与运算数,直接放进后缀表达式之后 3.左括号 ,直接压入栈(因为括号的优先级最高,无需判断

    2024年02月03日
    浏览(49)
  • 中缀表达式求值(栈的应用)

    AcWing算法基础课-3302.表达式求值 给定一个表达式,其中运算符仅包含 +,-,*,/ (加 减 乘 整除),可能包含括号,请你求出表达式的最终值。 注意: 数据保证给定的表达式合法。 题目保证符号 - 只作为减号出现,不会作为负号出现,例如, -1+2 , (2+2)*(-(1+1)+2) 之类表达式均不

    2024年02月05日
    浏览(55)
  • 数据结构之表达式求值

     前言 运用堆栈解决表达式的求值,代码思路为: 1.定义两个栈,一个char类型的栈用于存放运算符(ysf)一个int类型的栈用于存放操作数(czs) 如一个表达式3+6*9,将“+”,“*”入ysf栈,将“3”“6”“9”入czs栈 2.运用getchar进行数据的录入,如果接收的是运算符,将其插入到运

    2024年04月29日
    浏览(35)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包