﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-局部变量的作用域-随笔分类-转载</title><link>http://www.cppblog.com/localvar/category/15393.html</link><description /><language>zh-cn</language><lastBuildDate>Tue, 09 Nov 2010 01:59:59 GMT</lastBuildDate><pubDate>Tue, 09 Nov 2010 01:59:59 GMT</pubDate><ttl>60</ttl><item><title>extern "C"</title><link>http://www.cppblog.com/localvar/archive/2008/03/10/132756.html</link><dc:creator>局部变量</dc:creator><author>局部变量</author><pubDate>Mon, 10 Mar 2008 01:16:00 GMT</pubDate><guid>http://www.cppblog.com/localvar/archive/2008/03/10/132756.html</guid><wfw:comment>http://www.cppblog.com/localvar/comments/132756.html</wfw:comment><comments>http://www.cppblog.com/localvar/archive/2008/03/10/132756.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/localvar/comments/commentRss/132756.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/localvar/services/trackbacks/132756.html</trackback:ping><description><![CDATA[<a href="http://www.newsmth.net/bbstcon.php?board=CPlusPlus&amp;gid=218259">原文</a><br>本以为很简单，仔细阅读了一下 C++ 标准，发现内容还不少。总结了一下。<br><br>要点：<br><br>函数类型，函数名，变量名具有语言链接性，language linkage。<br><br>语言链接性可能会影响到名字以及调用约定等，由实现决定。<br><br>C++ 默认的语言连接性是 C++ 语言链接性。<br><br>语言链接性仅作用于函数类型，函数名，变量名。<br><br>不同语言链接性的函数类型是不同的类型，即便其余的地方都相同。<br><br>语言链接性用链接性规格（linkage-specification）来声明，分有无大括号两种形式。<br>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;extern string-literal { declaration-seq opt }<br>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;extern string-literal declaration;<br><br>所有的实现都必须支持 "C" 和 "C++" 链接性。<br><br>链接性规格允许嵌套，此时最内层的那个起作用，但是并不建立作用域。<br>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;extern "C" { extern "C++" { class A{}; } }<br>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;extern "C" { class B:A{};}<br>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;因为不是作用域，B 可以看到 A<br><br>如果C链接性施加到C++类成员和成员函数的类型，忽略C链接性，但是其余的地方依然有效，比如成员函数的参数。<br>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;比如：<br>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;extern "C" { class A{ void f(void (*p)()){} }; }<br>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;A 不是函数和变量，没有语言链接性<br>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;f 是 C++ 成员函数，忽略所指定的 C 链接性，如果在外层没指定别的，就是<br>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;C++链接性。<br>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;p 具有 C 链接性。<br><br>除了C++链接性的函数外，同一个函数不带链接性规格的声明的函数不能早于带的。<br>如果前面声明了带链接性规格的形式，后面又出现了不带的形式，不受影响。<br>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;比如 extern "C" void foo(); void foo(); 是可以的，反过来不行。<br>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;但是 extern void foo(); extern "C++" void foo(); 可以。<br><br>特定名字的 C 语言链接性的函数最多只能有一个，即便放在不同的 namespace 里，<br>也是同一个函数，变量也是如此。这样的函数或变量也不能重复定义。<br>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;namespace A { extern "C" void f(); }<br>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;namespace B { extern "C" void f(); }<br>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;两个 f 是同一个<br><br>有链接性规格的函数具有外部链接性。<br>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;因此<br>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;extern "C" void f();<br>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;static void f();<br>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;是错误的。<br><br>链接性规格，带大括号的形式，不影响里面的声明是声明还是定义。<br>单个声明的形式，视为 extern 限定符。<br>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;extern "C" int i; &nbsp; &nbsp; &nbsp; 是声明；<br>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;extern "C" { int i; } &nbsp; 是定义；<br>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;extern "C" { extern int i; } 又是声明。<br><br>单个声明形式的连接性规格，不能再指定存储类别。<br>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;extern "C" static void f(); // error<br><br>其他说明：<br>因为语言链接性只跟链接性有关，因此 C 语言链接性的函数也可以有默认参数，<br>函数的接口可以有引用。<br><br>标准 C++ 库中的符号默认是 C++ 链接性。<br>C++ 用的标准 C 库中的外部链接性符号可以是 C 或者 C++ 语言链接性，推荐后者。<br>任何带有连续两个下划线的名字保留给实现用作同时具有 C 和 C++ 链接性的名字。<br><br>标准 C 库中的任何函数签名都保留给实现用来做同时具有 C 和 C++ 链接性的名字。<br><br>因为不同语言连接性的相同签名的函数类型算作不同类型，所以可以相互重载，不算同<br>一个函数。<br>//commented by ctrlz<br>//这里应该是ill-formed. <br>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;extern "C" int atexit(void (*f)(void)) &nbsp; // 这里的 f 是 C 链接性<br>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;extern "C++" int atexit(void (*f)(void)) // 这里的 f 是 C++ 链接性<br><br>类似的有两个原型的函数还有 bsearch，qsort 等带函数指针类型的参数的函数。<br><br>以上都是 C++ 标准里的理论，落实到实践上，为了实现方便，很多编译器都把 C 和 <br>C++ 链接性的函数的类型视为相同的类型，此时的链接性仅仅影响生成的名字，或者<br>还可能影响异常规格，比如把 extern "C" 的函数默认视为 throw() 的等，并不影响<br>调用约定。同样也为了实现的方便，C++ 所用的 C 库中的符号也都是 C 语言链接性的，<br>因此这样的情况下，atexit, bsearch, qsort 等在这些编译器搭配的库中也就只有一种<br>原型了。<br><br>这种情况下，同样签名的 C 和 C++ 链接性的函数类型就算做同种类型了，下边的程序<br>编译就会失败：<br>//commented by ctrlz.<br>//ill-formed<br>void f(void (*p)())<br>{<br>}<br>extern "C" void f(void (*q)())<br>{<br>}<br><br>VC 和 gcc 在这一点上都是不符合标准的。<br><br>链接性不应该影响函数的类型，就像普通函数的返回类型不影响函数的类型一样，从重载解<br>析的角度看，如果接受以上的行为，势必影响重载解析的过程。<img src="http://blog.vckbase.com/localvar/aggbug/32879.html" width=1 height=1> 
<img src ="http://www.cppblog.com/localvar/aggbug/132756.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/localvar/" target="_blank">局部变量</a> 2008-03-10 09:16 <a href="http://www.cppblog.com/localvar/archive/2008/03/10/132756.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>转载：现代医学真的昌明吗？</title><link>http://www.cppblog.com/localvar/archive/2007/06/19/132773.html</link><dc:creator>局部变量</dc:creator><author>局部变量</author><pubDate>Tue, 19 Jun 2007 03:24:00 GMT</pubDate><guid>http://www.cppblog.com/localvar/archive/2007/06/19/132773.html</guid><wfw:comment>http://www.cppblog.com/localvar/comments/132773.html</wfw:comment><comments>http://www.cppblog.com/localvar/archive/2007/06/19/132773.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/localvar/comments/commentRss/132773.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/localvar/services/trackbacks/132773.html</trackback:ping><description><![CDATA[<p><strong>《人体使用手册》的最后一章&nbsp;&nbsp;&nbsp; 吴清忠</strong><br>&nbsp;&nbsp;&nbsp; 十八世纪，西方发现了细菌，随后发明了抗生素，一举控制了瘟疫。从此奠定了西医权威的地位，同时也将西医的发展走向以微观证据为主的方向，所有医学的技术都朝向微小世界去寻找答案。为了在这个微小世界里找答案，因此发展出愈来愈精密的各种设备，这些设备的愈来愈进步，使人们也觉得医学愈来愈进步了。<br>&nbsp;&nbsp;&nbsp; 经过了近两百多年的发展，到了二十世纪末，在解剖学上，对于人体的各个部份，似乎都已经查清楚了，可是许多疾病的原因却仍然是个迷。也有许多的疾病虽然推断出了疾病的原因，可是依据这些原因所发展出来的医疗方法，并不能真正的把疾病去除。多数的慢性病，只能用药物控制，患者必需终身服药，而医生也很明白的告诉患者，这些药只能减缓疾病的恶化，并不能真正断除疾病的根。实际上除了细菌性的疾病和外科手术以外，西医能够完全治愈的疾病并不多。多数严重的疾病只能控制而不能治愈。<br>&nbsp;&nbsp;&nbsp; <font color=#ff0000>这不禁使我怀疑倒底是医学真正的昌明进步了，还是只有那些用来制造医疗仪器的电子科学进步了，现代医学不过穿了件先进的电子外衣而已。</font><br>&nbsp;&nbsp;&nbsp; 由于整体西医理论建立在解剖学的基础上，因此至今只有个别器官的学说，没有整个人体运行的完整理论模型。例如高血压就认为问题出在心血管，所有治疗完全着重在如何降压。糖尿病就认为问题出在分泌胰岛素的胰脏，就利用药物来平衡胰岛素的分泌。这些方法都建立在&#8220;人体会造成这些症状，必定是一种控制上的失误&#8221;的假设。这是一种完全忽视人体系统智能能力的逻辑。<br>&nbsp;&nbsp;&nbsp; 其实从工程上来分析，如果人体自身具有一个智能型的自动控制系统，这个系统很有可能在发现人体有问题时，能自动调整各种系统的参数，来克服这些问题所造成的影响。<br>&nbsp;&nbsp;&nbsp; 例如由于人体血液浓度改变，血管硬化等原因，使人体以原有的血压，无法将血液送到必需送到的地方时，人体会主动调高血压，来达到目的。也就是说，高血压的现象有可能只是人体的应变措施所造成的结果，它本身并不是一种疾病，而目前的治疗方法主要着重在调整血压，这只能防止血管因压力太大而破裂，并不能消除造成血压上升的真正原因，因此当然不能将之治好。<br>&nbsp;&nbsp;&nbsp; 如果这个假设成立，找出人体采取应变措施的原因，消除这些原因，才是治病的根本之道。<br>&nbsp;&nbsp;&nbsp; 1995年美国曾经出版一本书名为《还我健康（Reclaim to our health）》，揭开美国医学界的许多黑幕。其中对美国医师协会利用各种手段排斥西医以外医疗方法的研究和发展，以及美国医学界和利益团体之间的许多见不得人的事都有很深入的描述。我曾从事投资工作多年，了解西方国家的许多医学研究经费都是由药厂投资的。这一点使我怀疑他们的研究是真的想把人们的疾病去除，还是只想控制疾病。<font color=#ff0000>目前西医的治疗方式是要求患者每天每餐都要吃药，许多疾病都必需终身服药，这是最符合药厂利益的。</font><br>&nbsp;&nbsp;&nbsp; 在这本书中也提到很多这一类医学界轻人命而重利益的实例。最让人惊心的是癌症治疗的两种方法—放射性治疗（放疗）和化学性治疗（化疗）。书中提到放疗最早被用来治疗癌症的动机，完全是美国政府为了降低舆论反对其进行核能军事用途的研究而硬找出来的和平用途。而后透过政府和利益团体的力量，使这项治疗方法让保险公司列为合法的癌症治疗手段，保险公司愿意支付费用。从此这项方法虽然没有实际的证据证明真的能够治疗癌症，但是仍然是目前最主要的医疗手段。<br>&nbsp;&nbsp;&nbsp; 化疗则是另一个类似的例子，开始时它所使用的药剂，是二次大战时的化学武器药剂。同样的也是在没有证据证明它有效的前提下，只要保险公司愿意支付，医院就会推荐患者使用。因为所有的医生都知道这两个疗法都不能治愈疾病，因此他们从不说明到底有多少治愈率，而是强调它一年或五年的存活率有多少。<br>&nbsp;&nbsp;&nbsp; 书中还提到美国对这些放疗和化疗医生所做的问卷调查，问他们&#8220;如果他们自己或自己的家人得了癌症，愿不愿意接受他们经常替患者所做的放疗或化疗治疗？调查的结果出乎意料，居然绝大多数的医生都不愿意，理由是他们自己没有见过真正被治好的患者，但是这些治疗所带给患者的痛苦实在是太可怕了。<br>&nbsp;&nbsp;&nbsp; 中国大陆实施改革开放政策以后，认为西方的东西什么都好，没有认真研究，也不知道外国医学界的黑幕，就大量引进放疗和化疗的设备和治疗方法。最糟糕的是少数不肖医生由于工资低，素质很差，工作非常草率。许多患者在经过简单的检查就认定为癌症，有的甚至切片检查结果还没有出来就认定了，立刻进行化疗，相信有许多人命被糊里胡涂的扼杀了。<br>&nbsp;&nbsp;&nbsp; <font color=#ff0000>最可怕的是这种误诊的结果患者不是被活活吓死，就是被这些治疗手段治死，这时医生会说：&#8220;你看我的诊断没错吧，要不患者怎么会死&#8221;。只要患者死了，就证明医生的诊断是对的，这是非常可怕的矛盾逻辑(Interest conflict)，只要患者被确诊为癌症，就给予医生合法杀人的执照。<br></font>&nbsp;&nbsp;&nbsp; 每一个人环顾自己周围的亲人，总会发现在有限的亲友中，就有许多现代医学无法治愈的疾病。例如失眠、痛风、坐骨神经痛、肌无力、关节炎、过敏性疾病、各种心脏病、高血压、骨刺(骨质增生)、肝炎带原、哮喘、硬皮症、尿毒症、糖尿病、各种癌症&#8230;.等，都是非常普遍的现代人疾病。再从另外的角度来看，几乎人体从头到脚每一个器官都有现代医学束手无策的疾病。这两点使我对现代医学昌明的说法有很大的怀疑，当然对西医的治疗，只着重控制，而没有能力治愈疾病的本质，更有深切的体认。<br>&nbsp;&nbsp;&nbsp; 我再用计算机比喻的另一种情况，来解释目前医学上的一些现象。假设人体像一部计算机，将一部20世纪设计的计算机，交给一个19世纪的最优秀的科学家维修，由于缺乏整体设计理论的基础，科学家只能利用解剖的方法，一边拆解，一边研究，换换这个零件，再换换那个零件，试试看。以现在的眼光来看，这样要能修好计算机是完全需要很好的运气。<br>&nbsp;&nbsp;&nbsp; 同样的现代医学缺乏人体的完整理论，就像19世纪的科学家不了解20世纪的计算机理论一样，只能以解剖尸体的方法，一边拆解，一边研究，换换这个器官，换换那个器官，试试看。这样要能治愈疾病，也就只好完全靠运气了。<br>最要命的是有些权威的医生，自以为完全了解人体，当疾病发生时，就担负起和疾病对抗的作战指挥官，完全采取攻击的手段，就以人体为战场，在战场上和疾病展开激烈的战事。由于对人体知识的一知半解，这些医生的攻击手段，经常也会对人体产生巨大的伤害。有时候甚至分不清敌我，有些攻击手段的攻击目标还是人体真正的作战部队。常常疾病消失了，人体也崩溃了。<br>&nbsp;&nbsp;&nbsp; 现代医学，在基本逻辑上犯的最大错误就是：<font color=#ff0000>低估人体系统的智能能力，高估人类的知识能力。<br></font>举凡科学研究都必需从原始的哲学理论开始，先假设模型，再不断的利用实际的现象来验证其正确性，并反复的修正模型，直到这个模型能够解释所有的现象，才能从哲学领域进入科学领域。<br>&nbsp;&nbsp;&nbsp; 由于现代医学至今连一个独立系统最基本的能源供应系统的概念都没有，根本谈不上有什么系统模型和理论。但是由于众多电子设备所建立权威的先进形象，几乎没有人怀疑他们的理论缺陷，也接受疾病只能控制不能痊愈的现实结果。更因为如此，使得近百年来并没有人投入建立基本模型的工作，大多数的研究资源都花在解决各种疾病症状一类，以微观方向为主的枝微末节工作上。<br>&nbsp;&nbsp;&nbsp; 西方科技早年的发展动机，一直想发展出可以点石成金的点金术。因此，早年化学是西方最发达的科学。现代医学真正的发展是在一、两百年之前，也就是在西方化学最发达的年代，因此，整个医学都是用化学为基础发展出来的，我们可以说现代医学是用化学来治病的。<br>&nbsp;&nbsp;&nbsp; 古时候的中国人注重的是玄学，这是一种非常宏观的系统学，中国人所看到的是一个完整的系统，所以称人体是一个小宇宙，其实就是指人体是一个各项功能俱全的完整独立的系统。面对这样的系统，在当时科技手段欠缺的环境，无法用数据或类似现代科学的方法来描述，只能用阴、阳、虚、实等对比性的文字来说明他们的概念，后人看不懂这些文字的内涵，就说中医是玄学。中医是在这样的文化下发展出来的医学，可以说中国人是用系统学来治病。</p>
<p><strong>&#8220;中医现代化&#8221;还是&#8220;中医西医化&#8221; ?</strong><br>&nbsp;&nbsp;&nbsp; 中医和西医相反，从开始的理论就是用宏观的方式讨论人体的整体模型，从而发展出整套的医疗方法。但是其系统过于庞大，用西医微观的观点，很难立即提供直观的证据，因此一直被认为不科学。加上中国数百年来的弱国形象，使得中医的地位在世界上一直无法建立。<br>&nbsp;&nbsp;&nbsp; 近百年来，中国不断的在各个方面进行现代化，中医也不例外。由于现代化的主事者在开始时就先入为主的认为西医较中医为优。早期在二次大战期间的汪精卫伪政府，甚至还有废除中医的计划。因此中医现代化的工作，立足点采取完全否定中医，大胆引进西医的方法，用西医的分科，及西医的诊断方法来重新界定中医。<br>&nbsp;&nbsp;&nbsp; <font color=#ff0000>这种做法，与其说是&#8220;中医现代化&#8221;，不如称之为&#8220;中医西医化&#8221;来得贴切。这种牛头不对马嘴硬套的结果，使得中医完全失去了原有的优点。&#8220;头痛医头，脚痛医脚&#8221;是长期以来中医认定庸医的标准。但是中医现代化引进了西医的观点以后，西医和现代化以后的中医大多数用的是这个方法。<br></font>&nbsp;&nbsp;&nbsp; 真正的中医面对疾病的态度和西医完全不同，首先必需很清楚的了解敌人是疾病，而人体的作战指挥部是人体内的自我治疗系统，不是外在的医生。外在医生的任务，首先是了解敌情和战况、人体自我治疗系统的工作方向及作战时可能的外在反应。其次是了解人体自我治疗系统能力薄弱的环节，在适当的时候给予必要的支持，扮演好后勤补给的角色。<br>&nbsp;&nbsp;&nbsp; 同时适时的给予患者病情的解释，使患者了解自己身体的工作状况，配合人体自我治疗系统工作的需要，调整心理状况和生活作息，提供自我治疗系统最大的能量补给，做好安定患者情绪的工作。也就是外在的医生，实际上是人体内部医生的助手。<br>&nbsp;&nbsp;&nbsp; 传统中医的医疗手段，在古书中的记载是分为砭、针、灸、药四种方法。<br>&nbsp;&nbsp;&nbsp; &#8220;砭&#8221;指刮痧和按摩等物理治疗方法，是四种方法之首，是各种方法中最重要的方法。除了&#8220;砭&#8221;的治疗效果可能特别好以外，由于它完全不需要特别的材料和工具，只要一双手或简单的刮痧板就行了。只要懂得医理，随时随地都能为人治病，是最方便的医疗方法，可能因此被古人将之列为各种治病方法之首。药为四种方法之末，主要可能是这种方法需要各种不同的药材，不是随时随地都能具备的，因此最早被列为治疗方法中的下策。<br>&nbsp;&nbsp;&nbsp; 一个好的中医应该精通四种方法，视实际需要及资源状况，选择最好的方法为人治病。但是由于&#8220;砭&#8221;的治疗方法，医生最耗体力，也最耗时间，最不容易赚钱。而开方取药，不但能用最少时间、最少体力为人看病，同时在药材上也比较容易抬高价格，可以使医生获得最大的经济利益。<br>&nbsp;&nbsp;&nbsp; 多年演变下来，最终&#8220;以药为主&#8221;的治疗方法成为中医的主流，而各种&#8220;砭&#8221;的手段却沦为民俗疗法，中医的功效也大打折扣。和西医的发展一样，经济利益的诱导使得中医也走偏了方向。<br>&nbsp;&nbsp;&nbsp; 现代中医教学或医院把中医依不同的医疗手段予以分科，如针灸科和推拿科等，使得现代的中医师不再具备这四种最基本的技术。<br>&nbsp;&nbsp;&nbsp; 另外还将中医分成了内、外科，中医的理论中，外表的症状不过是五脏六腑疾病的表像而已，我无法理解如何能够将之分为内、外两个不同的科？这就是中医引进西医概念的最不好例证之一。<br>&nbsp;&nbsp;&nbsp; 在实际的经验里，多数疾病用砭、针、灸、药，这四种治疗方法都有效，但是各种方法对不同疾病的效用有很大区别。例如最常见的心包经阻塞，推拿、按摩最快收效，但是却很难找到药物可以有效治疗。<br>&nbsp;&nbsp;&nbsp; 许多因心包经阻塞引起的疾病，都因为&#8220;砭&#8221;的式微而成为今日的疑难杂症。例如多数的肌无力就是心包经阻塞造成疾病发生的主要原因，而成为今日中西医束手无策的疑难杂症。<br>&nbsp;&nbsp;&nbsp; 严格说来中医现代化，并没有真正开始。例如中医最讲究血气，可是至今没有任何仪器可以度量血气的多寡。其它虚症、实症更不用说了。另外中医的望诊也是非常重要的诊断手段，依古书的记载，人体的疾病都会在外表留下明显的痕迹，但是古时候医书都是刻在竹简上的，对望诊的记录只用很简单的几个字说明。<br>&nbsp;&nbsp;&nbsp; 例如小肠的毛病，古书里就记载&#8220;小肠疾颊丰&#8221;五个字，到底是脸颊的那一个部位？如何丰？后人就很难从书里了解。近代摄影乃至计算机影像处理、技术进步非常快，但是中医现代化的工作里，却很少听说有这方面的研究，多数的研究都集中在最容易赚钱的制药技术上。我们认为以基本中医理论为依据的中医现代化的医疗技术或医疗仪器的研究，将是新世纪医学发展能够有最大突破的可能方向之一，也将是中国人有机会攻下全球市场的另一个机会。<br>&nbsp;&nbsp;<font color=#ff0000>&nbsp; 在中国大陆，我接触过许多中西结合的医生，他们多数满脑子西医的概念，只是开药时中、西药都开。最常见的作法是&#8220;西医的诊断，中医的处方&#8221;。</font><br>&nbsp;&nbsp;&nbsp; 这是最大的弊病，首先对于脏器的定义，中西医就有很大的不同。其次中医的诊断还必需经过各种逻辑的推演，如四诊八纲的辩证，才能找出生病的器官。<br>&nbsp;&nbsp;&nbsp; <font color=#ff0000>用西医头痛医头的逻辑诊断，开中医的药，是完全行不通的。</font>例如胃溃疡和十二指肠溃疡，在西医认定为胃或十二指肠的疾病，但中医却认为是肝和情绪的疾病。如果依照西医的诊断，开出治疗胃或十二指肠的中药，其结果当然不会理想了。<br>&nbsp;&nbsp;&nbsp; 其实中医最重要的应该是诊断，透过望、闻、问、切的手段，对患者进行包括整个身体和从幼年到成年的生活习惯、心理状况以及近期的特殊事件等做全面性的了解，再依医生的经验进行整体的判断 (可惜的是今天能做这种诊断的中医师已经极为稀少了)。<br>&nbsp;&nbsp;&nbsp; 只要诊断的方向正确，治疗就变成很简单的事了，可以有许多不同的治疗手段，都能对疾病产生改善甚至痊愈的效果。</p>
<p><strong>总结</strong><br>&nbsp;&nbsp;&nbsp; 怀着戒慎恐惧的心情，花了许多年，不知改了多少个版本，把两个帮我校对的好朋友弄得人仰马翻，总算完成了这本书。在写作之前，写作之中，我一直在犹豫是不是适合写这么一本书，使用手册本应是设计制造者该写的，人类已经在这个星球上生存了近百万年，大概这是第一本使用手册，我是不是越俎代庖，太自大了？<br>&nbsp;&nbsp;&nbsp; 长期以来，也一直担心这本书是不是会给自己带来许多的麻烦。事实上这本书是一直在忐忑不安的心理压力下完成的。写完之后，松了一口气，无论这本书会带给自己多少麻烦，就让它来吧。<br>&nbsp;&nbsp;&nbsp; 在写这本书的这些年中，不断有新的进展，愈来愈多人了解我们的工作，也给我们很多的鼓励。到了接近完稿之前，我们又克服了几种严重的疾病，我们提供的只是一种养生保健的方法，教导人们如何正确的使用身体，没有使用任何药物和器材，这种方法称不上是医学，却创造了比医学更大的效用。<br>&nbsp;&nbsp;&nbsp; 现代的医学几乎对所有慢性病都束手无策，显然医学这门科学存在着很大的缺陷。这是一门会影响每一个人生和死的学问，有时候小小的一个错误都会让某些人失去生命，没有机会补救。因此，这个行业有很严格的法律，限制着各种行为。这些法律原来的用意是为了保护病患的权益，也保护了医生权益。法律规定医生只要用被认可的医疗方法为患者治病，如果因此导致患者发生生命上的任何损失，他们没有责任。由于事关人命，任何这方面的法律责任都是非常严重的。<br>&nbsp;&nbsp;&nbsp; 这样的法律也严格的限制了医生思考的权力，更不准有创新的权力，只有在教学医院工作的医生有资格开发新的治疗方法。即便是这些教学医院的医生，也必需冒着很大的风险，经过很长时间的动物实验，才能在病人身上进行实验。<br>&nbsp;&nbsp;&nbsp; 多数的医生都只能用学校所学，或医学界公认的方法来治病。虽然全世界有这么多的医生，但是只有极少数的医生有资格开创新的医疗方法。在这样的环境里，就算华陀再世也很难再成为一个神医。<br>&nbsp;&nbsp;&nbsp; 虽然在这本书中对整个现代医学有很直接的批评，但是当我们亲身经历在加护病房中与患者共同和死神博斗，体会了那种惊心动魄的历程之后，对于许多献身医疗事业，每天都必需面对这么恶劣环境的医护人员有无限的敬意，今天医学的问题并不是他们的错，而是各种大家无法掌握的因素长期造成的。<br>&nbsp;&nbsp;&nbsp; 我们所有的科学家伙伴们都没有医生资格，我们不能开业行医，只能建议一些具有医师资格的朋友参考我们的方法，或者提供朋友观念上的指导，更多的是一些医院已经放弃的病人，以死马当活马医的心态找我们帮忙。<br>&nbsp;&nbsp;&nbsp; 多数时候，我们是在自己的身上进行各种试验，我们每个人都很珍惜每一次生病的机会，因为这是最好的实验机会。所幸我们的方法是最自然而且安全的，因此，并没有冒任何风险。每次生病，都有机会仔细观察自己体内倒底发生了什么事，仔细思考身体在做什么。这本书有许多章节，都是我在生病时所写下来的。<br>&nbsp;&nbsp;&nbsp; 有一次，由于我们想观察心包积液流动的状态，需要一名自愿者，在心脏和心包膜之间注入显影剂，然后在MRI（磁共振设备）下观察，在心脏上注射是有一点点危险的，因此，我自愿担任&#8220;白老鼠&#8221;。后来因为其它原因，实验没有做成，这是唯一一次具有少许危险的实验。费伦教授则在多数的经络研究中充当白老鼠，多数实验中的照片都是他自己担任主角的。<br>&nbsp;&nbsp;&nbsp; 也因为&#8220;我们不是医生&#8221;的限制，让我们有机会发展出这一套完全不需要医生资格就能做的保健手段和全新的健康观念。却意外的发现，原来正确的观念比昂贵的药物和危险的手术更能帮助患者消除疾病。<br>&nbsp;&nbsp;&nbsp; 由于我们都没有受过正统医学的教育，在思考疾病的成因时，完全没有任何框架，每个人用他原来学过的科学知识来建构自己的疾病模型，再经过一群不同技术背景的朋友共同讨论，就发展出这一套理论和观念。我们不敢说这就是真理，但是，许多朋友读过我们整理的文章后，都认为很有道理，也很合逻辑。其中很多人身体力行的奉行我们的一式三招及健康观念，健康真的就这么得到了。<br>&nbsp;&nbsp;&nbsp; 我们希望这本书能够带给更多的朋友和家庭健康快乐的人生，也给医学界带来一点改变，让更多的人敢开始思考医学的基本问题，建构更接近真理的医学模型。有朝一日能够将现在困扰全世界的这些慢性病全数消除。<br>在掌握了健康的方法之后，真正享受到那种完全不用担心疾病的自信，这种感觉真好，但愿您也能和我们一样拥有这份自信。<br>&nbsp;&nbsp;&nbsp; 我们找了一群志同道合的医生朋友，在上海市经络科学研究中心的支持下，2002年七月，在上海最热闹的淮海中路开设了原安针灸推拿诊所，利用我们这套理论，提供需要的朋友及时协助，我们也在筹集资金建立一个大型的慢性病调养中心，这个调养中心将是一个休息和学习正确生活习惯的场所。<br>&nbsp;&nbsp;&nbsp; 我们理想中的医院应该更像一个休闲旅馆，没有药味，没有白大挂的医护人员，不再有白墙和白天花板构成冷冰冰的白色空间，有的是活泼的空间，和指导正确生活方式的健康顾问及义工；没有手术房，取而代之的是多媒体教室和休闲设施，每天有轻松的旅游节目和休闲活动。医生的处方除了经络的物理调理之外，还有&#8220;观念处方&#8221;，提供患者适当的生活建议，并帮助患者改正生活习惯。<br>&nbsp;&nbsp;&nbsp; 每一个人学会了正确使用自己身体的方法之后，未来的医疗费用应该极为低廉，希望有一天慢性病医疗服务不再是一种商业行为，而是社会最基本的公共设施，这个世界上的每一个人都有能力和权力享用，这是我们整个工作团队的最终理想。<br></p>
<img src="http://blog.vckbase.com/localvar/aggbug/26972.html" width=1 height=1> 
<img src ="http://www.cppblog.com/localvar/aggbug/132773.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/localvar/" target="_blank">局部变量</a> 2007-06-19 11:24 <a href="http://www.cppblog.com/localvar/archive/2007/06/19/132773.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>支持中医</title><link>http://www.cppblog.com/localvar/archive/2007/06/19/132774.html</link><dc:creator>局部变量</dc:creator><author>局部变量</author><pubDate>Tue, 19 Jun 2007 03:17:00 GMT</pubDate><guid>http://www.cppblog.com/localvar/archive/2007/06/19/132774.html</guid><wfw:comment>http://www.cppblog.com/localvar/comments/132774.html</wfw:comment><comments>http://www.cppblog.com/localvar/archive/2007/06/19/132774.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/localvar/comments/commentRss/132774.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/localvar/services/trackbacks/132774.html</trackback:ping><description><![CDATA[<p>本来以为大多数人都已经看清何祚庥这个伪院士的真面目了，但前两天，偶然注意到，在就&#8220;中医害死陈晓旭&#8221;的网络投票中，居然过半数的人支持何祚庥。昨天，在一个程序员的blog上也看到了一个分类叫&#8220;中医骗子&#8221;，里面收集了几十篇网络上诋毁中医的文章。我不禁感觉非常悲哀，老祖宗留下珍贵遗产，难道就这样完了？我想做点什么，可我能做什么呢？！我能做的可能就是在这里转贴一些文章吧，哪怕这些文章只能让一个人树立起对中医（以及其它传统文化）的正确认识。</p>
<img src="http://blog.vckbase.com/localvar/aggbug/26970.html" width=1 height=1> 
<img src ="http://www.cppblog.com/localvar/aggbug/132774.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/localvar/" target="_blank">局部变量</a> 2007-06-19 11:17 <a href="http://www.cppblog.com/localvar/archive/2007/06/19/132774.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C/C++中的序列点</title><link>http://www.cppblog.com/localvar/archive/2007/02/07/132780.html</link><dc:creator>局部变量</dc:creator><author>局部变量</author><pubDate>Wed, 07 Feb 2007 11:55:00 GMT</pubDate><guid>http://www.cppblog.com/localvar/archive/2007/02/07/132780.html</guid><wfw:comment>http://www.cppblog.com/localvar/comments/132780.html</wfw:comment><comments>http://www.cppblog.com/localvar/archive/2007/02/07/132780.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/localvar/comments/commentRss/132780.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/localvar/services/trackbacks/132780.html</trackback:ping><description><![CDATA[<a href="http://www.newsmth.net/bbstcon.php?board=CPlusPlus&amp;gid=177091">原文链接</a><br>
<h4>0. 什么是副作用（side effects）</h4>
<div>C99定义如下<br>Accessing a volatile object, modifying an object, modifying a file, or calling a function that does any of those operations are all side effects, which are changes in the state of the execution environment.</div>
<div>C++2003定义如下<br>Accessing an object designated by a volatile lvalue, modifying an object, calling a library I/O function, or calling a function that does any of those operations are all side effects, which are changes in the state of the execution environment.</div>
<div>可以看出C99和C++2003对副作用的定义基本类似，一个程序可以看作一个状态机，在任意一个时刻程序的状态包含了它的所有对象内容以及它的所有文件内容（标准输入输出也是文件），副作用会导致状态的跳转</div>
<div>一个变量一旦被声明为volatile-qualified类型，则表示该变量的值可能会被程序之外的事件改变，每次读取出来的值只在读取那一刻有效，之后如果再用到该变量的值必须重新读取，不能沿用上一次的值，因此读取volatile-qualified类型的变量也被认为是有副作用，而不仅仅是改写</div>
<div>注，一般不认为程序的状态包含了CPU寄存器的内容，除非该寄存器代表了一个变量，例如</div>
<table style="BORDER-COLLAPSE: collapse" border=1 cellSpacing=0 borderColor=#999999 cellPadding=0 width="75%" bgColor=#f1f1f1>
    <tbody>
        <tr>
            <td>
            <p style="LINE-HEIGHT: 150%; MARGIN: 5px"><code><span style="COLOR: #000000"><span style="COLOR: #0000ff">void</span> foo<span style="COLOR: #0000cc">(</span><span style="COLOR: #0000cc">)</span> <span style="COLOR: #0000cc">{</span><br>&nbsp;&nbsp;<span style="COLOR: #0000ff">register</span> <span style="COLOR: #0000ff">int</span> i <span style="COLOR: #0000cc">=</span> 0<span style="COLOR: #0000cc">;</span> <span style="COLOR: #ff9900">// 变量i被直接放入寄存器中，本文中被称为寄存器变量<br></span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #ff9900">// 注，register只是一个建议，不一定确实放入寄存器中<br></span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #ff9900">// 而且没有register关键字的auto变量也可能放入寄存器<br></span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #ff9900">// 这里只是用来示例，假设i确实放入了寄存器中<br></span>&nbsp;&nbsp;i <span style="COLOR: #0000cc">=</span> 1<span style="COLOR: #0000cc">;</span> <span style="COLOR: #ff9900">// 寄存器内容改变，对应了程序状态的改变，该语句有副作用<br></span>&nbsp;&nbsp;i <span style="COLOR: #0000cc">+</span> 1<span style="COLOR: #0000cc">;</span> <span style="COLOR: #ff9900">// 编译时该语句一般有警告：&#8220;warning: expression has no effect&#8221;<br></span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #ff9900">// CPU如果执行这个语句，也肯定会改变某个寄存器的值，但是程序状态<br></span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #ff9900">// 并未改变，除了代表i的寄存器，程序状态不包含其他寄存器的内容，<br></span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #ff9900">// 因此该语句没有任何副作用<br></span><span style="COLOR: #0000cc">}</span></span></code></p>
            </td>
        </tr>
    </tbody>
</table>
<p>特别的，C99和C++2003都指出，no effect的expression允许不被执行<br>An actual implementation need not evaluate part of an expression if it can deduce that its value is not used and that no needed side effects are produced (including any caused by calling a function or accessing a volatile object).</p>
<h4><br>1. 什么是序列点（sequence points）</h4>
<p>C99和C++2003对序列点的定义相同<br>At certain specified points in the execution sequence called sequence points, all side effects of previous evaluations shall be complete and no side effects of subsequent evaluations shall have taken place.</p>
<p>中文表述为，序列点是一些被特别规定的位置，要求在该位置前的evaluations所包含的一切副作用在此处均已完成，而在该位置之后的evaluations所包含的任何副作用都还没有开始</p>
<p>例如C/C++都规定完整表达式（full-expression）后有一个序列点</p>
<table style="BORDER-COLLAPSE: collapse" border=1 cellSpacing=0 borderColor=#999999 cellPadding=0 width="75%" bgColor=#f1f1f1>
    <tbody>
        <tr>
            <td>
            <p style="LINE-HEIGHT: 150%; MARGIN: 5px"><code><span style="COLOR: #000000"><span style="COLOR: #0000ff">extern</span> <span style="COLOR: #0000ff">int</span> i<span style="COLOR: #0000cc">,</span> j<span style="COLOR: #0000cc">;</span><br>i <span style="COLOR: #0000cc">=</span> 0<span style="COLOR: #0000cc">;</span><br>j <span style="COLOR: #0000cc">=</span> i<span style="COLOR: #0000cc">;</span></span></code></p>
            </td>
        </tr>
    </tbody>
</table>
<p>上面的代码中i = 0以及j = i都是一个完整表达式，;说明了表达式的结束，因此在;处有一个序列点，按照序列点的定义，要求在i = 0之后j = i之前的那个序列点上对i = 0的求值以及副作用全部结束（0被写入i中），而j = i的任何副作用都还没有开始。由于j = i的副作用是把i的值赋给j，而i = 0的副作用是把i赋值为0，如果i = 0的副作用发生在j = i之后，就会导致赋值后j的值是i的旧值，这显然是不对的</p>
<p>由序列点以及副作用的定义很容易看出，在一个序列点上，所有可能影响程序状态的动作均已完成，那这样能否推断出在一个序列点上一个程序的状态应该是确定的呢？！答案是不一定，这取决于我们代码的写法。但是，如果在一个序列点上程序的状态不能被确定，那么标准规定这样的程序是undefined behavior，稍后会解释这个问题</p>
<h4><br>2. 表达式求值（evaluation of expressions）与副作用发生的相互顺序</h4>
<p>C99和C++2003都规定<br>Except where noted, the order of evaluation of operands of individual operators and subexpressions of individual expressions, and the order in which side effects take place, is unspecified.</p>
<p>也就是说，C/C++都指出一般情况下在表达式求值过程中的操作数求值顺序以及副作用发生顺序是未说明的（unspecified）。为什么C/C++不详细定义这些顺序呢？原因是因为C/C++都是极端追求效率的语言，不规定这些顺序，是为了允许编译器有更大的优化余地，例如<br></p>
<table style="BORDER-COLLAPSE: collapse" border=1 cellSpacing=0 borderColor=#999999 cellPadding=0 width="75%" bgColor=#f1f1f1>
    <tbody>
        <tr>
            <td>
            <p style="LINE-HEIGHT: 150%; MARGIN: 5px"><code><span style="COLOR: #000000"><span style="COLOR: #0000ff">extern</span> <span style="COLOR: #0000ff">int</span> <span style="COLOR: #0000cc">*</span>p<span style="COLOR: #0000cc">;</span><br><span style="COLOR: #0000ff">extern</span> <span style="COLOR: #0000ff">int</span> i<span style="COLOR: #0000cc">;</span><br><span style="COLOR: #0000cc">*</span>p <span style="COLOR: #0000cc">=</span> i<span style="COLOR: #0000cc">+</span><span style="COLOR: #0000cc">+</span><span style="COLOR: #0000cc">;</span> </span></code></p>
            </td>
        </tr>
    </tbody>
</table>
根据前述规定，在表达式(1)中到底是*p先被求值还是i++先被求值是由编译器决定的；两次副作用（对*p赋值以及i++）发生的顺序是由编译器决定的；甚至连子表达式i++的求值（就是初始时i的值）以及副作用（将i增加1）都不需要同步发生，编译器可以先用初始时i的值（即子表达式i++的值）对*p赋值，然后再将i增加1，这样就把子表达式i++的整个计算过程分成了两个不相邻的步骤。而且通常编译器都是这么实现的，原因在于i++的求值过程同*p = i++是有区别的，对于单独的表达式i++，执行顺序一般是（假设不考虑inc指令）：先将i加载到某个寄存器A（如果i是寄存器变量则此步骤可以跳过）、将寄存器A的值加1、将寄存器A的新值写回i的地址；对于*p = i++，如果要先完整的计算子表达式i++，由于i++表达式的值是i的旧值，因此还需要一个额外的寄存器B以及一条额外的指令来辅助*p = i++的执行，但是如果我们先将加载到A的值写回到*p，然后再执行对i增加1的指令，则只需要一个寄存器即可，这种做法在很多平台都有重要意义，因为寄存器的数目往往是有限的，特别是假如有人写出如下的语句<br>
<table style="BORDER-COLLAPSE: collapse" border=1 cellSpacing=0 borderColor=#999999 cellPadding=0 width="75%" bgColor=#f1f1f1>
    <tbody>
        <tr>
            <td>
            <p style="LINE-HEIGHT: 150%; MARGIN: 5px"><code><span style="COLOR: #000000"><span style="COLOR: #0000ff">extern</span> <span style="COLOR: #0000ff">int</span> i<span style="COLOR: #0000cc">,</span> j<span style="COLOR: #0000cc">,</span> k<span style="COLOR: #0000cc">,</span> x<span style="COLOR: #0000cc">;</span><br>x <span style="COLOR: #0000cc">=</span> <span style="COLOR: #0000cc">(</span>i<span style="COLOR: #0000cc">+</span><span style="COLOR: #0000cc">+</span><span style="COLOR: #0000cc">)</span> <span style="COLOR: #0000cc">+</span> <span style="COLOR: #0000cc">(</span>j<span style="COLOR: #0000cc">+</span><span style="COLOR: #0000cc">+</span><span style="COLOR: #0000cc">)</span> <span style="COLOR: #0000cc">+</span> <span style="COLOR: #0000cc">(</span>k<span style="COLOR: #0000cc">+</span><span style="COLOR: #0000cc">+</span><span style="COLOR: #0000cc">)</span><span style="COLOR: #0000cc">;</span></span></code></p>
            </td>
        </tr>
    </tbody>
</table>
<p>编译器可以先计算(i++) + (j++) + (k++)的值，然后再对i、j、k各自加1，最后将i、j、k、x写回内存，这比每次完整的执行完++语义效率要高</p>
<h4><br>3. 序列点对副作用的限制</h4>
<p>C99和C++2003都有类似的如下规定<br>Between the previous and next sequence point a scalar object shall have its stored value modified at most once by the evaluation of an expression. Furthermore, the prior value shall be accessed only to determine the value to be stored. The requirements of this paragraph shall be met for each allowable ordering of the subexpressions of a full expression; otherwise the behavior is undefined.</p>
<p>也就是说，在相邻的两个序列点之间，一个对象只允许被修改一次，而且如果一个对象被修改则在这两个序列点之间对该变量的读取的唯一目的只能是为了确定该对象的新值（例如i++，需要先读取i的值以确定i的新值是旧值+1）。特别的，标准要求任意可能的执行顺序都必须满足该条件，否则代码将是undefined behavior</p>
<p>之所以序列点会对副作用有如此的限制，就是因为C/C++标准没有规定子表达式求值以及副作用发生之间的顺序，例如<br></p>
<table style="BORDER-COLLAPSE: collapse" border=1 cellSpacing=0 borderColor=#999999 cellPadding=0 width="75%" bgColor=#f1f1f1>
    <tbody>
        <tr>
            <td>
            <p style="LINE-HEIGHT: 150%; MARGIN: 5px"><code><span style="COLOR: #000000"><span style="COLOR: #0000ff">extern</span> <span style="COLOR: #0000ff">int</span> i<span style="COLOR: #0000cc">,</span> a<span style="COLOR: #0000cc">[</span><span style="COLOR: #0000cc">]</span><span style="COLOR: #0000cc">;</span><br><span style="COLOR: #0000ff">extern</span> <span style="COLOR: #0000ff">int</span> foo<span style="COLOR: #0000cc">(</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #0000cc">,</span> <span style="COLOR: #0000ff">int</span><span style="COLOR: #0000cc">)</span><span style="COLOR: #0000cc">;</span><br>i <span style="COLOR: #0000cc">=</span> <span style="COLOR: #0000cc">+</span><span style="COLOR: #0000cc">+</span>i <span style="COLOR: #0000cc">+</span> 1<span style="COLOR: #0000cc">;</span> <span style="COLOR: #ff9900">// 该表达式对i所做的两次修改都需要写回对象，i的最终值取决<br></span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #ff9900">// 于到底哪次写回最后发生，如果赋值动作最后写回，则i的值<br></span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #ff9900">// 是i的旧值加2，如果++i动作最后写回，则i的值是旧值加1，<br></span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #ff9900">// 因此该表达式的行为是undefined<br></span>a<span style="COLOR: #0000cc">[</span>i<span style="COLOR: #0000cc">+</span><span style="COLOR: #0000cc">+</span><span style="COLOR: #0000cc">]</span> <span style="COLOR: #0000cc">=</span> i<span style="COLOR: #0000cc">;</span> <span style="COLOR: #ff9900">// 如果=左边的表达式先求值并且i++的副作用被完成，则右边的<br></span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #ff9900">// 值是i的旧值加1，如果i++的副作用最后完成，则右边的值是i<br></span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #ff9900">// 的旧值，这也导致了不确定的结果，因此该表达式的行为将是<br></span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #ff9900">// undefined<br></span>foo<span style="COLOR: #0000cc">(</span>foo<span style="COLOR: #0000cc">(</span>0<span style="COLOR: #0000cc">,</span> i<span style="COLOR: #0000cc">+</span><span style="COLOR: #0000cc">+</span><span style="COLOR: #0000cc">)</span><span style="COLOR: #0000cc">,</span> i<span style="COLOR: #0000cc">+</span><span style="COLOR: #0000cc">+</span><span style="COLOR: #0000cc">)</span><span style="COLOR: #0000cc">;</span> <span style="COLOR: #ff9900">// 对于函数调用而言，标准没有规定函数参数的求值<br></span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #ff9900">// 顺序，但是标准规定所有参数求值完毕进入函数体<br></span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #ff9900">// 执行之前有一个序列点，因此这个表达式有两种执<br></span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #ff9900">// 行方式，一种是先求值外层foo调用的i++然后求值<br></span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #ff9900">// foo(0, i++)，然后进入到foo(0, i++)执行，这之<br></span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #ff9900">// 前有个序列点，这种执行方式还是在两个相邻序列<br></span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #ff9900">// 点之间修改了i两次，undefined<br></span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #ff9900">// 另一种执行方式是先求值foo(0, i++)，由于这里<br></span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #ff9900">// 有一个序列点，随后的第二个i++求值是在新序列<br></span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #ff9900">// 点之后，因此不算是两个相邻的序列点之间修改i<br></span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #ff9900">// 两次<br></span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #ff9900">// 但是，前面已经指出标准规定任意可能的执行路径<br></span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #ff9900">// 都必须满足条件才是定义好的行为，这种代码仍然<br></span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <font color=#ff9933>// 是undefined</font></span></code></p>
            </td>
        </tr>
    </tbody>
</table>
<p>前面我提到在一个序列点上程序的状态不一定是确定的，原因就在于相邻的两个序列点之间可能会发生多个副作用，这些副作用的发生顺序是未指定的，如果多于一个的副作用用于修改同一个对象，例如示例代码i = ++i + 1;，则程序的结果是依赖于副作用发生顺序的；另外，如果某个表达式既修改了某个对象又需要读取该对象的值，且读取对象的值并不用于确定对象新值，则读取和修改两个动作的先后顺序也会导致程序的状态不能唯一确定<br>所幸的是，&#8220;在相邻的两个序列点之间，一个对象只允许被修改一次，而且如果一个对象被修改则在这两个序列点之间只能为了确定该对象的新值而读一次&#8221;这一强制规定保证了符合要求的程序在任何一个序列点位置上其状态都可以确定下来</p>
<p>注，由于对于UDT类型存在operator重载，函数语义会提供新的序列点，因此某些对于built-in类型是undefined behavior的表达式对于UDT确可能是良好定义的，例如<br></p>
<table style="BORDER-COLLAPSE: collapse" border=1 cellSpacing=0 borderColor=#999999 cellPadding=0 width="75%" bgColor=#f1f1f1>
    <tbody>
        <tr>
            <td>
            <p style="LINE-HEIGHT: 150%; MARGIN: 5px"><code><span style="COLOR: #000000">i <span style="COLOR: #0000cc">=</span> i<span style="COLOR: #0000cc">+</span><span style="COLOR: #0000cc">+</span><span style="COLOR: #0000cc">;</span> <span style="COLOR: #ff9900">// 如果i是built-in类型对象，则该表达式在两个相邻的序列点之间对<br></span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #ff9900">// i修改了两次，undefined<br></span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #ff9900">// 如果i是UDT类型该表达式也许是i.operator=(i.operator++(int))，<br></span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #ff9900">// 函数参数求值完毕后会有一个序列点，因此该表达式并没有在两个<br></span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #ff9900">// 相邻的序列点之间修改i两次，OK<br></span></span></code></p>
            </td>
        </tr>
    </tbody>
</table>
<p>由此可见，常见的问题如printf("%d, %d", i++, i++)这种写法是错误的，这类问题作为笔试题或者面试题是没有任何意义的<br>类似的问题同样发生在cout &lt;&lt; i++ &lt;&lt; i++这种写法上，如果overload resolution选择成员函数operator&lt;&lt;，则等价于(cout.operator&lt;&lt;(i++)).operator&lt;&lt;(i++)，否则等价于operator&lt;&lt;(operator&lt;&lt;(cout, i++), i++)，如果i是built-in类型对象，这种写法跟foo(foo(0, i++), i++)的问题一致，都是未定义行为，因为存在某条执行路径使得i会在两个相邻的序列点之间被修改两次；如果i是UDT则该写法是良好定义的，跟i = i++一样，但是这种写法也是不推荐的，因为标准对于函数参数的求值顺序是unspecified，因此哪个i++先计算是不能预计的，这仍旧会带来移植性的问题，这种写法应该避免<br></p>
<h4>4. 编译器的跨序列点优化</h4>
<p>根据前述讨论可知，在同一个表达式内对于同一个变量i，允许的行为是<br>A. 不读取，改写一次，例如</p>
<table style="BORDER-COLLAPSE: collapse" border=1 cellSpacing=0 borderColor=#999999 cellPadding=0 width="75%" bgColor=#f1f1f1>
    <tbody>
        <tr>
            <td>
            <p style="LINE-HEIGHT: 150%; MARGIN: 5px"><code><span style="COLOR: #000000">i <span style="COLOR: #0000cc">=</span> 0<span style="COLOR: #0000cc">;</span></span></code></p>
            </td>
        </tr>
    </tbody>
</table>
B. 读取一次或者多次，改写一次，但所有读取仅仅用于决定改写后的新值，例如
<table style="BORDER-COLLAPSE: collapse" border=1 cellSpacing=0 borderColor=#999999 cellPadding=0 width="75%" bgColor=#f1f1f1>
    <tbody>
        <tr>
            <td>
            <p style="LINE-HEIGHT: 150%; MARGIN: 5px"><code><span style="COLOR: #000000">i <span style="COLOR: #0000cc">=</span> i <span style="COLOR: #0000cc">+</span> 1<span style="COLOR: #0000cc">;</span> <span style="COLOR: #ff9900">// 读取一次，改写一次<br></span>i <span style="COLOR: #0000cc">=</span> i <span style="COLOR: #0000cc">&amp;</span> <span style="COLOR: #0000cc">(</span>i <span style="COLOR: #0000cc">-</span> 1<span style="COLOR: #0000cc">)</span><span style="COLOR: #0000cc">;</span> </span></code></p>
            </td>
        </tr>
    </tbody>
</table>
C. 不改写，读取一次或者多次，例如
<table style="BORDER-COLLAPSE: collapse" border=1 cellSpacing=0 borderColor=#999999 cellPadding=0 width="75%" bgColor=#f1f1f1>
    <tbody>
        <tr>
            <td>
            <p style="LINE-HEIGHT: 150%; MARGIN: 5px"><code><span style="COLOR: #000000">j <span style="COLOR: #0000cc">=</span> i <span style="COLOR: #0000cc">&amp;</span> <span style="COLOR: #0000cc">(</span>i <span style="COLOR: #0000cc">-</span> 1<span style="COLOR: #0000cc">)</span><span style="COLOR: #0000cc">;</span></span></code></p>
            </td>
        </tr>
    </tbody>
</table>
<p>对于情况B和C，编译器是有一定的优化权利的，它可以只读取一次变量的值然后直接使用该值多次</p>
<p>但是，当该变量是volatile-qualified类型时编译器允许的行为究竟如何目前还没有找到明确的答案，ctrlz认为如果在两个相邻序列点之间读取同一个volatile-qualified类型对象多次仍旧是undefined behavior，原因在于该读取动作有副作用且该副作用等价于修改该对象，RoachCock的意见是两个相邻的序列点之间读取同一个volatile-qualified类型应该是合法的，但是不能被优化成只读一次。一段在嵌入式开发中很常见的代码示例如下<br></p>
<table style="BORDER-COLLAPSE: collapse" border=1 cellSpacing=0 borderColor=#999999 cellPadding=0 width="75%" bgColor=#f1f1f1>
    <tbody>
        <tr>
            <td>
            <p style="LINE-HEIGHT: 150%; MARGIN: 5px"><code><span style="COLOR: #000000"><span style="COLOR: #0000ff">extern</span> <span style="COLOR: #0000ff">volatile</span> <span style="COLOR: #0000ff">int</span> i<span style="COLOR: #0000cc">;</span><br><span style="COLOR: #0000ff">if</span> <span style="COLOR: #0000cc">(</span>i <span style="COLOR: #0000cc">!</span><span style="COLOR: #0000cc">=</span> i<span style="COLOR: #0000cc">)</span> <span style="COLOR: #0000cc">{</span> <span style="COLOR: #ff9900">// 探测很短的时间内i是否发生了变化<br></span>&nbsp;&nbsp;<span style="COLOR: #ff9900">// ...<br></span><span style="COLOR: #0000cc">}</span></span></code></p>
            </td>
        </tr>
    </tbody>
</table>
如果i != i被优化为只读一次，则结果恒为false，故RoachCock认为编译器不能够对volatile-qualified类型的变量做出只读一次的优化。ctrlz则认为这段代码本身是不正确的，应该改写成<br>
<table style="BORDER-COLLAPSE: collapse" border=1 cellSpacing=0 borderColor=#999999 cellPadding=0 width="75%" bgColor=#f1f1f1>
    <tbody>
        <tr>
            <td>
            <p style="LINE-HEIGHT: 150%; MARGIN: 5px"><code><span style="COLOR: #000000"><span style="COLOR: #0000ff">int</span> j <span style="COLOR: #0000cc">=</span> i<span style="COLOR: #0000cc">;</span><br><span style="COLOR: #0000ff">if</span> <span style="COLOR: #0000cc">(</span>j <span style="COLOR: #0000cc">!</span><span style="COLOR: #0000cc">=</span> i<span style="COLOR: #0000cc">)</span> <span style="COLOR: #0000cc">{</span> <span style="COLOR: #ff9900">// 将对volatile-qualified类型变量的多次读取用序列点隔开<br></span>&nbsp;&nbsp;<span style="COLOR: #ff9900">// ...<br></span><span style="COLOR: #0000cc">}</span></span></code></p>
            </td>
        </tr>
    </tbody>
</table>
虽然尚不能确定volatile-qualified类型的变量在相邻两个序列点之间读取多次行为是否合法以及将如何优化（不管怎么样，对于volatile-qualified类型这种代码应该尽量避免），但是可以肯定的是，对于volatile-qualified类型的变量在跨序列点之后必须要重新读取，volatile就是用来阻止编译器做出跨序列点的过激优化的，而对于non-volatile-qualified类型的跨序列点多次读取则可能被优化成只读一次（直到某个语句或者函数对该变量发生了修改，在此之前编译器可以假定non-volatile-qualified类型的变量是不会变化的，因为目前的C/C++抽象机器模型是单线程的），例如<br>
<table style="BORDER-COLLAPSE: collapse" border=1 cellSpacing=0 borderColor=#999999 cellPadding=0 width="75%" bgColor=#f1f1f1>
    <tbody>
        <tr>
            <td>
            <p style="LINE-HEIGHT: 150%; MARGIN: 5px"><code><span style="COLOR: #000000"><span style="COLOR: #0000ff">bool</span> flag <span style="COLOR: #0000cc">=</span> <span style="COLOR: #0000ff">true</span><span style="COLOR: #0000cc">;</span><br><span style="COLOR: #0000ff">void</span> foo<span style="COLOR: #0000cc">(</span><span style="COLOR: #0000cc">)</span> <span style="COLOR: #0000cc">{</span><br>&nbsp;&nbsp;<span style="COLOR: #0000ff">while</span> <span style="COLOR: #0000cc">(</span>flag<span style="COLOR: #0000cc">)</span> <span style="COLOR: #0000cc">{</span> <span style="COLOR: #ff9900">// (2)<br></span>&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #ff9900">// ...<br></span>&nbsp;&nbsp;<span style="COLOR: #0000cc">}</span><br><span style="COLOR: #0000cc">}</span></span></code></p>
            </td>
        </tr>
    </tbody>
</table>
<p>如果编译器探测到foo()没有任何语句（包括foo()调用过的函数）对flag有过修改，则也许会把(2)优化成只在进入foo()的时候读一次flag的值而不是每次循环都读一次，这种跨序列点的优化很有可能导致死循环。但是这种代码在多线程编程中很常见，虽然foo()没有修改过flag，也许在另一个线程的某个函数调用中会修改flag以终止循环，为了避免这种跨序列点优化带来到错误，应该把flag声明为volatile bool，C++2003对volatile的说明如下<br>[Note: volatile is a hint to the implementation to avoid aggressive optimization involving the object because the value of the object might be changed by means undetectable by an implementation. See 1.9 for detailed semantics. In general, the semantics of volatile are intended to be the same in C++ as they are in C. ]<br><br></p>
<h4>5. C99定义的序列点列表</h4>
<p>— The call to a function, after the arguments have been evaluated.<br>— The end of the first operand of the following operators: <br>&nbsp;&nbsp;&nbsp;&nbsp; logical AND &amp;&amp; ; <br>&nbsp;&nbsp;&nbsp;&nbsp; logical OR || ; <br>&nbsp;&nbsp;&nbsp;&nbsp; conditional ? ; <br>&nbsp;&nbsp;&nbsp;&nbsp; comma , .<br>— The end of a full declarator: <br>&nbsp;&nbsp;&nbsp;&nbsp; declarators;<br>— The end of a full expression: <br>&nbsp;&nbsp;&nbsp;&nbsp; an initializer; <br>&nbsp;&nbsp;&nbsp;&nbsp; the expression in an expression statement; <br>&nbsp;&nbsp;&nbsp;&nbsp; the controlling expression of a selection statement (if or switch); <br>&nbsp;&nbsp;&nbsp;&nbsp; the controlling expression of a while or do statement; <br>&nbsp;&nbsp;&nbsp;&nbsp; each of the expressions of a for statement; <br>&nbsp;&nbsp;&nbsp;&nbsp; the expression in a return statement.<br>— Immediately before a library function returns.<br>— After the actions associated with each formatted input/output function conversion specifier.<br>— Immediately before and immediately after each call to a comparison function, and also between any call to a comparison function and any movement of the objects passed as arguments to that call.</p>
<h4><br>6. C++2003定义的序列点列表</h4>
<p>所有C99定义的序列点同样是C++2003所定义的序列点<br>此外，C99只是规定库函数返回之后有一个序列点，并没有规定普通函数返回之后有一个序列点，而C++2003则特别指出，进入函数（function-entry）和退出函数（function-exit）各有一个序列点，即拷贝一个函数的返回值之后同样存在一个序列点</p>
<p>需要特别说明的是，由于operator||、operator&amp;&amp;以及operator,可以重载，当它们使用函数语义的时候并不提供built-in operators所规定的那几个序列点，而仅仅只是在函数的所有参数求值后有一个序列点，此外函数语义也不支持||、&amp;&amp;的短路语义，这些变化很有可能会导致难以发觉的错误，因此一般不建议重载这几个运算符</p>
<h4><br>7. C++2003中两处关于lvalue的修改对序列点的影响</h4>
<p>在C语言中，assignment operators的结果是non-lvalue，C++2003则将assignment operators的结果改成了lvalue，目前尚不清楚这一改动对于built-in类型有何意义，但是它却导致了很多在合法的C代码在目前的C++中是undefined behavior，例如<br></p>
<table style="BORDER-COLLAPSE: collapse" border=1 cellSpacing=0 borderColor=#999999 cellPadding=0 width="75%" bgColor=#f1f1f1>
    <tbody>
        <tr>
            <td>
            <p style="LINE-HEIGHT: 150%; MARGIN: 5px"><code><span style="COLOR: #000000"><span style="COLOR: #0000ff">extern</span> <span style="COLOR: #0000ff">int</span> i<span style="COLOR: #0000cc">;</span><br><span style="COLOR: #0000ff">extern</span> <span style="COLOR: #0000ff">int</span> j<span style="COLOR: #0000cc">;</span><br>i <span style="COLOR: #0000cc">=</span> j <span style="COLOR: #0000cc">=</span> 1<span style="COLOR: #0000cc">;</span></span></code></p>
            </td>
        </tr>
    </tbody>
</table>
由于(j = 1)的结果是lvalue，该结果作为给i赋值的右操作数，需要一个lvalue-to-rvalue conversion，这个conversion代表了一个读取语义，因此i = j = 1就是先将1赋值给j，然后读取j的值赋值给i，这个行为是undefined，因为标准规定两个相邻序列点之间的读取只能用于决定修改对象的新值，而不能发生在修改之后再读取<br>由于C++2003规定assignment operators的结果是lvalue，因此下列在C99中非法的代码在C++2003中却是可以通过编译的
<table style="BORDER-COLLAPSE: collapse" border=1 cellSpacing=0 borderColor=#999999 cellPadding=0 width="75%" bgColor=#f1f1f1>
    <tbody>
        <tr>
            <td>
            <p style="LINE-HEIGHT: 150%; MARGIN: 5px"><code><span style="COLOR: #000000"><span style="COLOR: #0000ff">extern</span> <span style="COLOR: #0000ff">int</span> i<span style="COLOR: #0000cc">;</span><br><span style="COLOR: #0000cc">(</span>i <span style="COLOR: #0000cc">+</span><span style="COLOR: #0000cc">=</span> 1<span style="COLOR: #0000cc">)</span> <span style="COLOR: #0000cc">+</span><span style="COLOR: #0000cc">=</span> 2<span style="COLOR: #0000cc">;</span></span></code></p>
            </td>
        </tr>
    </tbody>
</table>
<p>显然按照C++2003的规定这个代码的行为是undefined，它在两个相邻的序列点之间修改了i两次</p>
<p>类似的问题同样发生在built-in类型的前缀++/--operators上，C++2003将前缀++/--的结果从rvalue修改为lvalue，这甚至导致了下列代码也是undefined behavior<br></p>
<table style="BORDER-COLLAPSE: collapse" border=1 cellSpacing=0 borderColor=#999999 cellPadding=0 width="75%" bgColor=#f1f1f1>
    <tbody>
        <tr>
            <td>
            <p style="LINE-HEIGHT: 150%; MARGIN: 5px"><code><span style="COLOR: #000000"><span style="COLOR: #0000ff">extern</span> <span style="COLOR: #0000ff">int</span> i<span style="COLOR: #0000cc">;</span><br><span style="COLOR: #0000ff">extern</span> <span style="COLOR: #0000ff">int</span> j<span style="COLOR: #0000cc">;</span><br>i <span style="COLOR: #0000cc">=</span> <span style="COLOR: #0000cc">+</span><span style="COLOR: #0000cc">+</span>j<span style="COLOR: #0000cc">;</span></span></code></p>
            </td>
        </tr>
    </tbody>
</table>
<p>同样是因为lvalue作为assignment operator的右操作数需要一个左值转换，该转换导致了一个读取动作且这个读取动作发生在修改对象之后</p>
<p>C++的这一改动显然是考虑不周的，导致了很多C语言的习惯写法都成了undefined behavior，因此Andrew Koenig在1999年的时候就向C++标准委员会提交了一个建议要求为assignment operators增加新的序列点，但是到目前为止C++标准委员会都还没有就该问题达成一致意见</p>
<div></div>
<img src="http://blog.vckbase.com/localvar/aggbug/24495.html" width=1 height=1> 
<img src ="http://www.cppblog.com/localvar/aggbug/132780.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/localvar/" target="_blank">局部变量</a> 2007-02-07 19:55 <a href="http://www.cppblog.com/localvar/archive/2007/02/07/132780.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>