﻿<?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++博客-TheAnswer的C++博客-随笔分类-C</title><link>http://www.cppblog.com/theanswerzju/category/5864.html</link><description>空有无可奈何落去花 却无似曾相识归来燕</description><language>zh-cn</language><lastBuildDate>Mon, 19 May 2008 17:53:10 GMT</lastBuildDate><pubDate>Mon, 19 May 2008 17:53:10 GMT</pubDate><ttl>60</ttl><item><title>也谈数组和指针</title><link>http://www.cppblog.com/theanswerzju/archive/2008/01/11/40940.html</link><dc:creator>TheAnswer</dc:creator><author>TheAnswer</author><pubDate>Thu, 10 Jan 2008 20:53:00 GMT</pubDate><guid>http://www.cppblog.com/theanswerzju/archive/2008/01/11/40940.html</guid><wfw:comment>http://www.cppblog.com/theanswerzju/comments/40940.html</wfw:comment><comments>http://www.cppblog.com/theanswerzju/archive/2008/01/11/40940.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/theanswerzju/comments/commentRss/40940.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/theanswerzju/services/trackbacks/40940.html</trackback:ping><description><![CDATA[<p style="FONT-SIZE: 12pt; FONT-FAMILY: courier new">&nbsp;&nbsp; 在C语言中，数组和指针仿佛一对欢喜冤家，总是会被扯到一起，本文试图从几个角度来阐述数组与指针的异同，资料和观点的主要来源出自《C专家编程》相应章节。</p>
<p style="FONT-SIZE: 12pt; FONT-FAMILY: courier new">&nbsp;&nbsp; 首先我们来明确一些基础概念</p>
<p style="FONT-SIZE: 12pt; FONT-FAMILY: courier new">&nbsp;&nbsp; 声明与定义：<br>&nbsp;&nbsp; 声明&nbsp;可以出现多次，表述对象的类型。<br>&nbsp;&nbsp; 定义&nbsp;只能出现一次，确定对象的类型并分配内存。定义是一种特殊的声明，因为牠为对象分配了内存。</p>
<p style="FONT-SIZE: 12pt; FONT-FAMILY: courier new">&nbsp;&nbsp; 左值和右值：<br>&nbsp;&nbsp; 左值&nbsp;编译时可知，表示的是变量的地址。<br>&nbsp;&nbsp; 右值&nbsp;运行时可知，表示的是变量的地址中的内容。<br>&nbsp;&nbsp; 值得注意的是，C语言包括C++中左值右值都和常量没有必然联系，C语言中还引入了&#8220;可修改的左值&#8221;的概念，标准规定赋值操作符左侧必须为一个可修改的左值。按照上面的定义，我们可以看到，数组名是一个左值（表示地址，编译时可知），但是是一个不可修改的左值，因此不能把数组名放在赋值操作符的左侧。</p>
<p style="FONT-SIZE: 12pt; FONT-FAMILY: courier new">&nbsp;&nbsp; 现在让我们来看数组和指针在访问上的差异。</p>
<p style="FONT-SIZE: 12pt; FONT-FAMILY: courier new">&nbsp;&nbsp; 数组：数组名所表示的地址存在于符号表中，所需地址编译时便可知，直接加上偏移量即可。<br>&nbsp;&nbsp; extern char p[]; p[3];<br>&nbsp;&nbsp; 1.取得符号表中p的地址，并与下标所表示的偏移量相加，生成一个新的地址。<br>&nbsp;&nbsp; 2.访问上面的地址，取得字符，<br>&nbsp;&nbsp; 若此时p的定义为指针，则会取到指针所在地址的偏移量处的内容。</p>
<p style="FONT-SIZE: 12pt; FONT-FAMILY: courier new">&nbsp;&nbsp; 指针：需要先得到指针的值，然后将此值作为地址，再加上偏移量等操作。<br>&nbsp;&nbsp; extern char *p; p[3];<br>&nbsp;&nbsp; 1.取得符号表中p的地址，提取存储于此处的指针。<br>&nbsp;&nbsp; 2.把下标所表示的偏移量和指针的值相加，生成一个地址。<br>&nbsp;&nbsp; 3.访问上面的地址，取得字符，<br>&nbsp;&nbsp; 若此时p的定义为数组，则会把一个char值解释为地址而出错。</p>
<p style="FONT-SIZE: 12pt; FONT-FAMILY: courier new">&nbsp;&nbsp; 也就是说，编译器是根据声明时的类型来确定访问的方式，声明和定义的不一致就像混用malloc()和delete一样可怕，定义和声明应当匹配。</p>
<p style="FONT-SIZE: 12pt; FONT-FAMILY: courier new">&nbsp;&nbsp; 另一方面，作为极容易混淆的两个概念，数组和指针必然有相同或者说相通之处，主要有以下三点：<br>&nbsp;&nbsp; 1.表达式中的数组名被编译器当作一个指向该数组第一个元素的指针。<br>&nbsp;&nbsp; 2.下标总和指针的偏移量相同。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 对数组的引用例如a[i]总是会被编译器改写为*(a+i)，这是标准规定的编译器必须具有的概念性行为。<br>&nbsp;&nbsp; 3.在函数参数的声明中，数组名被编译器当作指向该数组第一个元素的指针。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;关于这条，有段挺有意思的小程序可以验证。</p>
<p style="FONT-SIZE: 12pt; FONT-FAMILY: courier new">&nbsp;&nbsp;&nbsp;#include &lt;stdio.h&gt;</p>
<p style="FONT-SIZE: 12pt; FONT-FAMILY: courier new">&nbsp;&nbsp;&nbsp;char ga[] = "abcdefghijklm";</p>
<p style="FONT-SIZE: 12pt; FONT-FAMILY: courier new">&nbsp;&nbsp;&nbsp;void myArrayFunc(char ca[])<br>&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf("Address of ca = %#x\n", &amp;ca);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf("Address of (ca[0]) = %#x\n\n", &amp;(ca[0]));<br>&nbsp;&nbsp;&nbsp;}</p>
<p style="FONT-SIZE: 12pt; FONT-FAMILY: courier new">&nbsp;&nbsp;&nbsp;void myPointerFunc(char *pa)<br>&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf("Address of pa = %#x\n", &amp;pa);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf("Address of (pa[0]) = %#x\n\n", &amp;(pa[0]));<br>&nbsp;&nbsp;&nbsp;}</p>
<p style="FONT-SIZE: 12pt; FONT-FAMILY: courier new">&nbsp;&nbsp;&nbsp;int main(int argc, char *argv[])<br>&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf("Address of ga = %#x\n", &amp;ga);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf("Address of (ga[0]) = %#x\n\n", &amp;(ga[0]));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;myArrayFunc(ga);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;myPointerFunc(ga);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return 0;<br>&nbsp;&nbsp;&nbsp;}</p>
<p style="FONT-SIZE: 12pt; FONT-FAMILY: courier new">&nbsp;&nbsp;&nbsp;我在VC9下的运行结果为：<br>&nbsp;&nbsp;&nbsp;Address of ga = 0x417000<br>&nbsp;&nbsp;&nbsp;Address of (ga[0]) = 0x417000</p>
<p style="FONT-SIZE: 12pt; FONT-FAMILY: courier new">&nbsp;&nbsp;&nbsp;Address of ca = 0x12fe98<br>&nbsp;&nbsp;&nbsp;Address of (ca[0]) = 0x417000</p>
<p style="FONT-SIZE: 12pt; FONT-FAMILY: courier new">&nbsp;&nbsp;&nbsp;Address of pa = 0x12fe98<br>&nbsp;&nbsp;&nbsp;Address of (pa[0]) = 0x417000</p>
<p style="FONT-SIZE: 12pt; FONT-FAMILY: courier new">&nbsp;&nbsp;&nbsp;ca和ca[0]的地址居然不同，是不是很有出乎意料又在情理之中的味道。<br><br>&nbsp;&nbsp;&nbsp;OK,两者在概念层面上的异同基本就是上面这些了，最后谈谈效率。</p>
<p style="FONT-SIZE: 12pt; FONT-FAMILY: courier new">&nbsp;&nbsp; 访问效率，仅以一维数组为例做个对比。<br><br>&nbsp;&nbsp;&nbsp;//array.c<br>&nbsp;&nbsp;&nbsp;void array()<br>&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int a[10];<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int i;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for(i = 0; i &lt; 10; ++i)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; a[i] = 0;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;}</p>
<p style="FONT-SIZE: 12pt; FONT-FAMILY: courier new"><br>&nbsp;&nbsp;&nbsp;//pointer.c<br>&nbsp;&nbsp;&nbsp;void pointer()<br>&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int a[10];<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int *p = a;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int i;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for (i = 0; i &lt; 10; ++i)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*p++ = 0;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;从理论上来说，array.c中的a[i] = 0;会被改写为*(a+i) = 0，注意包含对步长的调整，也就是会出现乘法或者移位运算，而pointer.c中仅仅需要将指针的值加1即可，用指针访问的效率更高。</p>
<p style="FONT-SIZE: 12pt; FONT-FAMILY: courier new">实际情况如何呢？让我们来看看汇编吧。编译器版本：gcc version 3.4.4 (cygming special) (gdc 0.12, using dmd 0.125)<br><br>//array.s<br>&nbsp;&nbsp;&nbsp; .file&nbsp;&nbsp; "array.c"<br>&nbsp;&nbsp;&nbsp; .text<br>.globl _array<br>&nbsp;&nbsp;&nbsp; .def&nbsp;&nbsp;&nbsp; _array; .scl&nbsp;&nbsp;&nbsp; 2;&nbsp; .type&nbsp;&nbsp; 32; .endef<br>_array:<br>&nbsp;&nbsp;&nbsp; pushl&nbsp;&nbsp; %ebp<br>&nbsp;&nbsp;&nbsp; movl&nbsp;&nbsp;&nbsp; %esp, %ebp<br>&nbsp;&nbsp;&nbsp; subl&nbsp;&nbsp;&nbsp; $72, %esp<br>&nbsp;&nbsp;&nbsp; movl&nbsp;&nbsp;&nbsp; $0, -60(%ebp)<br>L2:<br>&nbsp;&nbsp;&nbsp; cmpl&nbsp;&nbsp;&nbsp; $9, -60(%ebp)<br>&nbsp;&nbsp;&nbsp; jg&nbsp; L1<br>&nbsp;&nbsp;&nbsp; movl&nbsp;&nbsp;&nbsp; -60(%ebp), %eax<br>&nbsp;&nbsp;&nbsp; movl&nbsp;&nbsp;&nbsp; $0, -56(%ebp,%eax,4)<br>&nbsp;&nbsp;&nbsp; leal&nbsp;&nbsp;&nbsp; -60(%ebp), %eax<br>&nbsp;&nbsp;&nbsp; incl&nbsp;&nbsp;&nbsp; (%eax)<br>&nbsp;&nbsp;&nbsp; jmp L2<br>L1:<br>&nbsp;&nbsp;&nbsp; leave<br>&nbsp;&nbsp;&nbsp; ret</p>
<p style="FONT-SIZE: 12pt; FONT-FAMILY: courier new">//pointer.s<br>&nbsp;&nbsp;&nbsp; .file&nbsp;&nbsp; "pointer.c"<br>&nbsp;&nbsp;&nbsp; .text<br>.globl _pointer<br>&nbsp;&nbsp;&nbsp; .def&nbsp;&nbsp;&nbsp; _pointer;&nbsp;&nbsp; .scl&nbsp;&nbsp;&nbsp; 2;&nbsp; .type&nbsp;&nbsp; 32; .endef<br>_pointer:<br>&nbsp;&nbsp;&nbsp; pushl&nbsp;&nbsp; %ebp<br>&nbsp;&nbsp;&nbsp; movl&nbsp;&nbsp;&nbsp; %esp, %ebp<br>&nbsp;&nbsp;&nbsp; subl&nbsp;&nbsp;&nbsp; $72, %esp<br>&nbsp;&nbsp;&nbsp; leal&nbsp;&nbsp;&nbsp; -56(%ebp), %eax<br>&nbsp;&nbsp;&nbsp; movl&nbsp;&nbsp;&nbsp; %eax, -60(%ebp)<br>&nbsp;&nbsp;&nbsp; movl&nbsp;&nbsp;&nbsp; $0, -64(%ebp)<br>L2:<br>&nbsp;&nbsp;&nbsp; cmpl&nbsp;&nbsp;&nbsp; $9, -64(%ebp)<br>&nbsp;&nbsp;&nbsp; jg&nbsp; L1<br>&nbsp;&nbsp;&nbsp; movl&nbsp;&nbsp;&nbsp; -60(%ebp), %eax<br>&nbsp;&nbsp;&nbsp; movl&nbsp;&nbsp;&nbsp; $0, (%eax)<br>&nbsp;&nbsp;&nbsp; leal&nbsp;&nbsp;&nbsp; -60(%ebp), %eax<br>&nbsp;&nbsp;&nbsp; addl&nbsp;&nbsp;&nbsp; $4, (%eax)<br>&nbsp;&nbsp;&nbsp; leal&nbsp;&nbsp;&nbsp; -64(%ebp), %eax<br>&nbsp;&nbsp;&nbsp; incl&nbsp;&nbsp;&nbsp; (%eax)<br>&nbsp;&nbsp;&nbsp; jmp L2<br>L1:<br>&nbsp;&nbsp;&nbsp; leave<br>&nbsp;&nbsp;&nbsp; ret</p>
<p style="FONT-SIZE: 12pt; FONT-FAMILY: courier new">&nbsp;&nbsp;&nbsp;居然是指针版本在关键的L2循环中执行的指令数量更多，是不是很出人意料，所以，千万不要低估现代编译器的智慧，选择可读性和效率都占优的数组下标访问吧。<img src="http://www.cppblog.com/CuteSoft_Client/CuteEditor/images/emsmile.gif" align=absMiddle border=0><br><br></p>
<img src ="http://www.cppblog.com/theanswerzju/aggbug/40940.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/theanswerzju/" target="_blank">TheAnswer</a> 2008-01-11 04:53 <a href="http://www.cppblog.com/theanswerzju/archive/2008/01/11/40940.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C与C++对单字符的不同默认处理</title><link>http://www.cppblog.com/theanswerzju/archive/2008/01/10/40891.html</link><dc:creator>TheAnswer</dc:creator><author>TheAnswer</author><pubDate>Thu, 10 Jan 2008 05:57:00 GMT</pubDate><guid>http://www.cppblog.com/theanswerzju/archive/2008/01/10/40891.html</guid><wfw:comment>http://www.cppblog.com/theanswerzju/comments/40891.html</wfw:comment><comments>http://www.cppblog.com/theanswerzju/archive/2008/01/10/40891.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/theanswerzju/comments/commentRss/40891.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/theanswerzju/services/trackbacks/40891.html</trackback:ping><description><![CDATA[<p style="FONT-SIZE: 12pt; FONT-FAMILY: courier new">&nbsp;&nbsp;&nbsp; 昨天看《C专家编程》的时候发现了这个细微的差别，感觉还挺有意思的，以前也没意识到这个问题。&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;<br>&nbsp;&nbsp;&nbsp; 下面这段简单的代码<br><br>&nbsp;&nbsp;&nbsp; #include &lt;stdio.h&gt;</p>
<p style="FONT-SIZE: 12pt; FONT-FAMILY: courier new">&nbsp;&nbsp;&nbsp; int main(int argc, char *argv[])<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf("%d\n", sizeof('A'));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return 0;<br>&nbsp;&nbsp;&nbsp; }<br><br>&nbsp;&nbsp; 用gcc编译后运行结果为4，用g++编译后运行结果为1。<br><br>&nbsp;&nbsp; 说明C语言中默认把单字符视为int，也就是sizeof ('A')默认和sizeof(65)等同，加上强制类型转换sizeof((char)'A')输出的就是期望结果1。<br><br>&nbsp;&nbsp;&nbsp; C++中看来默认把单字符就作为一个char处理，比较符合直觉感受。<img src="http://www.cppblog.com/CuteSoft_Client/CuteEditor/images/emsmile.gif" align=absMiddle border=0><br><br></p>
<img src ="http://www.cppblog.com/theanswerzju/aggbug/40891.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/theanswerzju/" target="_blank">TheAnswer</a> 2008-01-10 13:57 <a href="http://www.cppblog.com/theanswerzju/archive/2008/01/10/40891.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>《C陷阱与缺陷》读书笔记</title><link>http://www.cppblog.com/theanswerzju/archive/2007/12/24/39463.html</link><dc:creator>TheAnswer</dc:creator><author>TheAnswer</author><pubDate>Sun, 23 Dec 2007 18:55:00 GMT</pubDate><guid>http://www.cppblog.com/theanswerzju/archive/2007/12/24/39463.html</guid><wfw:comment>http://www.cppblog.com/theanswerzju/comments/39463.html</wfw:comment><comments>http://www.cppblog.com/theanswerzju/archive/2007/12/24/39463.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/theanswerzju/comments/commentRss/39463.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/theanswerzju/services/trackbacks/39463.html</trackback:ping><description><![CDATA[<span style="FONT-SIZE: 12pt; FONT-FAMILY: courier new">&nbsp;&nbsp;&nbsp; 考虑到考研有C语言，于是花了一个晚上又把《C陷阱与缺陷》翻了一遍，算是把C语言复习过了。细细算起来，这应该是我第三遍读这本书了，不过经典著作的伟大之处就是每次读都有新的体会，像一个无穷的宝藏，这次得到的珍宝往往是上次挖掘时被无视的小石头。<br>&nbsp;<br>&nbsp;&nbsp;&nbsp; 随手做了一点笔记，把比较有感触的部分给摘录了一下。虽然难免挂一漏万，不过总比一丝不挂要好一些。<br>&nbsp;<br>&nbsp;&nbsp;&nbsp; 第0章 导读<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 讨论内容：程序并没有按照程序员所期待的方式执行<br>&nbsp;<br>&nbsp;&nbsp;&nbsp; 第1章 词法"陷阱"<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1.1 =不同于==<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1.2 &amp; 和 | 不同于&amp;&amp; 和 ||<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1.3 词法分析中的"贪心法"<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; y = x/*p，/*被编译器理解为一段注释的开始，可以作为操作符左右应加空格的例子。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1.4 整型常量<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0开头的整型常量会被视为八进制数，即010代表的是的8(十进制)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1.5 字符与字符串<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 用单引号引起的一个字符实际上代表一个整数，整数值对应于该字符在编译器采用的字符集中的序列值。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 用双引号引起的字符串，代表的是一个指向无名数组起始字符的指针。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 'yes'在VC和GCC下均为's'。<br>&nbsp;<br>&nbsp;&nbsp;&nbsp; 第2章 语法"陷阱"<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2.1 理解函数声明<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 声明由类型和一组类似表达式的声明符组成，对声明符求值应该返回一个声明中给定类型的结果。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 把声明中的变量名和声明末尾的分号去掉，再将剩余的部分用一个括号封装起来就可以得到该类型的类型转换符。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2.2 运算符的优先级问题<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 任何一个逻辑运算符的优先级低于任何一个关系运算符。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 移位运算符的优先级比算术运算符要低，但是比关系运算符要高。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2.3 注意作为语句结束标志的分号<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2.4 switch语句<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2.5 函数调用<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2.6 "悬挂"else引发的问题<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else始终与同一对括号内最近的未匹配的if结合。<br>&nbsp;<br>&nbsp;&nbsp;&nbsp; 第3章 语义"陷阱"<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3.1 指针与数组<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; C语言中只有一维数组，且大小必须为一个编译期常量，但数组中的元素可以是任何类型的对象(&#8220;仿真&#8221;出多维数组)。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 对于数组，只可以确定该数组的大小以及获得指向该数组下标为的元素的指针，其他操作实际都是通过指针来完成。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3.2 非数组的指针&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3.3 作为参数的数组声明<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3.4 避免"举隅法"　43<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 复制指针并不同时复制指针所指向的数据。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ANSI C禁止对string literal做出修改。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3.5 空指针并非空字符串<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 编译器保证由0转换而来的指针不等于任何有效的指针。但当常数0被转换为指针使用时，这个指针绝对不能被解除引用。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(p == (char*)0) ... /* OK */&nbsp; if(strcmp(p, (char*)0) == 0) ... /* Illegal */<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3.6 边界计算与不对称边界<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 用第一个入界点和第一个出界点来表示一个数值范围。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 考虑缓冲区问题时一般将指针指向第一个未占用的位置。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; n次迭代可以使用while(--n &gt;= 0)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3.7 求值顺序<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; C语言中只有&amp;&amp;、||、?:、,四个运算符存在规定的求值顺序，其他所有运算符对其操作数求值的顺序是未定义的。特别的，赋值运算符并不保证任何求值顺序。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3.8 运算符&amp;&amp;、|| 和 !<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3.9 整数溢出<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 有符号整数运算时需要考虑溢出问题。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3.10 为函数main提供返回值<br>&nbsp;<br>&nbsp;&nbsp;&nbsp; 第4章 连接<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 4.1 什么是连接器<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 4.2 声明与定义<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 每个外部变量只定义一次。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 4.3 命名冲突与static修饰符<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 如果一个函数或者变量仅仅被同一个源文件中的其他函数调用，就应该声明为static。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 4.4 形参、实参与返回值<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 如果一个函数在被定义或者声明之前被调用，那么它的返回值就默认为整型。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; main()<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; double s;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; s = sqrt(2);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf("%g\n", s);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在gcc中加上-fno-builtin后，打印出来就是垃圾信息了。(感谢adoal的指点)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 4.5 检查外部类型<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 应该保证一个特定名称的所有外部定义在每个目标模块中都有相同的类型，尤其是数组和指针。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 4.6 头文件<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 每个外部对象都在头文件中声明，需要用到该外部对象的所有模块都应该包括这个头文件，定义该外部对象的模块也应该包括这个头文件。<br>&nbsp;<br>&nbsp;&nbsp;&nbsp; 第5章 库函数<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 5.1 返回整数的getchar函数<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 5.2 更新顺序文件<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 要对同个文件同时进行输入和输出操作，必须在其中插入fseek函数的调用，一个输入操作不能随后直接紧跟一个输出操作，反之亦然。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 5.3 缓冲输出与内存分配<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 预防缓冲区最后一次被清空的时候缓冲区已经被释放<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 5.4 使用errno检测错误<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在调用库函数时，应该首先检测作为错误指示的返回值，确定程序执行已经失败。然后，再检查errno。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 5.5 库函数signal<br>&nbsp;<br>&nbsp;&nbsp;&nbsp; 第6章 预处理器<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 6.1 不能忽视宏定义中的空格&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 6.2 宏并不是函数<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 把宏定义中的每个参数都用括号括起来(预防引起优先级有关的问题)，整个结果表达式也用括号括起来(预防宏用于一个更大的表达式中的问题)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 确保宏中的参数没有副作用。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 6.3 宏并不是语句<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 6.4 宏并不是类型定义<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 尽量使用typedef替代。<br>&nbsp;<br>&nbsp;&nbsp;&nbsp; 第7章 可移植性缺陷<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 7.1 应对C语言标准变更<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 7.2 标识符名称的限制<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 7.3 整数的大小<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 7.4 字符是有符号整数还是无符号整数<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 7.5 移位运算符<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 右移时，如果被移位的对象是无符号数，那么空出的位将被0填充；如果是有符号数，那么C语言既可以用0填充空出的位，也可以用符号位的副本填充空出的位。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 7.6 内存位置0<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 7.7 除法运算时发生的截断<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; q = a / b; r = a % b; C语言的定义只能保证q * b + r = a，以及当a &gt;= 0, b &gt; 0时，保证|r| &lt; |b|以及r &gt;= 0。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 7.8 随机数的大小<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 常数RAND_MAX表示随机数的最大取值。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 7.9 大小写转换<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 7.10 首先释放，然后重新分配<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 7.11 可移植性问题的一个例子<br>&nbsp;<br>&nbsp;&nbsp;&nbsp; 第8章 建议与答案<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 8.1 建议<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 不要说服自己相信&#8220;皇帝的新装&#8221;。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 直截了当地表达意图。(使用括号或者其他方式)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 考察最简单的特例。(输入数据为空或者只有一个元素时)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 使用不对称边界。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 注意潜伏在暗处的Bug。(坚持只使用C中众所周知的部分)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 防御性编程。(对程序用户和编译器实现的假设不要过多)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 8.2 答案<br>&nbsp;&nbsp;&nbsp; 附录A PRINTF，VARARGS与STDARG<br>&nbsp;&nbsp;&nbsp; 附录B Koenig和Moo夫妇访谈<img src="http://www.cppblog.com/CuteSoft_Client/CuteEditor/images/emsmile.gif" align=absMiddle border=0><br><br><br></span>
<img src ="http://www.cppblog.com/theanswerzju/aggbug/39463.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/theanswerzju/" target="_blank">TheAnswer</a> 2007-12-24 02:55 <a href="http://www.cppblog.com/theanswerzju/archive/2007/12/24/39463.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>