你必须知道的495个C语言问题,学习体会三
本文是 本系列的第三篇,本文主要对C语言的表达式做个小结
先从两个坑爹的表达式说起:i++ 与++i
上大学的时候,学长告诉我,这两个表达式,意义是一样的,后来老师纠正说,还是有区别的,于是让我们记住以下观点:i++是先使用i的值,再对i进行+1操作,而后者则刚好相反,先将i+1,再使用i的值。一直都是这么记得,笔试也没错过,也就信了老师了。更合理的解释是:++i在存储的值上增加1 并向使用它的表达式“返回”新的、增加后的值,而i++对i增加1,但返回的是原来的、未增加的i的值。
但是,据本书所解释的: i++ 或者i--在输出其旧值才会执行运算,但这里的“之后”的含义和准确定义常常被误解,无法保证自增或者自减会在放弃变量原值之后和对表达式的其他部分进行计算之前立即进行,只能保证变量的更新会在表达式“完成”之前的某个时刻进行。
回到学长的观点,以下语句的有区别么?
for (int i = 0;i<5;i++)
for (int i = 0;i<5;++i)
从达到的目的上看,确实没有区别。都实现了i的递增加1 ,但是linux之父 linus告诉我们,后者比前者的运行效率更高,因为后者没有产生中间量,在很多次的运行中,这种差异还是有的,于是,我相信了,以后在我所有的循环自加的语句中都使用++i的形式。这在C++中 ,区别就更加明显了,不信可以重构下++ 操作符,如果你也懂C++的话。
现在思考以下表达式:
a[i] = i++;
a[i] = ++i;
这种计算依赖于计算机编译器,在我的x86_64-w64-mingw32-gcc编译下,二者遵循老师的运算逻辑,但是,这样的代码是应该避免的,毕竟,我们并不怕多一个中间变量。
是的,凡是与表达式相关的不清晰的表达,都可以用增加中间变量来解决。
a[i] = i++;=====> j = i;a[i] = j; i++;
a[i] = ++i;=====> j = i+1;a[i] = j;i++;
表达式 自身的 运算以及 运算符的优先级问题 本身不应该成为问题,这样的问题 最好还是留在笔试题目中,我们在实际编码中,更关心代码的清晰度和易读性,因此,不建议乱用高级的表达式来达到 简单化代码的目的,实在要用,可以多用括号表达。
于是,有以下建议可以避免未定义的求值顺序问题:
1.确保一个表达式最多只修改一个对象:一个简单变量、一个数组成员或者一个指针指向的位置,“修改”是指=操作符的简单赋值,+=、-=或者*=操作符的复合赋值或者++ --操作符的前后缀形式
2.如果一个对象在一个表达式 中出现一次以上 而且在表达式中被修改,则要确保该对象的所有读访问都被用于计算它的最终值,这条规则允许表达式i=i+1 ——尽管i出现两次而且也被修改了,但对i的旧值是用于计算i的新值
3.如果想要破坏第一条规则,就要确保修改的对象互不相同。表达式c = *p++是合法的,因为修改的两个对象c和p互不相同,
4.如果在两次修改或者修改和访问之间置入定义的序列点操作符,则可以破坏第一条规则和第二条规则,这个表达式(通常在一个while循环中看到,用来读入一行的内容)是合法的,因为 第二次访问变量c出现在&&引入的序列点之后。
(c = getchar()) != EOF && c != '\n'
其他知识
- && 与|| 的短路规则,
- 左值 与 右值 ,可赋值变量
你必须知道的495个C语言问题,学习体会三的更多相关文章
- 你必须知道的495个C语言问题,学习体会一
C语言作为一门古老的语言,其灵活性和容易出错都让人 又爱又恨,书籍<你必须知道的495个C语言问题>,使用问答的形式,告诉读者 C语言使用的各个方面的知识,包括一些冷知识等.以下,我要摘录 ...
- C语言学习书籍推荐《你必须知道的495个C语言问题》
萨米特 (Steve summit) (作者), 孙云 (译者), 朱群英 (译者) 下载地址:点我 <你必须知道的495个C语言问题>以问答的形式组织内容,讨论了学习或使用C语言的过程中 ...
- 《你必须知道的495个C语言问题》知识笔记及补充
1. extern在函数声明中是什么意思? 它能够用作一种格式上的提示表明函数的定义可能在还有一个源文件里.但在 extern int f(); 和 int f(); 之间并没有实质的差别. 补充:e ...
- 你必须知道的495个C语言问题,学习体会四
本文,我们来学习下指针,这是个梦魇啊.无数次折磨着C语言学习者,无数次的内存泄露,无数次的访问失败,无数次的越界溢出, 这些错误造就的仅仅是一个 跟随者,真正的优秀者必须要正视语言的局限,同时在最大限 ...
- 你必须知道的495个C语言问题,学习体会二
这是本主题的第二篇文章,主要就结构体,枚举.联合体做一些解释 1.结构体 现代C语言编程 结构化的基石,diy时代的最好代言人,是面向对象编程中类的老祖宗. 我们很容易定义一个结构体,比如学生: st ...
- 你必须知道的495个c语言问题(笔记)
1.1我该如何决定使用哪种整数类型? 用到较大的数用long:空间很重要(例如有很大的数组或很多的结构)用short:此外用int. win32: int 32bit 4byte char 8b ...
- 《你必须知道的495个C语言问题》读书笔记之第11-14章:ANSI C标准、库函数、浮点数
一.ANSI C标准 1. ANSI向C语言预处理器引入了几项新的功能,包括“字符串化”操作符(#).“符号粘贴”操作符(##).#pragma指令. 2. Q:char a[3] = "a ...
- 《你必须知道的495个C语言问题》读书笔记之第8-10章:字符串、布尔类型和预处理器
一.字符和字符串 1. Q:为什么strcat(string, '!')不行? A:strcat()用于拼接字符串,所以应该写成strcat(string, "!")." ...
- 《你必须知道的495个C语言问题》读书笔记之第4-7章:指针
1. Q:为什么我不能对void *指针进行算术运算? A:因为编译器不知道所值对象的大小,而指针的算法运算总是基于所指对象的大小的. 2. Q:C语言可以“按引用传参”吗? A:不可以.严格来说,C ...
随机推荐
- (转)牛B的代码不一定是好代码
最近经常做业务逻辑代码review的工作,发现各种风格的代码,其中有一种是封装和抽象做的非常的多,代码层次非常的深入,表面给人感觉是:牛逼的代码. 但是从清晰度和可维护性来说,还是不推荐这么做. 1. ...
- eclipse 安装 spring boot suite 插件遇到的问题
问题:安装失败,报如下错误: An error occurred while collecting items to be installedsession context was:(profile= ...
- 112. Path Sum (判断路径和是否等于某值)
Given a binary tree and a sum, determine if the tree has a root-to-leaf path such that adding up a ...
- [转]Linux下RPM软件包的安装及卸载 yum操作
在 Linux 操作系统下,几乎所有的软件均通过RPM 进行安装.卸载及管理等操作.RPM 的全称为Redhat Package Manager ,是由Redhat 公司提出的,用于管理Linux 下 ...
- Bean的id、name、ref、refid
Spring中Bean的命名 1.每个Bean可以有一个id属性,并可以根据该id在IoC容器中查找该Bean,该id属性值必须在IoC容器中唯一: 2.可以不指定id属性,只指定全限定类名,如: & ...
- Apache 优化
1.关闭DNS和名字解析 * HostnameLookups on | off | double 2.关闭客户端主机名解析 Apache .3之前HostnameLookups默认是打开的.这样客户端 ...
- 会话控制Cookie的应用
Cookie是一种由服务器发送给客户端的片段信息,存储在客户端浏览器的内存或者硬盘上,在客户端对服务器的请求中发回它.PHP透明地支持HTTP Cookie.可以利用他在远程浏览器端存储数据并以此来跟 ...
- JAVA基础补漏--反射
获得CLASS的三种方式: 1.Class.forname("全类名"):将字节码文件加载进内存,返回Class对象. 多用于配置文件,将类名放到配置文件中,读取配置文件,加载类 ...
- MSER(Maximally Stable Extremal Regions)算法总结
ER与ER Tree形成过程 判断ER是否位MSER条件 MSER源码分析
- 一位交易巨匠的十年心得:如何使用MACD判断后期趋势,把握买卖点
高手与散户的区别在哪里? 高手与散户的区别在哪里,从宏观上讲:一流高手用境界,二流高手用趋势,三流高手用技术,普通散户用迷糊.最高境界的人在讲心境如何,什么也不看,只用感觉就能炒好股赚钱.我说一流高手 ...